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
-
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).
-
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 -
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-versionis 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.