When everything returns 503
createPlatform from @phosra/gatekeeper — the catch-all platform
facade that mounts /api/phosra/{status,par,authorize,token,delivery,retry}. Everything below
(adapter.authorizedProviders, adapter.database, GET /api/phosra/status, ready()) is that
factory’s surface. The SDK reference is @phosra/gatekeeper →
createPlatform.It does not apply to createConnectReceiver from @phosra/gatekeeper/next, which returns an
object with exactly one member — POST. That factory mounts no status route, exposes no
ready(), and has no readiness machinery at all. If you followed the
createConnectReceiver quickstart and are curling
/api/phosra/status, you will get your framework’s HTML 404 no matter which SDK version you
install. Move to createPlatform before using this page./api/phosra/authorize returns this and nothing else:
GET /api/phosra/status is the answer. Same deployment, same process, same request cycle.
It always answers 200 and names the outstanding precondition.
@phosra/gatekeeper ≥ 0.8.68. Published and live on npm. If you declare
^0.6.0, npm will not resolve 0.8.68 — a caret on a 0.x version pins the minor. Change the
range to ^0.8.68.If you vendored the package, remove the vendored copy first; a vendored build will not have this
route no matter what your package.json says.phosra.next.handlers() that already mounts
your protocol routes, and is also reachable directly at platform.handlers.status.GET.
Read it
/api/phosra/authorize returns 503. That is the
point: the diagnostic is not behind the gate it explains. A diagnostic that 503s under the
fault it describes is not a diagnostic.
Fields
signingKey means the check never got that far, not that
the key is fine.?probe=0
PLATFORM_GENERATION_INACTIVE
is only observable this way.
Without probe=0 the endpoint reuses the existing generation coordinator: a healthy platform
answers with zero network I/O, and an unhealthy one respects the coordinator’s bounded
exponential backoff (30s base, 5min cap). You cannot hammer the census with this endpoint.
Non-GET methods return 405 with Allow: GET.
Is it safe to expose?
Yes, and the reasoning is worth stating rather than assuming. Every field is already public: DIDs are public identifiers; the trust list is world-readable at/.well-known/ocss/trust-list; a signing key’s public half is precisely the thing meant to
be published; missing is the SDK’s own schema object names; versions are in your package.json.
Gating it would require distributing a new shared secret — the exact friction Phosra Link
exists to remove (OCSS §8.1.6 bans bearer secrets) — and would withhold the answer from the
person most likely to need it: a partner debugging your deployment from outside it.
The thirteen preconditions
PLATFORM_READY — everything passed
PLATFORM_READY — everything passed
correlationId in the route’s own error body.CREDENTIAL_MISSING — no credential configured
CREDENTIAL_MISSING — no credential configured
createPlatform is not a
Phosra credential envelope.Cause. PHOSRA_CREDENTIAL is unset, empty, or holds something that is not an envelope
(envelopes start phosra_cred_v<n>.). A very common shape: the variable exists in your dashboard
but was never rebuilt into the image.Do. Set it to the credential value you were issued and redeploy. Confirm the deploy
actually rebuilt — versions in this same response tells you which SDK is running.remedy string above names
POST /developers/orgs/{orgId}/apps, and that funnel is real — but it
is live only on the Phosra-internal staging sandbox census, and a freshly self-registered DID is
provisional while issuance requires accredited. Today a platform credential is
operator-issued: email developers@phosra.com with your DID (or
desired display name), your canonical application origin, and sandbox-or-production. See
Getting a platform credential.CREDENTIAL_UNPARSEABLE — envelope present but will not bind
CREDENTIAL_UNPARSEABLE — envelope present but will not bind
- Truncated on copy — envelopes are long and survive no line-wrapping.
- Environment mismatch — a sandbox credential in a production deployment, or vice versa.
- Manifest drift. The credential pins the environment manifest’s
idand aconfig_digestcomputed over the manifest’s wholeconfigurationobject. If any part of that configuration changes —sdk_minimum, origins, rule-registry edition, root key id — every previously issued credential fails to bind at once. This is a global condition, not a per-deployment one: if several unrelated platforms broke simultaneously, look here first.
TRUST_LIST_UNVERIFIABLE — cannot reach or verify the census
TRUST_LIST_UNVERIFIABLE — cannot reach or verify the census
824vsCATBxyUiA-znpGx01N48NNs_3gPE3M7f7vIEaI. Pin the public X,
not the key id. A credential pinning the retired root-prod-2026-06 fails closed on every
current document — correctly.PLATFORM_SIGNING_KEY_UNPUBLISHED — census publishes no such kid
PLATFORM_SIGNING_KEY_UNPUBLISHED — census publishes no such kid
kid that is simply not on your trust-list entry — often
after a rotation that added a new kid and dropped the old one, while a deployment still holds a
credential naming the dropped one.Do. Read signingKey.kid from the response, then list what the census actually publishes:document is served as a JSON string, not a nested object — it is the exact byte sequence
the root signature covers. Hence the fromjson. Parsing it any other way changes the bytes and
breaks verification.PLATFORM_SIGNING_KEY_MISMATCH — the divergence this endpoint exists for
PLATFORM_SIGNING_KEY_MISMATCH — the divergence this endpoint exists for
/apps, which generates the keypair and
mints the credential from that same key, so they cannot disagree. Then set the returned
credential and redeploy. See the divergence note for how
this happens and who is exposed.PLATFORM_STANDING_NOT_ACCREDITED — your standing lapsed
PLATFORM_STANDING_NOT_ACCREDITED — your standing lapsed
valid_through elapsed.Do. Read the standing object in the response, then confirm against the census:active and accredited. Restoring standing is an operator action — attestations are
issued by a verifying agency, not self-served.PLATFORM_COMPOSITION_FAILED — SDK-internal
PLATFORM_COMPOSITION_FAILED — SDK-internal
onDiagnostic
hook, plus the versions block from this response.onDiagnostic is a first-class option
on createPlatform and is absent from most quickstart snippets:operation: "materialization" (a delivery pass failed
mid-flight) and operation: "target_aggregate_lifecycle". See
onDiagnostic.PLATFORM_DATABASE_NOT_READY — your Postgres
PLATFORM_DATABASE_NOT_READY — your Postgres
database.missing — it lists the exact object names still absent. Confirm the Pool
handed to adapter.database points at a reachable Postgres the app is allowed to migrate, then
retry.PLATFORM_GENERATION_INACTIVE — nothing has tried yet
PLATFORM_GENERATION_INACTIVE — nothing has tried yet
?probe=0 before anything ran, or the process genuinely has not
attempted bootstrap.Do. Re-read without probe=0 to trigger one bounded attempt, then read the real code.A 2am triage order
Confirm the endpoint even exists
curl -s .../api/phosra/status, and read the shape of the failure — the three cases have
three different fixes:404with{"error":{"code":"PHOSRA_ROUTE_NOT_FOUND"}}— the SDK is mounted and answering, but predates 0.8.68. Upgrade.- Your framework’s HTML
404— nothing Phosra is mounted at that path. Either the catch-all is not wired, or you built with the wrong factory:createConnectReceivermounts a singlePOSTand no status route, so no version of the SDK will ever answer here. Check which factory your singleton calls before you touchpackage.json. 307/302to a login page — your own auth middleware is shadowing/api/phosra/*. Exempt that prefix.
Read `precondition`, not the logs
remedy names the fix.Check `versions`
If it is a key or standing fault, verify against the census yourself
signingKey.censusPublicKey with what
/.well-known/ocss/trust-list publishes for your DID.If it is a divergence, re-provision — do not retry
PLATFORM_SIGNING_KEY_MISMATCH never heals on its own. See
the divergence note.What readiness cannot check
Readiness proves the boot preconditions. Four things break a live ceremony and are invisible here, so check them by hand once per deployment:PLATFORM_READY and nothing ever arrives, ask Phosra to confirm it.
@phosra/gatekeeper — createPlatform
authorizedProviders,
framework-agnostic mounting, the worker, and how to obtain a credential.