The endpoints on this page are live on both sandbox censuses today, and the SDKs are published to npm:
@phosra/provider, @phosra/classify, @phosra/provider-harness (all 0.1.0), plus @openchildsafety/ocss 0.1.0 and @phosra/gatekeeper 0.2.0. See SDK install.Three roles
| Role | Does | Sees plaintext? |
|---|---|---|
| Platform (e.g. Snaptr) | Seals content to the enclave; calls classify() | Transiently, before sealing |
| Provider enclave | Decrypts, classifies; sends content-free signal to census | Yes — inside the enclave only, then zeroizes |
| Phosra (census) | Enclave registry; content-free signal + manifest + minimization receipt | Never. Carries only metadata. |
Quickstart
Both demo scripts run against the live sandbox with the default loopline + courier sim seeds:CENSUS_URL they run in-process against a mock census (also exit 0 on PASS — no network required).
Sandbox base URLs:
- Staging:
https://phosra-api-sandbox-staging.up.railway.app - Production:
https://phosra-api-sandbox-production.up.railway.app
- Staging:
jDWZfB70DM8B3ZnPG8FQ0tnanqLgwJX4ZMr_E1Ugv3w - Production:
CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU
PLATFORM_SEED_B64URL(loopline):bG9vcGxpbmUBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEPROVIDER_SEED_B64URL(courier):Y291cmllcgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE
SDK install
All three are published to npm (0.1.0), alongside
@openchildsafety/ocss (0.1.0) and @phosra/gatekeeper (0.2.0).node_modules/@phosra/…):
Register an enclave
1. Issue a challenge nonce (Nitro only)
For thenitro hardware-attestation path, request a one-shot anti-replay challenge before registering:
201):
challenge_id.
2. Register the enclave
provider_did must be on the OCSS Trust List).
Request body:
| Field | Required | Notes |
|---|---|---|
provider_did | Yes | did:ocss:<slug> — must be on the OCSS Trust List |
mode | Yes | "standin" (dev/demo — no hardware) or "nitro" (AWS Nitro Enclave) |
endpoint_url | Yes | Base URL of your enclave; the /classify path is derived from this |
payload_pubkey_jwk | Yes | EC P-256 public JWK the platform seals content to |
signing_pubkey | Yes | Ed25519 pub X (base64url) for receipt verification |
attestation_document | No | Nitro COSE_Sign1 attestation doc (base64url); required for attested:true |
challenge_id | No | UUID from POST /enclaves/challenge; required when providing attestation_document |
201 Created):
provider_did, the census re-points the existing record (new endpoint + keys, attestation reset) and returns 200 with the existing enclave_id instead of 409. This is the correct deploy-time pattern: call POST /enclaves on every deploy without deactivating first.
Nitro attestation and the standin tier
attested:true is hardware-gated — it requires a real AWS Nitro Enclave build, PCR pins (OCSS_NITRO_PCR0/PCR1/PCR2 env vars on the census), and a key generated inside the enclave. The live sandbox today returns attested:false for all mode:"standin" registrations. This is the honest live tier: enclaves operate normally with attested:false; only Phosra-staff revocation (revoked:true) triggers a hard fail-closed refusal.Discover an active enclave
200):
attested is computed at read time — true only when the DB row’s attested_expires_at is in the future and revocation_reason is NULL. Expired (attested:false, revoked:false) is fail-open. Revoked is fail-closed in the SDK when requireAttested:true is set.
Classify (single provider)
@phosra/classify seals content and sends it directly to the provider enclave:
- Validates
contentTypeis"text/*"client-side — rejects non-text before any network call. GET {censusUrl}/api/v1/enclaves/active?provider_did=…— the only census call; carries no content.- Seals
contenttopayload_pubkey_jwk→ JWE. - Builds an OCSS Envelope (
inner.payload = JWE) and signs withplatformSenderKey. POST {endpoint_url}/classifydirectly to the enclave — not to the census.- Returns the enclave’s
{ receipt, verdict }.
requireAttested: true): revoked:true → RevokedEnclaveError (fail-closed). Expired attestation → proceed (fail-open — safety preserved).
Classify (fan-out — N providers in parallel)
classifyMulti seals content and delivers it in parallel to N provider enclaves:
- Each provider gets a separate JWE (sealed to its own
payload_pubkey_jwk). A single shared ciphertext is not used. - All
Nenclave POSTs fire in parallel viaPromise.all. - Fail-partial: K failures → N-K receipts, honest
errorper failure.classifyMultinever throws on partial failure. - The
POST /routing-manifestsis fired after all enclave deliveries complete (post-hoc). A manifest failure leavesmanifestReceiptundefined but does not throw.
classifyMulti, the platform can use the fan-out discovery endpoint to get the active enclaves for a child it has active consent standing with:
standing_failure if the calling platform has no active consent_attestation for (caller.DID, child_ref). Response is an array of { provider_did, endpoint_url, payload_pubkey_jwk, signing_pubkey, attested }.
Routing manifest (post-hoc fan-out record)
AfterclassifyMulti, a content-free manifest is filed with the census:
classifyMulti — you do not need to call it manually unless you are building a custom fan-out loop.
Request body:
provider_count must equal len(provider_key_ids) — the census rejects mismatches. No content, no URLs, no plaintext — DisallowUnknownFields rejects any extra field.
Response (201): a router-signed routing_manifest_receipt. Store the raw JSON string — use it as-is for verifyComplianceBundle; do not re-serialize.
Compliance bundle (client-side verification)
verifyComplianceBundle independently verifies receipt signatures and the Merkle root — no trust in the census’s claims:
verifyComplianceBundle is purely client-side):
201): compliance_bundle_receipt with body.verified_count, body.unverified_count, body.merkle_root. Use verifyComplianceBundle to independently recompute the Merkle root — do not trust the census’s verified_count alone.
Minimization attestation
At the end of each epoch, the enclave self-reports how many messages it classified, how many were flagged, and how many harmful excerpts were delivered via the census. The census cross-checksf_delivered against its own harm_context_routes table and signs a receipt.
1. Accumulate messages during the epoch
HMAC-SHA256(enclaveIdentityKey, msgRef|kind|harmClass) — bare SHA-256 hashes are rejected. delivered: false means the excerpt was not forwarded via the census harm_context lane (the typical case for DIRECT topology; DIRECT/intra-provider deliveries are not census-visible).
2. Close the epoch and submit
Wire contract (POST /api/v1/minimization-attestation)
Requires RFC-9421 signature (enclave_did in the body must match the verified caller DID).
DisallowUnknownFields rejects any body carrying extra fields (e.g. excerpt, content, payload). The census returns 201 regardless of conservation_check — a "fail" label is the honest signal of a discrepancy, not an error.
Honest limits of the minimization receipt:
conservation_check: "self_report"means: your counts are internally consistent AND match the census’sharm_contextdelivery count. It does NOT prove any specific benign message was bit-wiped.two_attestor: falsemeans: only your count was checked. The two-attestor upgrade (platform co-signs its ingestion count) is planned but requires a co-signing partner.- Never use the phrase “verifiable deletion” — it is over-strong for solo mode.
Conformance harness
@phosra/provider-harness runs 7 behavioral assertions against your enclave. Pass it to Phosra as part of your accreditation request.
| ID | Name | Live status |
|---|---|---|
| A1 | closed-enum fail-closed | PASS (census rejects out-of-enum harm class) |
| A2 | content-free signal lane | PASS (mock mode — requires in-process inspector) |
| A3 | sealed-to-consent-recipient only | PENDING — requires consent infra |
| A4 | parent-sole-control + monitoring_active indicator | PENDING — requires GET /capabilities on enclave |
| A5 | minimization attestation wired (HMAC-salted leaves) | PASS — labels receipt conservation_check: "self_report" |
| A6 | abuse-at-home → advocate routing | PENDING — advocate lane (Tier 2.5) not built |
| A7 | attestation-fail → suspend (fail-closed before content) | PASS — forged sender_signature → 4xx |
Revoke an enclave (staff-only)
revocation_reason so the entry reads attested:false, revoked:true at GET time. The SDK’s RevokedEnclaveError then prevents any platform from sealing to that enclave when requireAttested:true.
Providers cannot self-revoke — Phosra staff revoke only on evidence of compromise.
Next steps
- Sandbox onboarding — see OCSS Onboarding to self-register your
did:ocss:<slug>on the sandbox Trust List. - Content monitoring architecture — see Safe Content Monitoring for the full on-device classifier design.
- Production accreditation — see Production Accreditation to submit your harness results for Trust List accreditation.