Skip to content

Production Release Checklist

P8 machine release gate

Before tag or deployment review, run uv run python scripts/release_gate.py from a locked checkout. The command must finish with exit code zero and release/evidence/p8-release-evidence.v1.json must report release_status as passed, identical required/executed gate counts, no missing or failed gates, and SHA-256 entries for every release artifact. A missing test, skipped command, stale generated artifact, or unavailable scanner is a failure; manual narrative cannot replace this evidence package.

The versioned inputs reviewed with that package are under release/: schema catalog, Agent Tool Registry, OpenAPI, Skill metadata, provider coverage, migration manifest, and SPDX SBOM. CLI, MCP, REST, Skill, and generated documentation remain Registry consumers rather than independently maintained inventories.

Casdoor OAuth gate

  • [ ] The deployed website serves the current English and Simplified Chinese Terms of Service, Privacy Notice, and Account Deletion Guide; each page shows its effective date and version, and the global footer links all three.
  • [ ] Casdoor admin/argus-cli links the current Terms, requires Agreement on username/password signup only, hides it on ordinary sign-in, leaves custom signup HTML empty, and shows the versioned Terms/Privacy disclosure for GitHub signup plus all three legal footer links.
  • [ ] A request to every legal page with Origin: https://auth.argusfa.com returns that exact origin in Access-Control-Allow-Origin, while unrelated pages and unapproved origins do not receive a CORS grant; opening the Casdoor Agreement displays the Terms.
  • [ ] The GitHub provider requests only identity scopes (read:user and user:email), and a fresh test registration visibly encounters the legal disclosure before the provider redirect. If affirmative, versioned consent evidence is contractually required, an application-owned consent gate and receipt store are in place; Casdoor's provider button alone is insufficient.
  • [ ] [email protected] can receive privacy and account-deletion requests, and the operator has a documented process to verify, fulfil, retain lawful exceptions, and record completion of those requests.

  • [ ] API and MCP use distinct configured audiences/resources.

  • [ ] Discovery issuer is exactly https://auth.argusfa.com; authorization, token, and JWKS endpoints use HTTPS.
  • [ ] Every access token has 0 < exp-iat <= 3600; Casdoor applications use the minimum one-hour lifetime.
  • [ ] DCR is disabled and ChatGPT, Claude, and CLI are pre-registered because Casdoor DCR applications default to 7 days.
  • [ ] Casdoor application admin/argus-cli exposes the compiled public Client ID, registers exactly http://127.0.0.1:8765/callback, and uses PKCE plus refresh rotation; the root-owned Casdoor management credential lives in a separate preflight-only directory and lets production preflight verify the unmasked callback, grant types, and one-hour access-token lifetime before validating that the live authorization endpoint accepts the release ID, scopes, response type, and callback.
  • [ ] ARGUS_CLIENT_DISTRIBUTION__* values are empty; the CLI wheel is bundled in the signed API image and its compiled client ID matches server configuration.
  • [ ] MCP clients are pre-registered and every approved redirect URI is configured in Casdoor; DCR remains disabled.
  • [ ] /mcp-machine is live for machine clients and public /mcp is OAuth-only.
  • [ ] Production uses ARGUS_MCP_ROLLOUT_PHASE=oauth-cutover.
  • [ ] Email, SMS, GitHub, Google, Codex, Claude, and ChatGPT staging smoke passes.
  • [ ] Casdoor disablement is confirmed to reject refresh immediately; an already issued access token expires within one hour.
  • [ ] OAuth shared institution cannot cross into machine institutions; caller-owned resources remain isolated.

Use this checklist for every staging or production release. A historical 15.2 completed note means contract and local acceptance passed; production release is allowed only after the live data, internal distribution, Compose startup, and operations gates below pass for the target institution.

Public edition

  • [ ] Bump the public version in pyproject.toml and src/argus/__init__.py to the same value. Use that current project version rather than a historical checklist value; the website masthead, OpenAPI info.version, Skill package version, and /health/live all read this number.
  • [ ] Set website/package.json (and the root website/package-lock.json package version) to the same value, and update the fallback version in website/src/pages/index.astro and website/src/pages/404.astro.
  • [ ] Regenerate website/generated/authority-data.json via scripts/generate_website_data.py so the header cannot keep showing a stale edition.
  • [ ] Regenerate tracked release artifacts with uv run python scripts/generate_release_artifacts.py so OpenAPI and the SBOM match.
  • [ ] From website/, run npm run build:brand, npm run build:screenshots, and npm run verify:screenshots; confirm the screenshot manifest reports this release version, the current Registry and tool count, and only CLI + Skill, MCP, and REST.
  • [ ] Confirm all 12 locales have section-specific Open Graph cards, localized campaign lockups use reviewed website copy, the 9:16 lockup remains inside the platform-safe area, and assets/brand/source/asset-manifest.json matches both source-plate hashes and the approved distribution record.

Required Gates

  1. Confirm the release commit and ARGUS_IMAGE registry digest are immutable; do not deploy a tag. The image must be ghcr.io/ksahdsambn/argus@sha256:..., and Cosign must be installed so the preflight can verify the release.yml GitHub Actions identity before the image receives production secrets. Confirm the image signature identity is a semantic v* tag, points exactly to the current protected main commit, and that the release workflow's reusable quality-gate job passed for that exact tagged commit. Protect the production-release GitHub Environment with required reviewers before enabling releases.
  2. Run migrations against staging or a restored production backup first, and record the successful restore rehearsal identifier and elapsed time.
  3. Confirm ARGUS_DATA_LICENSE__ENFORCEMENT_ENABLED=false and ARGUS_CONNECTORS__FAIL_CLOSED_ON_LICENSE_ERROR=false. Verify the mounted production policy still preserves source-specific prohibited uses, restricted fields, and redistribution metadata so enforcement can be restored later. The application user must be able to read the mounted policy file. The prepared preflight explicitly parses and compares that file even with generic enforcement disabled, so an unreadable/invalid mount cannot pass through the runtime's empty-table fallback. Verify the actual service uid/groups against the file and parent directories; retain the content checksum when repairing permissions. A readable placeholder table still does not prove customer institution eligibility or source subscription rights.
  4. Confirm production settings reject placeholder secrets before deployment, that ARGUS_SECRETS_DIR_HOST is root-owned with group ARGUS_SECRETS_GID and exact mode 0750 or 0550, and that every required file below it is a non-placeholder, root-owned one-line value with mode 0640 or 0440, group matching ARGUS_SECRETS_GID, and no world access. Confirm the separate ARGUS_CASDOOR_PREFLIGHT_SECRETS_DIR_HOST follows the same permissions, contains only the two management credential files, and is absent from every long-running service mount. Confirm docker compose config and docker inspect do not contain the PostgreSQL, Redis, signing, metrics, model, Voyage, or connector secret values.
  5. Confirm the provider keys and identifiers are present for SEC EDGAR, FRED, OpenFIGI, and Voyage vector/rerank evidence retrieval.
  6. Run the protected GitHub Actions Staging validation workflow with real provider keys. It must complete its live connector smoke and deployed API checks before production promotion. It must also pass the known-filing structured evidence assertion and the public MCP initialize plus authenticated tool-call assertion.
  7. Validate production Compose configuration:
docker compose --env-file .env.production -f docker-compose.prod.yml config --quiet
scripts/production-preflight.sh
  1. Before application deployment, issue an institution:argus-primary machine credential from the migrated production database, store its one-time plaintext key, and update every production caller. Existing institution:prod credentials are not compatible. Set ARGUS_PRIMARY_MACHINE_CREDENTIAL_READY=YES only after the new key has been distributed and its institution id verified.
  2. Run the guarded deployment entrypoint. It checks the credential migration gate, starts dependencies, runs migrations, verifies the exact Alembic head, and waits for every app service to become healthy:
bash scripts/deploy-production.sh
  1. Verify health and readiness:
docker compose --env-file .env.production -f docker-compose.prod.yml exec -T api \
  python -m argus.container_health --json --require postgres redis object-storage
curl -fsS https://api.argusfa.com/health/ready

Verify the service-role checks too:

docker compose --env-file .env.production -f docker-compose.prod.yml exec -T worker \
  python -m argus.container_health --json --require celery-worker
docker compose --env-file .env.production -f docker-compose.prod.yml exec -T beat \
  python -m argus.container_health --json --require celery-beat
  1. Verify the replacement institution:argus-primary credential through REST or MCP, then revoke the superseded institution:prod credential.
  2. Confirm authenticated Redis access is reachable, because production machine rate limiting is distributed through Redis and fails closed when Redis is unavailable. Kill one worker while a test task is running and confirm the late- acknowledged persistent message is redelivered, reaches a terminal persisted status, and does not create a duplicate business result.
  3. Confirm /metrics requires X-Argus-Metrics-Key or a bearer token. Validate deploy/prometheus/argus.rules.yml and the active Prometheus configuration with promtool; prove API/MCP blackbox, node_exporter textfile, cAdvisor, queue depth, object-storage free space, and audit-integrity metrics are present. Trigger controlled alerts for disk, container restart, API error rate, queue backlog, stale backup, certificate expiry, and missing monitoring signals, and confirm Alertmanager sends them to an attended receiver. Confirm the MCP metrics port is bound only to loopback or the approved RFC1918 private/VPN address, the external Prometheus target resolves to that address, and the firewall permits the port only from the monitoring host. Confirm Celery beat contains audit-log-retention, verify the configured ARGUS_AUDIT__RETENTION_DAYS matches the approved retention policy, and confirm the latest run persisted a system audit with cutoff, deleted-record count, retention days, and a success or failure terminal status.
  4. Confirm API and MCP remain bound to loopback and are reachable only through the TLS reverse proxy. Verify the rendered Nginx configuration with nginx -t; confirm per-IP limit responses are 429, request size and timeout limits are active, and filesystem object storage is writable and persisted on the host volume. Confirm ARGUS_MCP_PUBLIC_URL uses HTTPS; non-local startup must reject HTTP. If the S3 backend is selected, confirm its endpoint uses HTTPS and ARGUS_OBJECT_STORAGE__SECURE_TRANSPORT=true.
  5. Run a scoped internal smoke query through REST or MCP and verify the response carries facts, evidence, license status, output restrictions, and an audit id.
  6. Capture rollback steps: previous image digest, migration downgrade decision, backup identifier, restore rehearsal identifier, and operator owner. Confirm restore-production.sh refuses to run while API, MCP, worker, or beat is running, paused, restarting, created, dead, or removing, then perform the rehearsal in an isolated Compose project and verify transactional PostgreSQL restore plus the known object-storage file.
  7. Confirm the systemd backup timer is active, .env.production is mode 0600, local retention is approved, and the latest restic snapshot exists when encrypted off-host replication is enabled. Confirm whether optional Redis queue backup is intentionally true or false. Confirm /var/lib/argus-backups is owned by argus-deploy and a forced systemctl start argus-backup.service reports restart failures rather than silently succeeding. During that forced run, verify API and MCP remain available; the online backup must not stop application services. Confirm node_exporter reports the newly updated argus_backup_last_success_unixtime value.
  8. Verify the release digest has passed Trivy repository/image scans and has a Cosign signature, SPDX SBOM attestation, and build provenance attestation.

Static website release gate

  1. Build the static website from the lockfile and confirm all locales and generated tool pages, /docs/, search, Markdown/JSON copy fallbacks, only the documented Skill download routes, localized section-specific Open Graph/Twitter cards, SEO/hreflang, axe, Lighthouse, and static artifact audit pass. Rebuild and verify product screenshots rather than carrying images forward from an earlier release.
  2. Confirm the release contains no API key input, secret, internal address, production Usage data, Registry/OpenAPI/Skill mirror, or page-level Markdown/JSON export. Verify Usage privacy thresholds and stale-state text.
  3. Set .env.website to mode 0600, run the website production preflight, build both Compose website images from the lockfiles with the same Git SHA, validate docker-compose.website.yml, and confirm its Nginx cache policy, MIME, security headers, 404 behavior, machine discovery files, and loopback-only reverse-proxy origin port. For a legacy state file, verify the actual :local Usage image was migrated to the previous release SHA before activation. Record current and previous Git-SHA state for website and Usage images, then rehearse their joint scripts/rollback-website-compose.sh, including failed-activation recovery, without touching API/MCP or the Usage volume.
  4. Confirm the website-usage Compose service is healthy with only its Prometheus token mount and persistent Usage volume; require /data/usage.json to exist and pass checksum validation, record the last valid snapshot age, and retry any pending IndexNow notification separately from release status.

CI Expectations

GitHub Actions must run unit, integration, end-to-end, documentation, container build, production Compose config, and production Compose startup smoke on normal pull requests and main pushes. Live connector smoke is intentionally excluded from ordinary commits and is required when the reusable quality gate is invoked with run_live_connector_smoke: true; the secure release workflow does so and must provide SEC_USER_AGENT, FRED_API_KEY, OPENFIGI_API_KEY, BEA_API_KEY, BLS_API_KEY, and EIA_API_KEY. Staging validation continues to run the same provider checks explicitly with staging secrets.

The prepared release includes an immutable build manifest inside its installed wheel and OCI revision label. /v1/release-identity reports the build commit, package/Registry versions and dependency-lock SHA-256; local or older builds without the manifest explicitly report that identity is unavailable. Runtime environment variables cannot substitute a release commit.

Dispatch staging validation at the candidate commit and supply that same full SHA as expected_commit. Before provider and business checks, the workflow requires the deployed API manifest to match the workflow source, versions and lock checksum. Redirects and mismatches fail the gate; preserve its JSON receipt even on failure. Use /mcp-machine for staging machine credentials, and verify ordinary OAuth independently. API identity does not prove that MCP, worker or beat uses the same digest: retain Compose/container digest and revision evidence for every deployed role, signed image provenance and the complete business matrix. This identity gate does not replace source or business acceptance.