This is the engineering pre-flight — not accreditation. Getting your entry onto the
production Trust List is a separate governance gate with its own vetting, SLA, and
eligibility bar. Do that on
Production Accreditation. Come back here for the
code-side switch-over once you are cleared.
The checklist
- 1. Rotate test → live keys — mint
phosra_live_keys; retire everyphosra_test_key from prod config - 2. Lock scopes to least privilege — each key holds only the scopes its service calls
- 3. Verify webhook signatures in prod — reject any delivery whose HMAC does not recompute
- 4. Arm Deprecation / Sunset monitoring — alert on any
DeprecationorSunsetresponse header - 5. Diff sandbox → prod config — base URL, trust root, and org id all move together
- 6. Confirm rate-limit headroom — read
X-RateLimit-Remaining; back off on429, never hammer - 7. Pin exact SDK versions — no caret ranges on a pre-1.0 line
- 8. Wire health / status monitoring — poll
/healthand the trust-listETag
Rotate test → live keys
A The raw key is in the response
phosra_test_ key only works against the sandbox base URL; a phosra_live_ key
only works against production. Mint live keys, load them into your prod secret manager,
and make sure no phosra_test_ value survives in production config.Mint a live key
key field and is shown exactly once — only a
SHA-256 hash is stored. Prefer zero-downtime rotation (mint
the new key, deploy, then revoke the old) so nothing breaks mid-cutover.→ Authentication · test vs live keysLock scopes to least privilege
Every key carries an explicit scope list; a call to a route your key lacks returns
→ Authentication · scoping
403, never silent success. Grant the fewest scopes each service needs — a read-only
reporting job should never hold write:enforcement. If one key leaks, the blast radius
is only that key’s scopes on that one environment.Read the scopes bound to a key
Verify webhook signatures in prod
Every delivery is signed → Webhooks · verifying signatures
(copy-paste verifiers in TypeScript, Python, and Go)
t=<unix>,v1=<hex> in the Phosra-Signature header. Recompute
HMAC-SHA256(secret, "<t>." + rawBody) over the raw body and reject on any mismatch —
otherwise anyone who learns your endpoint URL can forge policy-change events. Use the
per-environment signing secret returned when you register the production webhook, and
compare in constant time.Recompute and compare (bash)
Arm Deprecation / Sunset monitoring
Before any v1 endpoint or field changes, the census sends
RFC 9745 → Deprecation & Sunset Policy · on the wire
Deprecation and
RFC 8594 Sunset response headers through the
entire advance-notice window. Log or alert on their presence so a migration deadline
never reaches you as a surprise 410. Today the sandbox sends none — a clean scan is the
healthy baseline you are monitoring for change against.Scan for lifecycle headers (empty today = healthy)
Diff sandbox → prod config
Three values change together when you leave the sandbox. Miss one and a
Do not copy the sandbox trust root into production — the production root is a different
Ed25519 identity (For an end-to-end round-trip, → CLI · phosra doctor ·
Production Accreditation · after you are accredited
phosra_live_ key will hit the sandbox census (or vice-versa) and fail closed.| Setting | Sandbox | Production |
|---|---|---|
| API / census base URL | https://phosra-api-sandbox-production.up.railway.app | https://prodapi.phosra.com/api/v1 |
LinkConfig.trustRootXB64Url | sandbox root X | distinct prod root X (Phosra provides) |
LinkConfig.developerOrgId | — | your org_… (billing attribution) |
| API key | phosra_test_… | phosra_live_… |
root-prod-2026-06), and pinning the wrong one makes every signature
verification fail. Before you pin, confirm the census you are about to trust really is the
production one by reading the root key id it signs its Trust List with:Confirm the production root (run live)
Output
phosra doctor drives all three values at once. It reads
the census from the environment (there is no --env flag) — override PHOSRA_CENSUS_URL
and PHOSRA_TRUST_ROOT_X to point it at any census without touching your config file:Drive the doctor against a chosen census
Confirm rate-limit headroom
Every metered response carries → Rate limits · read your window
X-RateLimit-Limit, X-RateLimit-Remaining, and
X-RateLimit-Reset (unix epoch seconds). Read them instead of hard-coding a quota, and
on a 429 sleep until the reset — never retry immediately. Check your standing before a
launch that spikes traffic:Read your current window
Response headers
Pin exact SDK versions
The published SDKs are pre-1.0, so a → Versioning · pin exact ·
Forward compatibility
0.x minor bump may carry breaking changes. Pin
the exact version you tested against — not a caret range — and bump on purpose after
reading the changelog. Every version below is live on the npm registry.package.json
Install pinned
Wire health / status monitoring
Poll → Versioning · conditional reads ·
Deprecation · detect programmatically
/health for liveness, and watch the trust-list ETag so you notice when the
signed census document (rules, accredited entries, spec version) actually moves — a
304 Not Modified means nothing changed; a 200 means re-read and re-verify.Liveness + change detection
One-command pre-flight
Drop this in your CI or run it by hand right before the switch. It confirms liveness, prints your rate-limit headroom, flags any active deprecation, and echoes the spec version the census is pinned to. PointPHOSRA_API at production once your live keys are loaded.
preflight.sh
Output (run live against the sandbox census)
All four green, keys rotated, scopes locked, webhook verification live, and monitoring
armed? You are clear to flip
PHOSRA_API and your keys to production. The request shapes
are identical between sandbox and prod — only the base URL, trust root, and key change.Next steps
Production Accreditation
The governance gate: get your entry onto the production Trust List.
Authentication
Test vs live keys, scoping, and zero-downtime rotation.
Webhooks
Signed event catalog and signature verification in four languages.
Deprecation & Sunset
The advance-notice window and the headers that carry it.
Rate limits
The window headers and a copy-paste backoff loop.
Versioning
Pin the API path, spec version, dated editions, and SDK semver.