Skip to main content
The model in one line: a platform seals a child’s content directly to the provider’s enclave — Phosra (the census) carries only the content-free abuse signal, a minimization receipt, and a routing manifest. The census never sees plaintext or ciphertext.
The endpoints on this page are live on both sandbox censuses today, and the SDKs are published to npm: @phosra/provider (0.1.0), @phosra/classify (0.1.1), the conformance harness @openchildsafety/provider-harness (≥ 0.1.3 — live-enclave targeting), plus public @openchildsafety/ocss 0.1.5 and @phosra/gatekeeper 0.7.0. See SDK install.

Three roles

Invariants — do not weaken:
  • Phosra never receives content (plaintext or sealed JWE). Only content-free metadata reaches the census.
  • Classification is fail-open on safety: an expired attestation does not block content classification. Only Phosra-staff revocation triggers a hard fail-closed refusal.
  • The minimization receipt is labeled conservation_check: "self_report" in solo mode. Never write “verifiable deletion” — solo mode proves a count discrepancy but cannot prove any specific benign message was destroyed.
  • The routing-manifest and compliance-bundle POSTs are post-hoc — content is sealed and delivered to the enclave first, unconditionally. A manifest failure must never block delivery.

Quickstart

Both demo scripts run against the live sandbox with the default loopline + courier sim seeds:
Both scripts exit 0 on PASS, 1 on FAIL. Without CENSUS_URL they run in-process against a mock census (also exit 0 on PASS — no network required). Sandbox base URL (the one canonical partner sandbox host — why one host):
  • https://phosra-api-sandbox-production.up.railway.app
Root key X (base64url Ed25519 pub) — pinned out-of-band, matches the census above:
  • CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU
Default sim seeds (32-byte Ed25519 seeds, base64url — match the sandbox Trust List public keys):
  • PLATFORM_SEED_B64URL (loopline): bG9vcGxpbmUBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE
  • PROVIDER_SEED_B64URL (courier): Y291cmllcgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE

SDK install

All are published to npm: @phosra/provider (0.1.0) + @phosra/classify (0.1.1), the harness @openchildsafety/provider-harness (≥ 0.1.3), alongside public @openchildsafety/ocss (0.1.5) and @phosra/gatekeeper (0.7.0).
Import them from the installed packages:

Register an enclave

1. Issue a challenge nonce (Nitro only)

For the nitro hardware-attestation path, request a one-shot anti-replay challenge before registering:
Requires RFC-9421 signature. Response (201):
The nonce is single-use (5-minute TTL). Include it in the registration body as challenge_id.

2. Register the enclave

Requires RFC-9421 signature (the provider_did must be on the OCSS Trust List). Request body:
Response (201 Created):
Owner-upsert (redeploy). If your provider already has an active enclave and you re-register with the same RFC-9421-verified 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 stand-in (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

No authentication required. Response (200):
attested is computed at read timetrue 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:
What classify() does:
  1. Validates contentType is "text/*" client-side — rejects non-text before any network call.
  2. GET {censusUrl}/api/v1/enclaves/active?provider_did=… — the only census call; carries no content.
  3. Seals content to payload_pubkey_jwk → JWE.
  4. Builds an OCSS Envelope (inner.payload = JWE) and signs with platformSenderKey.
  5. POST {endpoint_url}/classify directly to the enclave — not to the census.
  6. Returns the enclave’s { receipt, verdict }.
The census never receives the JWE or plaintext. Step 5 goes to the enclave’s endpoint directly. FIX 3 attestation policy (when requireAttested: true): revoked:trueRevokedEnclaveError (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:
Invariants:
  • Each provider gets a separate JWE (sealed to its own payload_pubkey_jwk). A single shared ciphertext is not used.
  • All N enclave POSTs fire in parallel via Promise.all.
  • Fail-partial: K failures → N-K receipts, honest error per failure. classifyMulti never throws on partial failure.
  • The POST /routing-manifests is fired after all enclave deliveries complete (post-hoc). A manifest failure leaves manifestReceipt undefined but does not throw.
Consent-scoped discovery. Before calling classifyMulti, the platform can use the fan-out discovery endpoint to get the active enclaves for a child it has active consent standing with:
Requires RFC-9421 signature. Returns 403 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)

After classifyMulti, a content-free manifest is filed with the census:
Requires RFC-9421 signature. This is called automatically by 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:
POST /api/v1/compliance-bundles (called by your code; verifyComplianceBundle is purely client-side):
Requires RFC-9421 signature.
Response (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-checks f_delivered against its own harm_context_routes table and signs a receipt.

1. Accumulate messages during the epoch

Each leaf is 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’s harm_context delivery count. It does NOT prove any specific benign message was bit-wiped.
  • two_attestor: false means: 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

@openchildsafety/provider-harness (≥ 0.1.3) runs 7 behavioral assertions against your enclave. Pass it to Phosra as part of your accreditation request.
Live-enclave targeting requires ≥ 0.1.3. Older builds are reference-only and reject --enclave-url with v0 supports only --enclave ref. Pin the version (@openchildsafety/provider-harness@0.1.3) if npx resolves a cached older copy. There is no --census flag — the live-target lane probes your enclave directly.
The 7 assertions: The top-level label after a full run reads: “self-tested; production accreditor pending” — passing all available assertions is a necessary but not sufficient condition for Trust-List accreditation. A production accreditor (external to Phosra) will run the full suite including the pending assertions before any production Trust List entry.

Revoke an enclave (staff-only)

Requires Phosra-staff admin session. Sets 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.