SSA digest-registry rotation

On this page

Why this runbook exists

Every SSA screening run stages its cohort under versioned SSN digests (v<N>:<base64>) and a cohort hash computed over them. Rotating the digest registry to a new version makes every ACTIVE run staged under the old version fleet-wide unassertable: custody refuses the stale digest at release time (SSN_DIGEST_VERSION_ROTATED), so those runs can only fail member-by-member. The ratified posture (fork 4, option a) is an OPERATOR mass-invalidation as a rotation step — invalidate the stale runs up front so cases re-screen under the new version — with the D3 consumption-gate recompute as the lazy backstop for anything the sweep misses.

The rotation sequence

  1. Rotate the digest registry to the new version (the #1399 migration tooling owns the registry mechanics; this runbook starts once the new version is LIVE).

  2. Run the sweep, dry-run first (always — the candidate list is the review artifact):

    # Device-code OAuth as the responsible human admin:
    export CRAIG_SSA_OIDC_ISSUER=https://<keycloak>/realms/<realm>
    cargo xtask ssa-invalidate-stale-cohorts --current-version <N>
    
    # Review the candidate list, then execute:
    cargo xtask ssa-invalidate-stale-cohorts --current-version <N> --yes
  3. Re-screen: the invalidated runs' cases re-stage on their next screening intent and pick the new version up automatically.

What the sweep does (and audits)

The tool drives POST /v1/exchange/ssa-screening-runs/invalidate-stale-digests (human-admin-only; direct-token — service identities and BFF-lifted callers are both refused). Per stale run, in its own transaction: the B5 invalidate_run writer (cohort_changed, one-way predicate — a peer’s earlier stamp refuses, never doubles), the same-tx sweep (paused/pending jobs → failed/run-invalidated, pending members → cancelled) + complete_run_if_final, and the B6b exchange.screening_run_invalidated event. The ssa.stale_cohorts_invalidated summary stages BEFORE the per-run stamps, as the sweep INTENT over the candidate set — so every stamp that follows already has its attributing record even if the process dies mid-sweep. Pre/post counts are the response’s candidates vs invalidated arrays, printed by the tool.

Re-running is safe at any point: invalidated runs drop out of the candidate SELECT, and the one-way predicate refuses double stamps — a crash mid-sweep just leaves the remainder for the next pass, and the re-run declares a fresh intent summary over that remainder.

Boundaries

  • --current-version is deliberately explicit — the operator states the rotation target; the tool never guesses a version.

  • The sweep touches ACTIVE (pending, non-invalidated) runs only. Completed runs quiesce for archive under the fork-2 freshness predicate; their stale digests are inert.

  • Devstack: pass --bearer-token (an admin ROPC token) instead of the device-code envs.

Edit this page · latest