# Consult advisor Source: https://docs.phosra.com/api-reference/control-plane/consult-advisor POST /advisors/consult Dispatches a consultation to the best available advisor for the given capability domain. Returns a verdict and a consultation ID for audit purposes. If no suitable advisor is registered, verdict is `unavailable` with reason `no_advisor` — this is still a 200 response. # Create API key Source: https://docs.phosra.com/api-reference/control-plane/create-key POST /developers/orgs/{orgId}/keys **First key? Start here.** This endpoint needs an existing developer org and a **WorkOS session bearer** (from signup) — *not* a `phosra_` key. You never need a `phosra_` key to mint your first `phosra_` key. The full zero-to-key path — sign up, auto-provision an org, mint the first key (console or one `curl`) — is [Create your account & get keys](/platform/create-account). The `orgId` above is the org that page returns. # Create MCP token Source: https://docs.phosra.com/api-reference/control-plane/create-mcp-token POST /mcp-tokens Issues a new MCP token bound to the authenticated user's first family. The plaintext token (`plain`, prefix `phomcp_`) is returned once in `CreatedMcpToken` and never stored server-side — the caller must display it to the user immediately. # Create organization Source: https://docs.phosra.com/api-reference/control-plane/create-org POST /developers/orgs # Declare payload key Source: https://docs.phosra.com/api-reference/control-plane/declare-payload-key POST /advisors/{id}/payload-key Self-service possession-auth declaration. Authentication is by the advisor's registered Ed25519 signing key: the body must carry a detached signature over the exact `payload_public_key_jwk` string bytes plus a freshness timestamp. No bearer token scheme applies to this operation — authorization is established by possession of the registered private key. Clearing a declared key is not self-service — use the admin revoke flow. # Delete organization Source: https://docs.phosra.com/api-reference/control-plane/delete-org DELETE /developers/orgs/{orgId} # Get organization Source: https://docs.phosra.com/api-reference/control-plane/get-org GET /developers/orgs/{orgId} # Get usage Source: https://docs.phosra.com/api-reference/control-plane/get-usage GET /developers/orgs/{orgId}/usage # List API keys Source: https://docs.phosra.com/api-reference/control-plane/list-keys GET /developers/orgs/{orgId}/keys # List MCP tokens Source: https://docs.phosra.com/api-reference/control-plane/list-mcp-tokens GET /mcp-tokens # List members Source: https://docs.phosra.com/api-reference/control-plane/list-members GET /developers/orgs/{orgId}/members # List organizations Source: https://docs.phosra.com/api-reference/control-plane/list-orgs GET /developers/orgs # Regenerate API key Source: https://docs.phosra.com/api-reference/control-plane/regenerate-key POST /developers/orgs/{orgId}/keys/{keyId}/regenerate Issues a new secret for the given key, invalidating the previous secret. The raw secret (`key`) is returned only in this response. # Register advisor Source: https://docs.phosra.com/api-reference/control-plane/register-advisor POST /advisors/register Registers an advisor agent at provisional tier. For scope=global a DNS verification token is returned — publish it as a DNS TXT record at `_phosra-verify.` to complete ownership verification. An unrecognized capability name causes the request to be rejected with 400 (`unknown_capability: `). A recognized capability above the provisional band is silently dropped from the accepted set. # Revoke API key Source: https://docs.phosra.com/api-reference/control-plane/revoke-key DELETE /developers/orgs/{orgId}/keys/{keyId} # Revoke MCP token Source: https://docs.phosra.com/api-reference/control-plane/revoke-mcp-token DELETE /mcp-tokens/{id} # Update organization Source: https://docs.phosra.com/api-reference/control-plane/update-org PUT /developers/orgs/{orgId} # Get provider connect config Source: https://docs.phosra.com/api-reference/providers/connect GET /providers/{did}/connect Returns the OAuth endpoints `@phosra/link` uses to run the connect ceremony against a provider. This is the **Phosra product layer** of a two-layer resolution — it answers *how* to reach a provider. Whether you *may* connect is decided first by the OCSS Trust List (`/.well-known/ocss/trust-list`, root-verified): the SDK's resolver requires the DID to be an `active`, unexpired entry before this endpoint is ever fetched, and fails closed otherwise. Connect configs are deliberately **not** part of the signed OCSS document — the standard stays vendor-neutral (trust, keys, accreditation); connection mechanics are Phosra's concern as the OCSS implementor. Responds `404` (without distinguishing the cases) when the DID is unknown, revoked, or accredited but has no connect config yet. ## Two layers, resolved in order Connecting a platform to a provider is a **two-layer resolution**, and the layers are deliberately not the same trust surface: 1. **The OCSS Trust List decides *whether* you may connect.** The SDK resolver fetches the root-verified `/.well-known/ocss/trust-list` and requires the target DID to be an `active`, unexpired entry. Absent, suspended, revoked, or expired — the resolver **fails closed** and this endpoint is never called. 2. **This endpoint (`GET /providers/{did}/connect`) says *how* to reach it.** It returns the OAuth `authorize_url` / `token_url` / `profiles_url` / `scopes` a client uses to run the connect ceremony. It is a **Phosra product-layer** lookup, deliberately **not** part of the signed OCSS document — the standard stays vendor-neutral (trust, keys, accreditation); connection mechanics are Phosra's concern as the OCSS implementor. `404` is returned (without distinguishing the cases) for an unknown DID, a revoked DID, or an accredited DID that has no connect config registered yet — no existence leak. ## Registering your connect config There is no self-serve write path for this endpoint yet. To get your platform's connect config published: **Interim (today):** email [developers@phosra.com](mailto:developers@phosra.com) with the four fields: * `authorize_url` — your OAuth 2.0 authorization endpoint (PKCE S256) * `token_url` — your OAuth 2.0 token endpoint * `profiles_url` — your child-profile listing endpoint (bearer access token; the token is discarded after the profile fetch) * `scopes` — the OAuth scopes the connect ceremony should request (e.g. `child_profiles.read`) Phosra publishes the config to the connect registry once your DID is on the Trust List. **Roadmapped:** a self-serve, RFC-9421-signed `PUT` on this same path, so an accredited platform can register and rotate its own connect config without an email round-trip. Not yet built — no such endpoint exists today. ## Finding the `{did}` This lookup takes a provider `{did}` — you find one by browsing [`GET /api/v1/directory`](/integration/provider-discovery). Enumerate the directory, select an entry that advertises a sealed-classify `enclave`, and call this endpoint on its DID: a `200` means connectable, a `404` means it is on the Trust List but has no connect config yet. # Bulk apply rules Source: https://docs.phosra.com/api-reference/rules/bulk-upsert-rules PUT /policies/{policyID}/rules/bulk Create or update multiple rules at once for a policy. Each rule in the array must include category, enabled, and config. # Apply a rule to a policy Source: https://docs.phosra.com/api-reference/rules/create-rule POST /policies/{policyID}/rules `category` is one of the 123 OCSS rule categories — see the [Rule Categories reference](/ocss/rule-reference). A 201 returns the stored `PolicyRule` record (`id`, `category`, `enabled`, `config`, …); it confirms the write, **not** that the rule is enforced at the platform. (The OCSS *signed* write receipt — an Ed25519-signed envelope verifiable against the Trust List — is a separate surface; see [OCSS signed rule writes](/ocss/intent-api).) **Testing this against the sandbox?** Point the call at the production sandbox census base URL `https://phosra-api-sandbox-production.up.railway.app/api/v1` (seeded demo family + `phosra_test_` keys), not the production base shown in the playground. The `@phosra/cli` `phosra init` flow wires this URL for you. # Delete a rule Source: https://docs.phosra.com/api-reference/rules/delete-rule DELETE /rules/{ruleID} # List a policy's rules Source: https://docs.phosra.com/api-reference/rules/list-policy-rules GET /policies/{policyID}/rules # Update a rule Source: https://docs.phosra.com/api-reference/rules/update-rule PUT /rules/{ruleID} # Authentication Source: https://docs.phosra.com/authentication Which credential to use — WorkOS AuthKit JWT, developer API key, or device key Phosra has three authentication surfaces, each for a different caller. Pick the row that matches what you are building: ## Which path do I use? | I am building… | Method | Credential | Issuer / format | | -------------------------------- | ------------------ | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Parent app (web / iOS / Android) | WorkOS AuthKit JWT | `Authorization: Bearer ` | iss `https://api.workos.com/user_management/client_01KH1YWWNH3MPMCPHCF6GNZ777`; JWKS `https://api.workos.com/sso/jwks/client_01KH1YWWNH3MPMCPHCF6GNZ777`; RS256 | | Server / B2B / agent | Developer API key | `Authorization: Bearer phosra_live_<64hex>` (test: `phosra_test_<64hex>`) | SHA-256 stored, scoped | | On-device enforcement agent | Device key | `X-Device-Key: phosra_dev_` | — | *** ## Consumer path — WorkOS AuthKit JWT Phosra uses [WorkOS AuthKit](https://workos.com/docs/auth-kit) for all end-user login. **There is no `/auth/register`, `/auth/login`, or `/auth/refresh` on the Go API.** These endpoints do not exist and return 404. Account creation and session issuance happen entirely in WorkOS — the Go API only validates the JWT, never issues or refreshes one. Authentication entry points: * **Web:** the `authkit-nextjs` SDK manages the session automatically. Your Next.js BFF exposes `/api/auth/mobile/exchange` and `/api/auth/mobile/refresh` — these are Next.js routes, not Go API routes. * **Native (iOS/Android):** initiate AuthKit's hosted login flow, exchange the code through your BFF, then pass the WorkOS access token to the Go API. **Token lifetime + refresh:** managed entirely by WorkOS. The web SDK refreshes silently via its session cookie. Native clients call your BFF's `/api/auth/mobile/refresh`. The Go API has no refresh route. Go API routes that exist under `/auth`: * `GET /auth/me` — returns the authenticated user profile * `POST /auth/logout` — no-op (session revocation is handled by WorkOS) ```bash theme={null} curl https://prodapi.phosra.com/api/v1/families \ -H "Authorization: Bearer " ``` Routes: `/api/v1/{resource}` — families, children, policies, enforcement, compliance, webhooks, standards, ratings, devices. *** ## Server / B2B path — Developer API key For server-to-server integrations, automated pipelines, and AI agents. **Key format:** `phosra_live_<64 hex chars>` for production, `phosra_test_<64 hex chars>` for sandbox. There is no `_sk_` infix — the format is exactly `phosra_live_` or `phosra_test_` followed by 64 hex characters. **Self-serve issuance** (no pre-existing key required — full walkthrough: [Create your account & get keys](/platform/create-account)): 1. Sign up (or log in) via WorkOS AuthKit at [dashboard.phosra.com/signup](https://dashboard.phosra.com/signup) — the [developer console](https://dashboard.phosra.com/dashboard/developers/console) auto-provisions your developer org on first visit, and its [Keys page](https://dashboard.phosra.com/dashboard/developers/keys) mints keys in the browser 2. Or via API: `POST /api/v1/developers/orgs` — create your developer org (WorkOS session bearer) 3. `POST /api/v1/developers/orgs/{orgId}/keys` — provision a key (shown once; store it immediately) 4. `POST /api/v1/developers/orgs/{orgId}/keys/{keyId}/regenerate` — rotate when needed ```bash theme={null} # Create a key (requires a WorkOS session bearer) curl -X POST https://prodapi.phosra.com/api/v1/developers/orgs/{orgId}/keys \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"name": "my-server-key", "scopes": ["read:families", "write:policies", "write:enforcement"]}' # Use the returned key on developer routes curl https://prodapi.phosra.com/api/v1/developer/families \ -H "Authorization: Bearer phosra_live_4a1b2c..." ``` Routes: `/api/v1/developer/{resource}` — same underlying handlers as the consumer surface, authenticated via the developer key path. See [Endpoint consistency](#consumer-vs-developer-surface) below. ### Scopes | Scope | Description | | ------------------- | ----------------------------------------- | | `read:families` | List and view family details | | `write:families` | Create, update, and delete families | | `read:policies` | View policies and rules | | `write:policies` | Create, update, delete policies and rules | | `read:enforcement` | View enforcement jobs and results | | `write:enforcement` | Trigger enforcement and retry jobs | | `read:ratings` | Access content rating systems | | `read:platforms` | Browse and filter supported platforms | | `write:compliance` | Connect and disconnect platforms | | `device:manage` | Register and manage on-device enforcement | | `webhook:manage` | Create, update, delete, and test webhooks | | `read:standards` | Browse safety standards | | `adopt:standards` | Adopt community safety standards | Never expose API keys in client-side code or commit them to version control. Use environment variables and server-side calls only. *** ## On-device path — Device key For iOS on-device enforcement via Apple FamilyControls. ```bash theme={null} curl https://prodapi.phosra.com/api/v1/device/policy \ -H "X-Device-Key: phosra_dev_abc123..." ``` Device keys are issued by `POST /children/{childID}/devices` and should be stored in the iOS Keychain. They provide access only to device-specific endpoints: * `GET /device/policy` — fetch compiled policy for this device * `POST /device/report` — submit activity reports * `POST /device/ack` — acknowledge policy version `X-Device-Key` is for **Apple FamilyControls on-device** enforcement only. The `CompiledPolicy` it returns is shaped for iOS ManagedSettings and **does not carry engagement rules** (e.g. `infinite_scroll_block`, `addictive_pattern_block`). If you are a **web or server-side platform**, use the OCSS provider path: RFC-9421 DID-signed requests + the signed enforcement profile (`GET /api/v1/enforcement-profiles/{endpoint_id}`). See [Platform integration](/integration/platform). *** ## Consumer vs developer surface `/api/v1/{resource}` and `/api/v1/developer/{resource}` are the **same handlers** — not separate codebases. The split is an auth-path decision, not a capability split. | Resource | Consumer path (WorkOS JWT) | Developer path (API key) | | -------- | ------------------------------------ | ---------------------------------------------- | | Families | `GET /api/v1/families` | `GET /api/v1/developer/families` | | Children | `GET /api/v1/families/{id}/children` | `GET /api/v1/developer/families/{id}/children` | | Policies | `GET /api/v1/children/{id}/policies` | `GET /api/v1/developer/children/{id}/policies` | | Enforce | `POST /api/v1/children/{id}/enforce` | `POST /api/v1/developer/children/{id}/enforce` | Choose the prefix that matches your credential. Both return identical response shapes. *** ## Error responses Authentication failures return `401 Unauthorized`: ```json theme={null} { "error": "unauthorized", "message": "Invalid or expired access token" } ``` Insufficient permissions return `403 Forbidden`: ```json theme={null} { "error": "forbidden", "message": "API key does not have the required scope: write:enforcement" } ``` # Children & Age Groups Source: https://docs.phosra.com/concepts/children-and-age How Phosra models children and computes age-appropriate defaults A **child** is a profile within a family, defined by their name and birth date. Phosra uses the birth date to compute the child's current age and map them to an **age group**, which determines the default rule configurations. Age groups are: | Group | Ages | Screen Time Default | Content Rating | | ---------- | ----- | ------------------- | ------------------------- | | Toddler | 0-2 | 30 min/day | TV-Y only | | Preschool | 3-5 | 60 min/day | TV-Y, G | | Elementary | 6-9 | 90 min/day | TV-Y7, PG, E | | Tween | 10-12 | 120 min/day | TV-PG, PG-13, E10+ | | Teen | 13-15 | 150 min/day | TV-14, PG-13, T | | Older Teen | 16-17 | 180 min/day | TV-14, R (with parent), T | When a child's age group changes (e.g., they turn 13), you can regenerate their policy rules to match the new bracket using `POST /policies/{policyID}/generate-from-age`. ## Related API Endpoints * [Add a child](/developers/api-reference/children/post-families-children) -- Add a child to a family * [Get age ratings](/developers/api-reference/children/get-children-age-ratings) -- Content ratings appropriate for a child's age * [Generate rules from age](/developers/api-reference/policies/post-policies-generate-from-age) -- Auto-generate rules for the child's current age group # Enforcement Source: https://docs.phosra.com/concepts/enforcement How Phosra pushes policies to connected platforms — verification contract, polling, and the consumer vs developer surface **Enforcement** is the process of pushing a child's active policy rules to connected platforms. When you trigger enforcement, Phosra creates an enforcement job that fans out to each connected platform in parallel, translating universal rules into platform-specific configurations. ## Flow 1. **Trigger** — Call `POST /children/{childID}/enforce` (optionally targeting specific platforms) 2. **Job created** — Returns `202 Accepted` with `{"job_id": "...uuid..."}` 3. **Fan-out** — Phosra pushes rules to each connected platform in parallel 4. **Poll for results** — there is **no push callback** on enforcement completion (see below) 5. **Job completes** — overall status reflects the aggregate result ## Verification contract — poll, do not expect a webhook There is **no per-platform push webhook fired on enforcement completion.** Webhooks exist for other events (`policy.updated`, `family.created`, etc.) but not for the completion of a per-platform enforcement result. To verify enforcement, poll: `GET /api/v1/enforcement/jobs/{jobId}/results` The developer surface is identical: `GET /api/v1/developer/enforcement/jobs/{jobId}/results` Both return `[]EnforcementResult`: ```json theme={null} [ { "id": "...", "enforcement_job_id": "...", "platform_id": "nextdns", "status": "completed", "rules_applied": 5, "rules_skipped": 2, "rules_failed": 0, "manual_steps": [], "completed_at": "2026-06-28T12:00:00Z" }, { "id": "...", "enforcement_job_id": "...", "platform_id": "disney_plus", "status": "partial", "rules_applied": 0, "rules_skipped": 0, "rules_failed": 0, "manual_steps": [ "Open Disney+ → Profile → Parental Controls → set maturity level to PG-13", "Enable PIN lock on adult profiles" ], "completed_at": "2026-06-28T12:00:01Z" } ] ``` ### Reading the result | Field | Meaning | | ---------------- | ------------------------------------------------------------------------------------------------ | | `rules_applied` | Rules written to the platform's API (dns, device, api\_write modes only) | | `rules_skipped` | Rules the platform does not support | | `rules_failed` | Rules that errored during the API write | | `manual_steps[]` | Human-readable steps the parent must apply; **non-empty = guided, not programmatically applied** | A non-empty `manual_steps` array means the platform is `manual_attested` — Phosra generated the steps but the parent is the actor, not the API. Do not count these as `rules_applied`. See [Platforms & enforcement modes](/concepts/platforms) for the full breakdown. ## EnforcementJob vs BrowserEnforcementJob `EnforcementJob` (the `GET /enforcement/jobs/{jobId}` response shape) is **deprecated**. Use `BrowserEnforcementJob` for all new enforcement workflows — it carries richer state (`screenshots`, `deployment_model`, `duration_ms`) and is the type returned by all current handler code. Both share the same polling pattern: poll `results` until `status` is `completed`, `partial`, or `failed`. ## Consumer vs developer surface The consumer and developer enforcement routes use the **same handlers** — the path prefix is the only difference: | Operation | Consumer path (WorkOS JWT) | Developer path (API key) | | --------------- | ---------------------------------------------- | -------------------------------------------------------- | | Trigger | `POST /api/v1/children/{id}/enforce` | `POST /api/v1/developer/children/{id}/enforce` | | Get job | `GET /api/v1/enforcement/jobs/{jobId}` | `GET /api/v1/developer/enforcement/jobs/{jobId}` | | **Get results** | `GET /api/v1/enforcement/jobs/{jobId}/results` | `GET /api/v1/developer/enforcement/jobs/{jobId}/results` | | List jobs | `GET /api/v1/children/{id}/enforcement/jobs` | `GET /api/v1/developer/children/{id}/enforcement/jobs` | This split is **deliberate, not drift** — it is an auth-path decision so server integrators (developer keys) and end-user apps (WorkOS JWTs) each reach the same logic through their respective credential type. Response shapes are identical. ## Related API Endpoints * [Trigger enforcement](/developers/api-reference/enforcement/post-children-enforce) — Push rules to all or selected platforms * [Get job status](/developers/api-reference/enforcement/get-enforcement-jobs) — Poll for completion * [Get results](/developers/api-reference/enforcement/get-enforcement-jobs-results) — Per-platform breakdown * [Retry failed job](/developers/api-reference/enforcement/post-enforcement-jobs-retry) — Re-attempt a failed enforcement # Families Source: https://docs.phosra.com/concepts/families How family groups work in Phosra The Core Concepts section describes how **Phosra's Parental Controls product** works — families, children, policies, and enforcement as Phosra has implemented them. This is Phosra's own product layer, distinct from the OCSS standard it conforms to. Developers building on the open OCSS protocol should start at [Developer Platform](/platform/overview). A **family** is the top-level organizational unit in Phosra. Every child, policy, platform connection, and webhook belongs to a family. Families can have multiple adult members, each with a role: **owner** (full control, including billing), **parent** (can manage children and policies), or **guardian** (read access plus limited management). A family must have at least one owner. When you create a family, you are automatically added as the owner. You can then invite other adults and add children. Children do not have user accounts -- they are represented as child profiles within the family. ## Related API Endpoints * [List families](/developers/api-reference/families/get-families) -- Get all families you belong to * [Create a family](/developers/api-reference/families/post-families) -- Create a new family group * [Manage members](/developers/api-reference/families/get-families-members) -- Add or remove parents and guardians * [Family overview report](/developers/api-reference/reports/get-families-reports-overview) -- Dashboard with enforcement health and child status Skip the manual steps. The quick setup endpoint creates a family, adds a child, and generates a full policy in a single call. # Platforms & Enforcement Modes Source: https://docs.phosra.com/concepts/platforms How GET /platforms signals honest enforcement capability — and what manual_attested means for your UI `GET /api/v1/platforms` returns a list of supported platforms. Each entry carries two new fields — `enforcement_mode` and `rule_support` — that tell you **honestly** what Phosra can do on that platform. ## enforcement\_mode | Value | What it means | Correct UI label | | ----------------- | ------------------------------------------------------------------------------------------------ | ------------------------------ | | `dns` | Phosra makes a live write to a DNS-provider API (NextDNS, CleanBrowsing, Control D) | **Enforced (DNS)** | | `device` | On-device enforcement via the Phosra app (Apple, Android device-sync) | **Enforced (requires app)** | | `api_write` | Phosra calls a live third-party write API on the platform | **Enforced** | | `manual_attested` | No programmatic write API — Phosra emits setup steps and records the parent's signed attestation | **Parent-Configured (Guided)** | | `coming_soon` | Adapter not yet built | grayed / not actionable | **Never render "Enforced" for a `manual_attested` platform.** Disney+, Hulu, Peacock, Snapchat, gaming consoles, and other stub platforms all carry `manual_attested`. For these, Phosra generates setup steps and records a signed OCSS receipt once the parent attests — but no programmatic API write occurs. The receipt is authentic; the action is human-completed. ## rule\_support Every platform response includes `rule_support`: a map from each of the **117** closed OCSS v4 rule categories to one of four honest values: | Value | Meaning | | ------------- | ----------------------------------------------------------------------- | | `dns` | DNS provider can filter this category via its API | | `device` | On-device enforcement handles this category | | `guided` | Platform-native category; Phosra guides the parent to apply it manually | | `unsupported` | This category cannot be addressed on this platform | ### Example — Disney+ (manual\_attested) ```json theme={null} { "id": "disney_plus", "name": "Disney+", "enforcement_mode": "manual_attested", "rule_support": { "content_rating": "guided", "time_daily_limit": "unsupported", "web_safesearch": "unsupported", "web_category_block": "unsupported", "age_gate": "guided", "purchase_approval": "guided" } } ``` No rule on Disney+ returns `"dns"` or `"device"`. Only `"guided"` (where native parental controls exist) or `"unsupported"`. ### Example — NextDNS (dns) ```json theme={null} { "id": "nextdns", "name": "NextDNS", "enforcement_mode": "dns", "rule_support": { "web_category_block": "dns", "web_safesearch": "dns", "web_custom_allowlist": "dns", "web_custom_blocklist": "dns", "web_filter_level": "dns", "content_rating": "unsupported", "time_daily_limit": "unsupported" } } ``` Only the five web-filtering categories return `"dns"`; all others are `"unsupported"` because a DNS filter cannot enforce content ratings, screen time, or purchase rules. ## UI contract Render platform enforcement status based on `enforcement_mode`: ``` dns → "Enforced (DNS)" device → "Enforced (requires app)" api_write → "Enforced" manual_attested → "Parent-Configured (Guided)" coming_soon → grayed / disabled ``` Render per-rule status within a platform based on `rule_support[category]`: ``` "dns" → DNS badge "device" → device badge "guided" → guided / parent-configured badge "unsupported" → gray out or omit ``` ## Why manual\_attested platforms still matter For `manual_attested` platforms, Phosra: 1. Translates the policy rules into human-readable setup steps 2. Guides the parent through applying them inside the platform's native parental controls 3. Records a **signed OCSS enforcement receipt** once the parent confirms they applied the steps The `manual_steps[]` array in `EnforcementResult` is populated for these platforms. A non-empty `manual_steps` means the enforcement was guided — the parent is the actor, not the API. The OCSS receipt is authentic and satisfies many regulatory attestation requirements; it is not the same as a programmatic apply. ## Related endpoints * `GET /api/v1/platforms` — full list with `enforcement_mode` + `rule_support` * `GET /api/v1/platforms/{platformId}` — single platform * `GET /api/v1/platforms/by-category?category={dns|streaming|gaming|device|browser}` — filter by type * `GET /api/v1/platforms/by-capability?capability={web_filtering|content_rating|...}` — filter by capability # Policies & Rules Source: https://docs.phosra.com/concepts/policies-and-rules How policies organize rules across 123 OCSS rule categories A **policy** is a named collection of rules assigned to a child. Policies have a status (`draft`, `active`, or `paused`) and a priority number for ordering when multiple policies exist. A **rule** belongs to a policy and controls one of **123 rule categories** — the closed OCSS v4 vocabulary (`internal/domain/models.go`, canonical registry at `registry/ocss-rules.json`). Each rule has: * **category** — which of the 123 categories it controls (e.g., `time_daily_limit`, `content_rating`) * **enabled** — whether this rule is active * **config** — category-specific configuration JSON (e.g., `{"max_ratings":{"mpaa":"PG-13"}}` for content rating) Only **active** policies are enforced. You can have multiple policies per child (e.g., "School Day" and "Weekend") and switch between them using activate/pause. ## Quick setup The fastest path: `POST /api/v1/setup/quick` creates a family, child, and active policy in one call. Pass `child_name`, `birth_date`, and `strictness` (`light`, `recommended`, `strict`). The response includes `max_ratings`, `rule_summary`, and the full `rules[]` array. ```bash theme={null} curl -X POST https://prodapi.phosra.com/api/v1/setup/quick \ -H "Authorization: Bearer $PHOSRA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "child_name": "Emma", "birth_date": "2016-03-15", "strictness": "recommended" }' ``` ## Bulk upsert rules `PUT /api/v1/policies/{policyId}/rules/bulk` creates or updates multiple rules in one call. The developer surface is identical: `PUT /api/v1/developer/policies/{policyId}/rules/bulk`. **Request body:** ```json theme={null} { "rules": [ { "category": "content_rating", "enabled": true, "config": { "max_ratings": { "mpaa": "PG-13", "tv": "TV-Y7", "esrb": "E" } } }, { "category": "time_daily_limit", "enabled": true, "config": { "daily_minutes": 120 } }, { "category": "web_safesearch", "enabled": true, "config": {} } ] } ``` **Response:** `[]PolicyRule` — the full updated rule set for the policy. ### Config shapes for common categories | Category | Config field | Example value | | ---------------------- | ------------------------------------- | --------------------------------------------------------------------- | | `content_rating` | `max_ratings` (object) | `{"mpaa":"PG-13","tv":"TV-Y7","esrb":"E","pegi":"12"}` | | `time_daily_limit` | `daily_minutes` (int) | `{"daily_minutes":120}` | | `time_scheduled_hours` | `schedule` (array of hour ranges) | `{"schedule":[{"days":["mon","tue"],"start":"07:00","end":"21:00"}]}` | | `time_downtime` | `start_hour`, `end_hour` (ints, 0-23) | `{"start_hour":22,"end_hour":7}` | | `web_filter_level` | `level` (string) | `{"level":"strict"}` | | `web_category_block` | `categories` (array of strings) | `{"categories":["adult","gambling"]}` | ## Rule categories The **123** closed OCSS v4 categories span: * **Content** — `content_rating`, `content_block_title`, `violence_threshold`, `firearm_content_block`, `sexual_content_threshold`, and 10 more * **Time** — `time_daily_limit`, `time_scheduled_hours`, `time_per_app_limit`, `time_downtime`, `phone_free_school_hours` * **Purchase** — `purchase_approval`, `purchase_spending_cap`, `purchase_block_iap` * **Social** — `social_contacts`, `social_chat_control`, `social_multiplayer`, `stranger_dm_block`, `stranger_outreach_friction` * **Web** — `web_safesearch`, `web_category_block`, `web_custom_allowlist`, `web_custom_blocklist`, `web_filter_level` * **Privacy** — `privacy_location`, `privacy_profile_visibility`, `privacy_data_sharing`, `data_minimization_enforce`, `third_party_tracker_block`, and more * **Monitoring** — `monitoring_activity`, `monitoring_alerts`, content-monitoring categories * **Algorithmic Safety** — `algo_feed_control`, `addictive_design_control`, `infinite_scroll_block`, `autoplay_disable`, and more * **Notifications** — `notification_curfew`, `usage_timer_notification` * **Advertising / Data** — `targeted_ad_block`, `dm_restriction`, `age_gate`, `data_deletion_request` * **Age Assurance** — `app_store_age_attestation`, `os_age_signal_ingest`, `age_signal_broadcast`, `parental_attestation_cert` * **AI Safety** — `ai_chatbot_tier_gate`, `ai_no_simulated_companionship`, `ai_chatbot_age_assertion`, and 12 more * **Compliance** — `csam_reporting`, `social_media_min_age`, `student_privacy_school_mode`, and more * **Legislation-driven** — `parental_consent_gate`, `parental_event_notification`, `screen_time_report`, `commercial_data_ban`, and more * **Integration surface** — `age_appropriate_profile_mode`, `ncii_takedown`, `adult_site_av_required`, `app_install_block`, `cipa_filter_attestation`, and more See `GET /api/v1/standards` for community packs that pre-configure curated subsets of these categories. ## Related API Endpoints * [Create a policy](/developers/api-reference/policies/post-children-policies) — Create a new policy for a child * [Generate rules from age](/developers/api-reference/policies/post-policies-generate-from-age) — Auto-populate all 123 rule categories * [Bulk upsert rules](/developers/api-reference/rules/put-policies-rules-bulk) — Create or update multiple rules in one call * [Activate a policy](/developers/api-reference/policies/post-policies-activate) — Set a policy as active for enforcement # Strictness Levels Source: https://docs.phosra.com/concepts/strictness-levels How strictness presets affect rule generation When generating rules (via quick setup or `generate-from-age`), you can specify a **strictness** preset that shifts all rule defaults tighter or looser relative to the age-group baseline. | Level | Behavior | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `strict` | Tighter limits across the board. Less screen time, more restrictive content ratings, stronger web filters, social features disabled. Best for families who want maximum protection. | | `recommended` | Balanced defaults based on pediatric and child safety research. This is the default if no strictness is specified. | | `relaxed` | Looser limits that give older children more autonomy. More screen time, broader content access, social features enabled with monitoring. Best for families who want a lighter touch. | Strictness is applied at rule-generation time. After generation, you can customize any individual rule. Changing strictness later requires re-generating rules (which overwrites existing configurations for that policy). ## Example: Screen Time by Strictness (Age 10) | Strictness | Daily Limit | Bedtime | | ------------- | ----------- | -------- | | `strict` | 60 min | 8:00 PM | | `recommended` | 120 min | 9:00 PM | | `relaxed` | 180 min | 10:00 PM | ## Related API Endpoints * [Quick setup](/developers/api-reference/setup/post-setup-quick) -- Pass `strictness` to configure defaults during onboarding * [Generate rules from age](/developers/api-reference/policies/post-policies-generate-from-age) -- Re-generate rules with a new strictness level # Errors Source: https://docs.phosra.com/errors HTTP status codes, error response format, and rate limiting The Phosra API uses standard HTTP status codes and returns structured error responses. ## Error Response Format All errors follow this structure: ```json theme={null} { "error": "error_code", "message": "A human-readable description of what went wrong" } ``` ## HTTP Status Codes ### Success Codes | Code | Meaning | Used For | | ----- | ---------- | ------------------------------------- | | `200` | OK | Successful read or update | | `201` | Created | Resource created successfully | | `202` | Accepted | Async job started (enforcement, sync) | | `204` | No Content | Successful delete | ### Client Error Codes | Code | Meaning | Common Causes | | ----- | -------------------- | -------------------------------------------------------------------- | | `400` | Bad Request | Missing required fields, invalid JSON, malformed parameters | | `401` | Unauthorized | Missing, expired, or invalid token/API key | | `403` | Forbidden | Valid auth but insufficient permissions or scope | | `404` | Not Found | Resource does not exist or is not accessible to your account | | `409` | Conflict | Duplicate resource (e.g., email already registered) | | `422` | Unprocessable Entity | Valid JSON but semantically invalid (e.g., birth date in the future) | | `429` | Too Many Requests | Rate limit exceeded | ### Server Error Codes | Code | Meaning | Action | | ----- | --------------------- | -------------------------------------------------------------- | | `500` | Internal Server Error | Retry with exponential backoff. If persistent, contact support | | `502` | Bad Gateway | Upstream platform unreachable (OAuth exchange, verification) | ## Common Error Examples ### Missing Required Field ```json theme={null} { "error": "validation_error", "message": "child_name is required" } ``` ### Authentication Failure ```json theme={null} { "error": "unauthorized", "message": "Invalid or expired access token" } ``` ### Resource Not Found ```json theme={null} { "error": "not_found", "message": "Child 550e8400-e29b-41d4-a716-446655440000 not found" } ``` ### Rate Limit Exceeded ```json theme={null} { "error": "rate_limit_exceeded", "message": "Rate limit exceeded. Retry after 30 seconds" } ``` ## Rate Limiting API requests are rate limited per API key or access token. Rate limit information is included in every response via headers: | Header | Description | | ----------------------- | ---------------------------------------- | | `X-RateLimit-Limit` | Maximum requests allowed per window | | `X-RateLimit-Remaining` | Requests remaining in the current window | | `X-RateLimit-Reset` | Unix timestamp when the window resets | Your current limits are returned on every response via the `X-RateLimit-*` headers above — read them rather than assuming a fixed quota. ### Handling Rate Limits When you receive a `429` response, read the `X-RateLimit-Reset` header and wait until that time before retrying: ```bash curl theme={null} # Check rate limit headers curl -i https://prodapi.phosra.com/api/v1/families \ -H "Authorization: Bearer $PHOSRA_API_KEY" # Response headers include: # X-RateLimit-Limit: 1000 # X-RateLimit-Remaining: 998 # X-RateLimit-Reset: 1708700000 ``` ```typescript SDK theme={null} try { const families = await phosra.families.list(); } catch (err) { if (err.status === 429) { const resetAt = err.headers['x-ratelimit-reset']; const waitMs = (resetAt * 1000) - Date.now(); await new Promise(resolve => setTimeout(resolve, waitMs)); // Retry the request } } ``` ## Retry Guidance | Scenario | Strategy | | ------------------ | ---------------------------------------------------------- | | `429` Rate Limited | Wait until `X-RateLimit-Reset`, then retry | | `500` Server Error | Exponential backoff: 1s, 2s, 4s, 8s (max 3 retries) | | `502` Bad Gateway | Retry once after 5 seconds; if persistent, contact support | | `401` Unauthorized | Refresh your access token, then retry once | Enforcement jobs (`202` responses) are asynchronous. Poll `GET /enforcement/jobs/{jobID}` to check completion status rather than retrying the trigger. # Billing Model Source: https://docs.phosra.com/integration/billing Phosra charges providers per distinct linked family (the FamilyMeter). Platforms pay nothing. The safety/enforcement path is never metered and never blocked. > This is the canonical billing reference. Read it before going to production. *** ## What is billed Phosra charges the **provider** (not the platform) **per distinct family linked**, measured by the FamilyMeter: * A "family" is the set of children sharing a `family_id` in the census, derived from the unlinkable household hash inside the grant. * One family with 3 children across 5 connected apps = **1 billable family**, not 15. * The metric is: distinct `family_id` values with at least one active grant stamped with your `developer_org`, counted over the billing interval (interval-union — churn and reconnect within the same interval counts as one day, not two). *** ## The attribution hook — your API key on the binding leg **Billing attribution happens at the binding leg of the connect ceremony.** When `runConnectCeremony` → `completeLink` POSTs `POST /api/v1/enforcement-endpoints`, the census handler reads `LinkConfig.developerOrgId` and stamps it on the `phosra_link_grants` row immediately after the endpoint is minted. That stamp is the audit trail for invoicing. ```ts theme={null} const cfg: LinkConfig = { // ... developerOrgId: process.env.PHOSRA_DEV_ORG_ID, // your org_... ID from the control plane } ``` ``` LinkConfig.developerOrgId = "org_01KH..." <- your phosra_live_ key resolves to this set this or the family is unbilled/anonymous ``` **Omit `developerOrgId`** and the grant lands in the **null/anonymous bucket** — no invoice, no attribution, no SLA. This is the correct default for local development and self-hosted deployments where you do not want billing. To provision an org and get a `phosra_live_` key, use the control-plane API or contact [developers@phosra.com](mailto:developers@phosra.com). *** ## Platform pays nothing The platform role is **entirely free**. These operations are never metered and never blocked: | Operation | Cost | | ------------------------------------------------ | -------------------------- | | `GET /api/v1/enforcement-profiles/{endpoint_id}` | Free | | `POST /api/v1/enforcement-confirmations` | Free | | `POST /api/v1/proposals` | Free | | `gk.isAllowed()` | Free — local, zero network | | `gk.refreshProfile()` | Free | > **You charge for the connection, never for keeping a kid safe.** If a provider account is over quota, invoices are issued but enforcement continues uninterrupted. No child is ever protected less because of a billing state. *** ## Co-bound families A family linked by **more than one provider** (e.g. both Guardline and BrightCanary have active grants for the same household) is **held out of auto-invoicing** until one owner is designated. Phosra will not charge both providers for the same family. Watch for a `co_bound_family` notification on your developer dashboard. Resolution is out-of-band; contact [developers@phosra.com](mailto:developers@phosra.com). *** ## Anonymous / self-hosted bucket Grants minted without a `developerOrgId` land in the anonymous bucket: * No invoice * No billing attribution * No support SLA * Full OCSS enforcement — safety is never gated on billing state This is the intended path for: * Local development (`phosra init` wires this by default) * Self-hosted deployments (running your own census via `@openchildsafety/bundle`) * Open-source integrations built on the OCSS standard directly *** ## The §12.3 line (free vs paid) The OCSS specification §12.3 defines the boundary between the open protocol and metered hosted services. Phosra's pricing follows this line exactly: | Side of the line | Who pays | | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | | Local signing and verification (`@openchildsafety/ocss`, SDK crypto) | Free — MIT-licensed | | Trust List fetch and verification | Free — public endpoint | | `isAllowed` enforcement decision | Free — local | | `GET /.well-known/ocss/capabilities` | Free — public endpoint | | Enforcement profile read | Free — safety path | | Enforcement confirmation | Free — safety path | | **Consent/grant ingest, endpoint binding, rule distribution, profile compilation, §8.3.8 confirmation record, proposal routing** | **Metered** — hosted census operations | The rule: **local = free; hosted persistence and distribution = metered**. *** ## Getting a billing account 1. [Apply for production accreditation](/integration/production-accreditation). Billing provisioning is part of the accreditation approval — Phosra will issue a `phosra_live_...` API key when your DID is promoted to `accredited` on the production Trust List. 2. Alternatively, email [developers@phosra.com](mailto:developers@phosra.com) to provision an org account directly if you are already accredited. 3. Derive your `developerOrgId` from the key or retrieve it via `GET /developers/orgs`. 4. Set `developerOrgId` in your `LinkConfig`. 5. Families you link are attributed to your org and appear in usage rollups at `GET /developers/orgs/{orgId}/usage`. *** ## Usage rollups Pull hourly usage counts via the control-plane API: ```ts theme={null} import { getOrgUsage } from "@phosra/sdk-dev" const { data, error } = await getOrgUsage({ path: { orgId: "org_01KH..." }, query: { from: "2026-06-01T00:00:00Z", to: "2026-07-01T00:00:00Z", }, headers: { Authorization: `Bearer ${process.env.PHOSRA_API_KEY}`, }, }) // data.families_linked — distinct active families in the window // data.rules_written — total rule writes // data.grants_active — grants with at least one rule write ``` *** ## Next * [Provider quickstart](/integration/provider) — set `developerOrgId` in `LinkConfig` * [Platform quickstart](/integration/platform) — platforms pay nothing; see what the platform ops cost * [Forward compatibility](/integration/forward-compatibility) — new rule categories arrive without a reinstall # CLI Quickstart Source: https://docs.phosra.com/integration/cli The fastest path to a signed round-trip: phosra init scaffolds a config, phosra doctor verifies census reachability, trust-list root signature, spec-version negotiation, and a full §8.3.2+§8.3.1 sandbox write — all in under two minutes with zero provisioning. `@phosra/cli` is the fastest path to a green setup check. It wires automatically to the **production sandbox** — the stable, partner-facing testing endpoint with 16 seeded trust-list entries, a pre-seeded child policy, and zero new backend required. No config needed out of the box. *** ## Install ```bash theme={null} npm install -g @phosra/cli ``` Verify installation: ```bash theme={null} phosra --version # 0.2.0 ``` *** ## Step 1 — `phosra init` Scaffold a `.phosra.env` in your current directory. Pass `--role provider` for a parental-controls integration or `--role platform` for a content-app integration. ```bash theme={null} phosra init --role provider ``` Output: ``` Phosra — init ✔ Wrote .phosra.env (role: provider) Next: run phosra doctor to verify the setup end-to-end. ``` The written `.phosra.env` is wired to the **production sandbox** — the stable testing endpoint — with the shared `did:ocss:household-acme` parent persona (a deterministic test key, safe for sandbox use). Add `.phosra.env` to your `.gitignore` — it contains a test seed. Config keys written: | Key | Value | Notes | | ----------------------- | ------------------------------------------------------ | --------------------------------------------------------------- | | `PHOSRA_CENSUS_URL` | `https://phosra-api-sandbox-production.up.railway.app` | Production sandbox census — the stable testing endpoint | | `PHOSRA_TRUST_ROOT_X` | `CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU` | Root public key X (base64url) — anchors trust-list verification | | `PHOSRA_HOUSEHOLD_SEED` | `aG91c2Vob2xkLWFjbWUB...` | household-acme Ed25519 seed (deterministic test key) | | `SANDBOX_MIA_POLICY_ID` | `b011c1e5-0000-4000-8000-000000000b01` | Pre-seeded test child policy | | `SANDBOX_MIA_CHILD_ID` | `a11ce0fa-0000-4000-8000-0000000000a1` | Pre-seeded Mia child ref | | `PHOSRA_TEST_KEYS` | `1` | Allows deterministic test-key derivation (sandbox only) | | `PHOSRA_ROLE` | `provider` | Your integration role | `https://phosra-api-sandbox-production.up.railway.app` is the **one canonical sandbox host** — the stable, partner-facing testing endpoint every Phosra doc, example, and SDK default points at ([why one host](/ocss/status)). Phosra runs a separate internal pre-release census for its own integration testing; it is not a partner surface and is deliberately never named in these docs. Point at the canonical host and stay there. To override for the **live accredited network** (real production traffic), replace `PHOSRA_CENSUS_URL`, `PHOSRA_TRUST_ROOT_X`, `PHOSRA_HOUSEHOLD_SEED`, and set `PHOSRA_TEST_KEYS=0`. Two different credentials, two different paths — don't conflate them: * Your **`phosra_` management/API key** is fully **self-serve** — sign up and mint it yourself, including `phosra_live_` keys, with no email to anyone. See [Create your account & get keys](/platform/create-account). * **Live accredited census access** (the real `PHOSRA_CENSUS_URL` + `PHOSRA_TRUST_ROOT_X` for production enforcement traffic, plus a production Trust List entry) is gated on **OCSS accreditation**, an external governance step — [apply here](/integration/production-accreditation). The sandbox above needs neither. *** ## Step 2 — `phosra doctor` Verify the full setup end-to-end. Runs five checks in order, exits `0` if all pass. ```bash theme={null} phosra doctor ``` Sample output (all green): ``` Phosra doctor ✔ census_reachable https://phosra-api-sandbox-production.up.railway.app ✔ trust_list_verified 16 entries, serial 1 ✔ caps_verified ocss_version 1.0.0, 123 rules, serial 4 ✔ version_negotiates agreed 1.0 ✔ sandbox_round_trip consent 201, rule 201, 16 trust-list entries — key verified vs trust-list 5 passed · 0 failed ``` ### What each check does | Check | What it verifies | | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `census_reachable` | `GET /health` → 200 from the configured census URL | | `trust_list_verified` | `GET /.well-known/ocss/trust-list` → parses the signed document and verifies the Ed25519 root signature against `PHOSRA_TRUST_ROOT_X` | | `caps_verified` | `GET /.well-known/ocss/capabilities` → verifies the root signature; warns (non-fatal) if the endpoint is 404 | | `version_negotiates` | Calls `negotiate(CLIENT_VERSIONS, server.supported)` — confirms client and server share a common OCSS spec major version | | `sandbox_round_trip` | Full §8.3.2 + §8.3.1 write cycle: fetches the trust list, extracts the router P-256 payload key, seals a `consent_attestation` envelope, POSTs to `/api/v1/webhooks/inbound/app-store` (→ 201), then POSTs `addictive_pattern_block` to `/api/v1/policies/{mia_policy_id}/rules` (→ 201) | ### Machine-readable output (for CI) ```bash theme={null} phosra doctor --json ``` Returns a JSON object: ```json theme={null} { "ok": true, "checks": [ { "name": "census_reachable", "status": "pass", "message": "https://..." }, { "name": "trust_list_verified", "status": "pass", "message": "16 entries, serial 1" }, { "name": "caps_verified", "status": "pass", "message": "ocss_version 1.0.0, 123 rules, serial 4" }, { "name": "version_negotiates", "status": "pass", "message": "agreed 1.0" }, { "name": "sandbox_round_trip", "status": "pass", "message": "consent 201, rule 201, 16 trust-list entries" } ] } ``` Exits `1` on any `"status": "fail"`. `"warn"` is non-fatal (e.g. caps doc not yet published on the target census). *** ## Step 3 — `phosra caps` Fetch, verify, and pretty-print the capabilities document — the self-describing 123-rule catalog that the census publishes and signs. ```bash theme={null} phosra caps ``` Sample output: ``` Phosra caps ✔ Verified to root (CMHWy3vUAiEc...) OCSS version: 1.0.0 Serial: 4 Not after: 2027-06-01T00:00:00Z Version range: 1.0 Rule categories (123): ANCHORED · addictive_pattern_block · age_appropriate_profile_mode · ai_chatbot_tier_gate · content_rating · ncii_takedown · parental_consent_gate · screen_time_report · ... PROVISIONAL · ... ``` `phosra caps --json` outputs the full structured document for downstream tooling. *** ## Additional commands ### `phosra register` (cold-party self-registration — sandbox) The quickstart above uses the shared `did:ocss:household-acme` test persona. To onboard **your own** identity, `phosra register` generates a fresh Ed25519 DID, self-registers it on the sandbox census as a **provisional** Trust List entry, and proves a green verifiable round-trip — no pre-existing credential, no email. This is the CLI form of the self-register documented in the [Provider quickstart](/integration/provider) (`POST /api/v1/advisors/self-register`). ```bash theme={null} phosra register --slug acme-controls --roles editor # PASS — registered DID: did:ocss:acme-controls # trust tier: provisional # census: https://phosra-api-sandbox-production.up.railway.app # env file: .phosra.env # GREEN — your own DID completed a verifiable round-trip ``` **Sandbox only** — the census rejects self-registration in production; the production Trust List is populated through [OCSS accreditation](/integration/production-accreditation). Writes `PHOSRA_DID` + your seed into `.phosra.env` (add it to `.gitignore`). ### `phosra link write` (provider) Write a signed rule directive via `@phosra/link`. Requires `PHOSRA_DATABASE_URL` for the grant store and an active grant ID from the connect ceremony. ```bash theme={null} phosra link write addictive_pattern_block \ --grant-id \ --child-ref child:a11ce0fa-0000-4000-8000-0000000000a1 \ --decision block ``` ### `phosra gatekeeper check` (platform) Refresh the enforcement profile and run `isAllowed()` for a rule category. Requires `PHOSRA_ENDPOINT_ID`. ```bash theme={null} phosra gatekeeper check addictive_pattern_block \ --endpoint-id \ --confirm applied ``` `--confirm applied|degraded|refused` emits a §8.3.8 enforcement-confirmation receipt. *** ## Config priority Config resolves in this order — higher priority wins: 1. Environment variables (e.g. `PHOSRA_CENSUS_URL=...`) 2. `.phosra.env` file in the current working directory 3. Baked-in production sandbox defaults (the values `phosra init` writes) Override any single value without touching the file — e.g. run `phosra doctor` against an explicit census URL (still the canonical sandbox host; shown here only to demonstrate the override precedence): ```bash theme={null} PHOSRA_CENSUS_URL=https://phosra-api-sandbox-production.up.railway.app \ PHOSRA_TRUST_ROOT_X=CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU phosra doctor ``` *** ## Troubleshooting | Symptom | Cause | Fix | | ---------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- | | `census_reachable: FAIL — network error` | Wrong URL or no network | Check `PHOSRA_CENSUS_URL` in `.phosra.env` | | `trust_list_verified: FAIL — signature verify` | `PHOSRA_TRUST_ROOT_X` doesn't match this census's root key | Get the correct root X from Phosra or use `phosra init` to reset to sandbox defaults | | `sandbox_round_trip: FAIL — 401` | Household seed does not match the trust-list key for `did:ocss:household-acme` | `phosra init --force` to reset the seed, or contact [developers@phosra.com](mailto:developers@phosra.com) | | `caps_verified: WARN — 404` | This census hasn't published its capabilities doc yet | Non-fatal — `phosra doctor` still passes overall; `phosra caps` will fail-exit | *** ## Next * [Provider quickstart](/integration/provider) — `@phosra/link` connect ceremony and rule writes * [Platform quickstart](/integration/platform) — `@phosra/gatekeeper` profile read and local enforcement * [Billing](/integration/billing) — what triggers an invoice # Safe Content Monitoring Source: https://docs.phosra.com/integration/content-monitoring Architect an on-device content classifier the accredited way — benign content deleted, harmful excerpts sealed to the parent, Phosra router-blind throughout. For child-safety app vendors. **The model in one line:** your app classifies a child's content **on the device**, **deletes everything benign**, and hands Phosra **only** a content-free signal or a **sealed** harmful excerpt — so the parent gets actionable safety, the child's ordinary messages are never read by any adult or server, and **Phosra itself never sees content at all.** It is provably more private than screen-scraping, keylogging, or notification capture — and it's built for Sammy's-Law / COPPA parental-control compliance. Phosra is **not** the classifier and **not** a content pipe. Getting the messages (ingestion) is your job — via the platform's Sammy's-Law API or your device agent — **not** through Phosra. Phosra carries the *result*: a classified signal, a sealed excerpt, and a minimization proof. ## Three roles — and the lines between them | Role | Does | Sees plaintext? | | ------------------- | --------------------------------------------------------------- | --------------------------------------- | | **Your app** | Ingests the child's messages, parent UX, sends signals/excerpts | Only transiently, on-device | | **Your classifier** | Decides harmful-vs-benign on the plaintext | Yes — **on-device only**, then zeroizes | | **Phosra (OCSS)** | Router-blind signal + sealed delivery + the minimization proof | **Never.** Forwards ciphertext verbatim | ## How to architect your classifier 1. **Run it where the plaintext already is — on the device.** Never POST raw content to a classification server (yours or anyone's). Content must not egress to be classified. 2. **Classify into the closed harm taxonomy + a severity level.** The taxonomy is a fixed, governed enum (`grooming`, `sexual_exploitation`, `self_harm`, `suicidal_ideation`, `bullying`, `companion_dependency`, `ai_mediated_grooming`, and the rest of the §4.4 set); severity runs `informational → imminent`. **Fail closed** (unknown/unsure ⇒ do not silently pass). Phosra **rejects any signal carrying a class outside the enum** — the structural guarantee against scope-creep from "safety" into general surveillance, so don't invent custom classes. 3. **Benign content (the overwhelming majority) → zeroize immediately.** Never transmit it, never persist it, never show it to an adult. An ordinary message is gone the instant it's judged benign. 4. **Harmful content → a minimal, length-bounded excerpt.** Seal that excerpt to the **recipient's public key** and send it through Phosra. Zeroize everything else. 5. **Phosra never reads it.** You `seal()` with the recipient's key (the consenting parent — or, for self-harm / abuse-disclosed-at-home, an independent advocate); Phosra forwards the sealed bytes verbatim and signs only the *ciphertext* digest. Only the recipient's private key decrypts. 6. **Peers' inbound messages** (the child's friends, who didn't consent) are classified on-device and **never transmitted** — only monitored-child-authored content may be sealed. (Two-party-consent / wiretap hygiene.) ## The data lifecycle ``` child's messages (on device) │ ▼ YOUR CLASSIFIER (on-device, accredited build) ├── benign ─────────────► zeroized instantly. never leaves. never seen. └── harmful ─► minimal excerpt ─► seal(excerpt, recipientPubKey) ─► "JWE:…" │ ▼ PHOSRA (router-blind: forwards JWE verbatim, signs SHA-256(ciphertext), never decrypts) │ ▼ PARENT (or advocate) pulls + open(JWE, theirPrivKey) ─► judges │ └── every epoch: minimization_attestation {N_classified, N_flagged, N_delivered, commitment} ─► Phosra countersigns ─► independent transparency log ``` ## Parental control & transparency **Control is the parent's; secrecy is not allowed.** The parent is the sole controller — consent, revocation, opt-out, and key authority all sit with the parent, and **the child has no opt-out and no revocation key** (matching Sammy's Law and COPPA). But the child's device **must** show a non-suppressable "monitoring is active" indicator the child can see but cannot turn off. A covert, child-undetectable monitor is **stalkerware** — banned by the Apple App Store and Google Play and flagged by the Coalition Against Stalkerware. A hidden design gets your app pulled. *(Minor-assent layers apply only in jurisdictions that require them — EU GDPR Art. 8, UK ICO Children's Code — as a config, off by default in the US.)* ## How Phosra carries the result | Lane | Use it for | Carries content? | | -------------------------- | ------------------------------------------------- | --------------------------------- | | `abuse_signal` | "Harm detected, class X, severity Y, ref Z" | **No** — classification only | | `harm_context` | The sealed harmful excerpt to the parent/advocate | **Yes — sealed**; Phosra is blind | | `minimization_attestation` | The per-epoch proof you minimized benign content | No — counts + a commitment | Build against the published SDKs and the sandbox: ```bash theme={null} npm install @openchildsafety/ocss # seal / open (E2E), verify-to-root npm install @phosra/link # write signed rules / signals npm install @phosra/gatekeeper # platform-side verification # sandbox census: https://phosra-api-sandbox-production.up.railway.app/api/v1 (test keys, seeded family) ``` The `harm_context` round-trip (seal → router-blind forward → recipient decrypts) is live and verifiable on the sandbox today. ## Server-side plane (live) For a fully server-side topology — a **provider enclave** that classifies content on dedicated infrastructure without an on-device component — see [Server-Side Enclave Routing](/integration/server-side-enclave-routing). That page covers: * Enclave registration (`POST /api/v1/enclaves`) — stand-in (`standin`) + Nitro hardware-attest, owner-upsert on redeploy * Single classify (`classify()` — seals JWE direct to enclave; census never carries content) * Fan-out classifyMulti (N enclaves in parallel, separate JWE per enclave, fail-partial) * Minimization attestation — epoch accumulator → `POST /api/v1/minimization-attestation` → `conservation_check: "self_report"` receipt * Compliance bundle (`verifyComplianceBundle` — fully client-side Ed25519 + Merkle verification) All endpoints are **live on the sandbox** today, and the enclave-routing SDKs (`@phosra/provider`, `@phosra/classify` at 0.1.0, plus the conformance harness `@openchildsafety/provider-harness` at 0.1.3) are **published to npm**. ## The minimization proof — what you commit to (and the honest limits) Each **epoch**, your classifier signs `{ classifier_build_hash, N_classified, N_flagged, N_delivered, salted_merkle_root }`; Phosra countersigns it into an **append-only log anchored to an independent transparency authority** (not Phosra itself). Two-attestor reconciliation (your count vs. the platform's ingestion count or your enclave's attestation) makes **silent over-retention *detectable*.** **What this does and doesn't prove — stated plainly:** * ✅ You didn't *quietly keep* benign content off-ledger. * ✅ Only sealed, consent-bound harmful excerpts ever egressed. * ❌ It does **not** prove a specific benign message was bit-wiped (you can't cryptographically prove a negative — so we never call it "verifiable deletion"). * ❌ It does **not** prove your classifier caught *all* harm — that's what accreditation is for. That honesty is the point: it's a *real, checkable* guarantee, which is more than any screen-scraping monitor offers today. ## Accreditation criteria — what we check to approve you To be Trust-Listed as an accredited safety classifier, your integration must demonstrate: 1. **On-device classification** — no raw content egresses to any classification service; `ingestion_method` is a declared, auditable field; screen-scrape / keylog / Accessibility-capture are **not** used. 2. **Closed harm enum, fail-closed** — you classify only into the governed taxonomy; out-of-enum signals are rejected by the census. 3. **Benign zeroized** — benign content is provably not retained or transmitted. 4. **Sealed-to-consent-recipient only** — harmful excerpts seal only to the consent-authorized parent/advocate key (no per-message key diversion). 5. **Parent sole controller + visible indicator** — parental consent/revocation; child cannot opt out; a non-suppressable monitoring indicator is present. 6. **Minimization-attestation lane wired** — your per-epoch attestation reconciles against an independent count and anchors to the transparency log. 7. **Self-harm / abuse-at-home routing** — those signals route to an independent advocate, not blindly to the parent. We run a conformance suite against these (out-of-enum rejection, post-revocation no-event, HMAC-salted leaves, attestation-fail ⇒ lane suspended, advocate/parent delivery indistinguishability). Pass ⇒ accredited and on the production Trust List. ## Phased path — you don't need it all on day one Your classifier runs on-device *inside the app* and self-reports its minimization. We accredit this as **"attested self-report"** — still router-blind, still minimization-accounted; trust rests on your open, inspectable build. The classifier runs in a **remote-attested enclave** (App Attest / Play Integrity / TEE) on an accredited build — now "we only retained harmful content" is *provable to a regulator*, not asserted. ## Why this beats what's out there Today's monitors screen-scrape, keylog, capture notifications, retain everything server-side, give the child no rights, prove nothing, and leak. This architecture: benign-never-read is **enforced**, not promised; the parent controls everything but the child isn't surveilled *covertly*; harmful-only egress is **sealed and Phosra-blind**; and the minimization is **independently checkable**. It's the safe, compliant, auditable way to do what the law is about to require anyway. ## Getting started 1. Pull the SDKs + read the protocol docs across this site. 2. Build the seal → `harm_context` round-trip against the **sandbox** (`https://phosra-api-sandbox-production.up.railway.app/api/v1`). 3. Wire the `minimization_attestation` epoch lane. 4. **Apply for accreditation** — see [Production Accreditation](/integration/production-accreditation) (or email `developers@phosra.com`) with your classifier build details. We review against the criteria above and Trust-List you for production. The published SDKs + the `harm_context` round-trip are live today. The `minimization_attestation` lane and the classifier-accreditation Trust-List role are **specified and on the roadmap** — contact `developers@phosra.com` to be an early accredited classifier partner. # Forward Compatibility Source: https://docs.phosra.com/integration/forward-compatibility New rule categories, new parameters, new accredited parties, and new spec minor versions reach your installed SDK as runtime data — no reinstall required. The wire protocol is the integration contract. The integration contract is the **wire protocol**, not your installed SDK version. Most evolution reaches you as runtime data, so you keep the package you have installed. *** ## What is zero-reinstall today ### New rule categories `rule_category` is an **opaque string** end-to-end — `@phosra/link.directive()` forwards it verbatim, and `@phosra/gatekeeper` evaluates whatever category the census-served profile carries, generically. The census is the sole vocabulary authority; the rule registry is **not** bundled into the SDKs. A new rule is a census-side change — your installed SDK already routes it. ```ts theme={null} // This works today for any category the census introduces — no SDK upgrade const verdict = gk.isAllowed({ category: "age_appropriate_profile_mode" }) // decision: "block" if no cached rule (fail-closed), or the rule's decision if present ``` ### New parameters on an existing rule Parameters are carried in the open `params` JSON object and validated server-side per category. Add a new parameter to an existing rule → census-side deploy, zero client change. ### New accredited parties, keys, and trust tiers New parties propagate through the **runtime-fetched, signed Trust List** — verified to root locally, refreshed at runtime. When a new router, issuer, or verifier joins the network and is added to the Trust List, your SDK picks it up on the next `refreshProfile` poll. No reinstall required to trust a new signer. ### Your own rating vocabularies The platform's `ratingMappings` are **runtime config** passed to `createGatekeeper`: ```ts theme={null} const gk = createGatekeeper({ // ... ratingMappings: [ { ocssCategory: "content_rating", myField: "mpaaRating", vocabulary: "mpaa" }, { ocssCategory: "age_gate", myField: "maturityAge", vocabulary: "age_band" }, // Add new mappings here — no SDK reinstall ], }) ``` *** ## What happens with an unrecognized rule A category your gatekeeper has no interpretation for **fails closed (blocks)**. This is the correct safe default — enforcement never silently passes an unknown rule. To interpret a genuinely new rating semantic (a new `params.scale` value), add a `ratingMappings` entry at runtime. You do not need to upgrade the SDK to handle the new vocabulary. *** ## API evolution rules Phosra holds to ### Additive-only We never remove or repurpose a response field. New behavior arrives as new **optional** fields that your installed SDK simply ignores. Breaking-field changes are not permitted without a major version bump (see below). ### Major-only version negotiation The `OCSS-Spec-Version` negotiation contract: * Negotiation happens on the **major** version only * Higher minor versions are tolerated — a minor spec revision is a server-only deploy * Installed clients and cached profiles are never locked out by a minor bump ``` OCSS-Spec-Version: 1.0 ← client sends; server returns agreed version OCSS-Spec-Version: 1.2 ← server may have updated; client running 1.0 still works ``` `phosra doctor` checks this negotiation (check 4, `version_negotiates`) every time. ### Capability discovery — `/.well-known/ocss/capabilities` The census publishes a root-signed capabilities document at `/.well-known/ocss/capabilities`. Your SDK fetches and verifies it at runtime to discover: * Current OCSS spec version and version range * All 123 rule categories with their tiers (anchored / provisional) and floors * Decision verbs and safety classes * Parameter families and rating crosswalk ordinals ```bash theme={null} # Inspect the live capabilities document phosra caps # Machine-readable phosra caps --json | jq '.rule_categories | length' # 117 ``` The capabilities document is the runtime source of truth for what the census currently enforces — the SDK does not bundle a static copy of the rule vocabulary. *** ## When a reinstall IS needed Only for a genuinely new **client-side local verb** (a new SDK method). Examples: * A new rule category that requires new local decision logic (not just a new opaque string) * A new SDK API method added to `@phosra/link` or `@phosra/gatekeeper` These ship as **opt-in, non-breaking semver-minor releases** and are server-gated — an installed client that does not upgrade continues to work exactly as before. The new surface is additive; existing integrations do not need it to stay green. A **breaking wire change** (a fleet-wide major bump) is reserved for true protocol breaks and should be near-never. `phosra doctor` would surface this as a `version_negotiates: FAIL`. *** ## Hardening items in progress The following items make every minor spec change a zero-reinstall deploy. They are tracked in `docs/superpowers/specs/2026-06-27-zero-reinstall-extensibility.md` (checklist D1–D6): | Item | Status | | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | | Opaque-rule path (`rule_category` string, not enum) | **Live** | | Open `params` JSON | **Live** | | Runtime-fetched Trust List | **Live** | | Runtime rating-mappings config | **Live** | | Spec-version range negotiation (`negotiate()` in `phosra doctor` check 4) | **Live** | | `OCSS-Spec-Version` HTTP header enforcement (server-side rejection of out-of-range clients) | **Preview** — header shape committed; enforcement in progress | | Runtime crosswalk distribution (new `params.scale` values via caps doc) | **Preview** | | `/.well-known/ocss/rules` discovery doc (alias to caps; per-category landing page) | **Preview** | *** ## Next * [CLI quickstart](/integration/cli) — `phosra caps` to inspect the live capabilities document * [OCSS Conformance Status](/ocss/status) — live vs. preview surface-by-surface # Partner Integration Overview Source: https://docs.phosra.com/integration/overview Two integration roles, one hosted census. Parental-controls providers write rules; platforms enforce them. Both SDKs are MIT-licensed; you pay Phosra only for the hosted census. Phosra operates a **hosted OCSS census** — an OCSS Trust-Framework-conformant relay that routes signed child-safety rules between parental-controls providers and the apps where children consume content. There are **two roles**. Every integration is one or both. | Role | Who | SDK | What you do | | ------------ | ------------------------ | -------------------- | ------------------------------------------------------ | | **Provider** | parental-controls vendor | `@phosra/link` | Link families, ingest parent consent, write rules | | **Platform** | content or service app | `@phosra/gatekeeper` | Receive signed rule profiles, enforce locally, confirm | The two SDKs speak the same OCSS Trust Framework protocol under the hood. All cryptographic signing happens **in your process** — your private key never leaves your machine. The hosted census persists, distributes, and confirms; your SDK verifies. *** ## Before you write any code Run the CLI to prove the setup end-to-end against the shared sandbox in under two minutes. No account required. ```bash theme={null} # 1. Install the CLI npm install -g @phosra/cli # 2. Scaffold a config file wired to the staging shared sandbox phosra init --role provider # or --role platform # 3. Verify: census reachable, trust list verified, spec version negotiated, signed round-trip phosra doctor ``` `phosra doctor` runs five checks and exits `0` on green: 1. Census reachable (`GET /health` — mounted at the ROOT, not under `/api/v1`) 2. Trust list verified (Ed25519 root signature against the baked-in root key) 3. Capabilities doc verified (123-rule self-describing document, WARN if absent) 4. Spec-version negotiation (client ↔ server OCSS version range agreement) 5. Signed sandbox round-trip (§8.3.2 consent attestation + §8.3.1 rule write, both 201) The sandbox wired by `phosra init` uses the **staging shared sandbox census** — no new backend, no database, no provisioning. *** ## SDK availability All five SDKs (`@phosra/link`, `@phosra/gatekeeper`, `@phosra/cli`, `@phosra/sdk`, and `@openchildsafety/ocss`) are published on npm. See the [status page](/ocss/status) for full version info. *** ## Pricing — the §12.3 line **The OCSS standard and both SDKs are MIT-licensed.** You can run your own census at zero cost. You pay Phosra only when **Phosra operates the census for you**. * **SDK operations** (signing, verifying, the local `isAllowed` decision) — **free, always** * **Safety-path operations** (enforcement profile reads, enforcement confirmations, proposals) — **free, never metered** * **Hosted census operations** (consent ingest, grant mint, endpoint binding, rule writes, profile distribution) — **metered per distinct linked family** The §12.3 line is the pricing boundary: local crypto and enforcement are free; hosted persistence and distribution are paid. > You pay for the connection, never for keeping a kid safe. For the full billing model, see [Billing](/integration/billing). *** ## What is live today | Surface | Status | | --------------------------------------------------------- | --------------------------------------------------------------------- | | Shared sandbox census (staging + production) | **Live** — zero account required | | `/.well-known/ocss/trust-list` (signed, verified to root) | **Live** | | `/.well-known/ocss/capabilities` (123 rules, signed) | **Live** | | `phosra init` / `phosra doctor` / `phosra caps` | **Live** — on npm | | `@phosra/link` (Provider SDK) | **Live** — on npm | | `@phosra/gatekeeper` (Platform SDK) | **Live** — on npm | | Self-serve Trust List registration (sandbox) | **Live** — `phosra register` or `POST /api/v1/advisors/self-register` | | Production Trust List accreditation | **Vetted gate** — [apply here](/integration/production-accreditation) | | Control-plane API (`prodapi.phosra.com/api/v1`) | **Live** — requires `phosra_` API key | *** ## Next `phosra init` → `phosra doctor` → green signed round-trip in under two minutes `@phosra/link` — connect a family, ingest consent, write rules `@phosra/gatekeeper` — receive profiles, decide locally, confirm enforcement Per-family FamilyMeter — what is metered, what is always free # Platform Quickstart Source: https://docs.phosra.com/integration/platform Content and service apps use @phosra/gatekeeper to receive signed enforcement profiles, make local enforcement decisions with zero network latency, and emit §8.3.8 confirmation receipts. Content and service apps use `@phosra/gatekeeper` to: 1. **Receive a connection** — accept the `endpoint_id_label` from the provider after the connect ceremony 2. **Fetch + verify the enforcement profile** — GET the signed profile from the census, verify the router's Ed25519 signature against the Trust List 3. **Enforce locally** — `isAllowed()` runs entirely in-process; zero network latency 4. **Confirm enforcement** — emit a §8.3.8 receipt to the census **No API key required** — the safety/enforcement path is never pay-gated and is never blocked for billing reasons. **The full loop, end to end — fully self-serve, no email required.** A cold, brand-new self-registered DID can drive the entire connect → profile → enforce loop against the sandbox census with **no roster edit and no live counterparty**. The sandbox census plays the provider for you: `POST /api/v1/sandbox/test-connect` runs the EXT-04 §3.2 ceremony against your own DID + webhook using a seeded test child (*Mia*), and `POST /api/v1/sandbox/consent-attestations` self-mints the consent row a binding needs — both are sandbox-gated and both are live-verified self-serve for any provisional DID (no `OCSS_CONSENT_ATTESTATION_APPS` edit). Every step below is live-verified against `https://phosra-api-sandbox-production.up.railway.app`: 1. **Self-register your DID** — `POST /api/v1/advisors/self-register` (or `phosra register`) → **`200`**. See [Prerequisites](#prerequisites). 2. **Register your webhook once** — [Platform Registration](/integration/platform-registration) → **`201`** returns your `endpoint_id_label` + `connect_secret`. 3. **Implement the gatekeeper** — [Bootstrap](#bootstrap) + [Step 1: `handleConnect`](#step-1-receive-the-connection-handleconnect); expose `POST /api/ocss/connect`. 4. **Trigger the provider side** — `POST /api/v1/sandbox/test-connect` with your `{ platform_did, webhook_url, connect_secret }` → **`200`**, delivering a real binding label to your webhook. See [Test your receiver](#test-your-apiocssconnect-receiver-in-sandbox). 5. **Observe the signed profile** — [Step 2: `refreshProfile`](#step-2-fetch-verify-the-profile-refreshprofile) polls the delivered label → **`200`** and verifies the router signature. 6. **Enforce + confirm** — [Step 3: `isAllowed`](#step-3-make-an-enforcement-decision-isallowed) → [Step 4: `confirm`](#step-4-record-enforcement-verdictconfirm). The **one** Phosra-gated caveat is scoped to the *provider-driven* mint (`POST /api/v1/enforcement-endpoints`), not to this self-serve path — see the [Step 1 provider-mint note](#step-1-receive-the-connection-handleconnect). ### Cold-path status codes — live-verified Each code below is verified against the live sandbox census (`https://phosra-api-sandbox-production.up.railway.app`): | Call | Code | | ----------------------------------------------------------------------------------- | ----- | | `POST /api/v1/advisors/self-register` | `200` | | `POST /api/v1/platforms/{did}/endpoints` — endpoint mint (self-scoped) | `201` | | `POST /api/v1/sandbox/test-connect` | `200` | | `GET /api/v1/enforcement-profiles/{binding_label}` — profile poll (RFC-9421 signed) | `200` | | Any mint/poll for a DID **≠** your caller DID (wrong resolver) | `404` | | Any signed endpoint called **unsigned** (missing RFC-9421 signature) | `401` | **Who mints the endpoint? The provider does — never you.** The connect ceremony has two roles (EXT-04 §3.1), and this is the one thing to get straight before anything else: * **You (the platform)** are a *Gatekeeper-to-be*. Your job: self-register your DID, register your connect webhook **once** (which hands you your `connect_secret`), run `@phosra/gatekeeper` (`handleConnect` → `refreshProfile` → `isAllowed` → `confirm`), expose `POST /api/ocss/connect`, then **wait to receive** a connect. You are the *governed* party. * **The provider** (the parental-controls app, acting for a consenting parent) **mints the per-child binding** via `POST /api/v1/enforcement-endpoints` with `audience_did =` your DID, then delivers your `endpoint_id_label` to your webhook, HMAC-signed with your `connect_secret`. **A platform cannot mint a binding for itself.** A `POST /api/v1/enforcement-endpoints` signed by the consuming platform's own DID is rejected `403 standing_failure` (EXT-04 §5.2 · EXT04-CN-06) — the governed party never mints its own credential. The mint is **consent-first**: it requires an active `consent_attestation` over `(your DID, child)` to exist first. Don't confuse the two `endpoint_id_label`s: your **registration** label (from `POST /api/v1/platforms/{did}/endpoints`) is your connect-leg receiver name — **not** a profile-poll target. The **binding** label a provider delivers to your webhook **is** the poll target. See the [Step 2 warning](#step-2-fetch-verify-the-profile-refreshprofile). *** ## Prerequisites | Item | Source | | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `did:ocss:` on the Trust List | **Sandbox:** self-serve via `phosra register` or `POST /api/v1/advisors/self-register`. **Production:** [apply for accreditation](/integration/production-accreditation). | | Ed25519 signing key pair | A `SenderKey` = `{ seed, keyID }` (32-byte Ed25519 seed + `did:ocss:#` key id). No `makeSenderKey()` helper exists — build the object directly; derive the public half with `@openchildsafety/ocss`'s `ed25519PublicFromSeed(seed)`. **The `` for a self-registered DID is the `YYYY-MM` the census assigned at registration — [read it back](/ocss/onboarding#3-read-back-your-bound-kid), don't invent one.** The `loopline-2026-06` kid in the examples below is loopline's published kid, not a template. | | Census URL | `https://phosra-api-sandbox-production.up.railway.app` (sandbox) | | Trust-root X | `CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU` (sandbox — see note below) | | A registered endpoint | Mint yours once — [Platform Registration](/integration/platform-registration) returns your `endpoint_id_label` + `connect_secret`. | | An `endpoint_id_label` | Delivered by a provider after the connect ceremony (into the webhook you declared at registration) | **New to the platform side?** Start with [Platform Registration](/integration/platform-registration): one signed `POST /api/v1/platforms/{did}/endpoints` call declares your webhook URL and returns your `endpoint_id_label` + `connect_secret` **once**, and documents the six `PHOSRA_*` env vars this quickstart's config maps onto. This page picks up after that. The trust-root X above is a **PUBLIC** key, pinned here out-of-band (that's the point of root verification — don't fetch it from the census you're about to verify). `phosra_live_...` API keys are **billing-attribution only**; the quickstart below runs correctly without one. No Postgres, no API key, no billing setup. Platforms pay nothing. **Encoding: two alphabets.** RFC-9421 request signing uses **standard** base64 (padded, `+/`) for the `Signature` header and the RFC-9530 `Content-Digest` header. Everything else — receipt `sender_signature`, envelope signatures, Trust List JWK `x`/`y`, public keys — is **base64url-RAW** (unpadded, `-_`, RFC 4648 §5). The SDKs handle this for you; hand-rolling RFC-9421 with base64url (or decoding keys with standard base64) produces a silent 401 / corrupted bytes. *** ## Bootstrap ```ts theme={null} import { createGatekeeper } from "@phosra/gatekeeper" import type { GatekeeperConfig } from "@phosra/gatekeeper" const gk = createGatekeeper({ platformDid: "did:ocss:loopline", // the live sandbox connect config platformKeyId: "did:ocss:loopline#loopline-2026-06", // FULL did:ocss:# form — see the key-id note below gatekeeperSigningKey: looplineSenderKey, // Ed25519 SenderKey { seed, keyID } censusBaseUrl: "https://phosra-api-sandbox-production.up.railway.app", trustRootXB64Url: "CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU", // PUBLIC — pinned out-of-band, see note above endpointId: "", // populated after handleConnect ratingMappings: [ { ocssCategory: "content_rating", myField: "mpaaRating", vocabulary: "mpaa", }, ], }) ``` `ratingMappings` declares how your platform's native rating fields map to OCSS categories. This is **runtime config** — add or change mappings without reinstalling the SDK. **`platformKeyId` is the FULL `did:ocss:#` key id — not a bare kid.** The `loopline-2026-06` above is loopline's published kid, so the full id is `did:ocss:loopline#loopline-2026-06`. For a **self-registered** DID the census binds your key under a bare kid equal to the **UTC month** at registration (e.g. `2026-07`), so your full id is `did:ocss:#`. Self-register (`POST /api/v1/advisors/self-register`) now **returns your bound `key_id` (and bare `kid`) directly in its `200` body** — use `key_id` verbatim. You can still confirm it against your published Trust-List entry: [Onboarding → Read back your bound kid](/ocss/onboarding#3-read-back-your-bound-kid). *** ## Step 1 — Receive the connection — `handleConnect` After the provider completes the connect ceremony, it delivers an `endpoint_id_label` server-to-server to a webhook URL you declare. Pass the raw `Request` to `handleConnect`. On success it stores the label and immediately attempts a `refreshProfile`. **Branding is an assessed conformance item — two parts.** (1) **Co-brand your OAuth leg:** the `authorize` page the parent hits during the ceremony MUST co-brand **`Phosra Link · `** — never a bare, unbranded auth form. (2) **Provenance:** once the profile is bound, surface a persistent **"Managed via Phosra"** label on the managed account/profile. Both are checked at accreditation. See the [Phosra Link Branding Requirement](/sdks/branding). **Sandbox: who can mint a binding for your DID.** The connect-ceremony label is minted by a **provider** via `POST /api/v1/enforcement-endpoints` with `audience_did =` your platform DID, and that mint is **consent-first**: an active `consent_attestation` for `(app_ref = audience_did, target_ref = child)` must already exist (§4.2.2). On the sandbox census, `app_ref` is restricted to the fixed **consent-attestation roster** (`OCSS_CONSENT_ATTESTATION_APPS`) — a cold platform DID that is not on the roster, and has no seeded consent row, gets `403 standing_failure` when it tries to be an `audience_did`. The end-to-end walkthrough (`scripts/sandbox/verify-web-engagement-rule.mjs`) runs the full write → mint → `refreshProfile` → `isAllowed` → `confirm` loop as `did:ocss:loopline`, a roster DID with a pre-seeded consent for child *Mia* — use it as the reference for the exact call sequence. ```ts theme={null} // POST /webhooks/ocss-connect (your server — URL declared at onboarding) app.post("/webhooks/ocss-connect", async (req, res) => { const response = await gk.handleConnect(req) res.status(response.status).json(await response.json()) }) ``` `handleConnect` expects a JSON body with `{ endpoint_id_label, state }`. The label is opaque — never log it; treat it like a session credential. ### Verifying the connect-leg delivery — `connectSecret` Phosra signs every connect-leg delivery with an `X-Phosra-Signature` header so your server can confirm the request came from Phosra and was not tampered with. **Always configure `connectSecret` in production.** Pass the `connectSecret` (returned once at [endpoint-mint time](/integration/platform-registration#step-1-mint-your-endpoint-post-api-v1-platforms-did-endpoints)) into `createGatekeeper`: ```ts theme={null} const gk = createGatekeeper({ // ... other config ... connectSecret: process.env.PHOSRA_CONNECT_SECRET, // from endpoint-mint response }) ``` **Algorithm:** `X-Phosra-Signature = lowercase_hex( HMAC-SHA256( connectSecret, rawRequestBody ) )` When `connectSecret` is set, `handleConnect` **rejects** any request with a missing or invalid signature with HTTP 401 (fail-closed). A replayed delivery with a valid signature but a previously-seen `endpoint_id_label` returns HTTP 200 immediately without re-running `refreshProfile` (idempotent). #### What the SDK does internally | Condition | Result | | ------------------------------------------------ | ------------------------------------------------ | | `connectSecret` configured and signature matches | Accept — persist label, attempt `refreshProfile` | | `connectSecret` configured and signature absent | HTTP 401 | | `connectSecret` configured and signature wrong | HTTP 401 | | Same label seen a second time (replay) | HTTP 200 immediately, no second `refreshProfile` | | No `connectSecret` configured | No signature check (trusted-network only) | #### Using `express.raw` to preserve the raw body Phosra computes the HMAC over the **exact request body bytes**. If your framework JSON-parses the body before your route handler runs, the bytes change and the HMAC will not match. Use a raw body parser: ```ts theme={null} // Express — keep the raw buffer so handleConnect can re-read it app.use( "/webhooks/ocss-connect", express.raw({ type: "application/json" }), ) app.post("/webhooks/ocss-connect", async (req, res) => { // Build a Web API Request from Express's raw buffer const webReq = new Request("https://yourapp.com/webhooks/ocss-connect", { method: "POST", headers: { ...req.headers as Record }, body: req.body, // Buffer }) const response = await gk.handleConnect(webReq) res.status(response.status).json(await response.json()) }) ``` `handleConnect` reads the body as raw text for HMAC verification, then parses JSON internally. Do **not** pre-parse the body before calling `handleConnect` — the raw bytes must be intact. ### Test your `/api/ocss/connect` receiver in sandbox You don't need a live provider to exercise the connect leg — the sandbox plays the provider for you. One signed call runs the whole EXT-04 §3.2 ceremony against your own DID and webhook: **`POST /api/v1/sandbox/test-connect`** (SANDBOX only; RFC-9421 signed as your DID). It mints a §9.3 binding for a seeded test child (*Mia* by default) scoped to your `platform_did`, POSTs the signed §3.6 callback to your `webhook_url`, and returns the `endpoint_id_label` + `profile_url`: ```jsonc theme={null} // body — signed with your gatekeeper key (RFC 9421) { "platform_did": "did:ocss:my-gatekeeper", // the DID you self-registered "webhook_url": "https://my-gatekeeper.example.com", // your /api/ocss/connect base "connect_secret": "" } // → 200 { "endpoint_id_label": "…43-char base64url…", "profile_url": "/api/v1/enforcement-profiles/…", "delivered": true, // your receiver returned 2xx "http_status": 200, "state": "sandbox-test-connect:…" } ``` No roster consent, no local scripts, no live counterparty. `delivered:false` + a `note` means the webhook leg failed (bad URL, or `connect_secret` mismatch → your receiver `401`s) — but the binding is still minted and `profile_url` is observable, so you can proceed to [Step 2](#step-2-fetch-verify-the-profile-refreshprofile) and poll the profile regardless. Assert your receiver duties: wrong-key HMAC → `401`; replay of the same `(label, state)` → idempotent `2xx`; no `connectSecret` configured → `503` (fail closed). Prefer to drive the delivery yourself? `deliverLabelToPlatform` from `@phosra/link` POSTs the exact §3.6 wire (`X-Phosra-Signature = hex(HMAC-SHA256(connectSecret, rawBody))`) to your receiver, and `scripts/sandbox/verify-web-engagement-rule.mjs` runs the full write → mint → `refreshProfile` → `isAllowed` → `confirm` loop against the live sandbox census. Both are equivalent to the single `test-connect` call above. **One delivery convention.** `deliverLabelToPlatform(connectUrl, …)` and the census `test-connect` (`webhook_url`) **both append the well-known path `/api/ocss/connect`** to the base you give them — so a self-driven delivery and a census-driven one hit the identical receiver URL. Pass your registered `connect_url` **base** (e.g. `https://you.example.com`), not the full receiver URL. Exported as `CONNECT_RECEIVER_PATH`; for a receiver mounted at a nonstandard path, pass `{ appendReceiverPath: false }` to POST the URL verbatim. **Sandbox consent surface — the census plays the reference OAuth provider.** So the *parent consent* leg is exercisable without standing up a real OAuth provider, the **sandbox census hosts a reference OAuth surface** on the canonical sandbox host: `GET /oauth/authorize` (a seeded consent page for children *Mia / Leo / Ava*; Approve → `302 redirect_uri?code=&state=`, custom mobile schemes like `propagate://` allowed), `POST /oauth/token` (`authorization_code` → `access_token`), and `GET /oauth/profiles` (bearer-scoped child list). It is **stateless** (opaque `sbxauth_` / `sbxtok_` tokens) and **sandbox-only** — gated on the Restricted band, so it returns **404 on dev / staging / production** censuses. Together with `test-connect` (the receiver-side rescue path above), the two let a cold developer drive the whole consent → connect → profile loop against `https://phosra-api-sandbox-production.up.railway.app` with no live counterparty. *** ## Step 2 — Fetch + verify the profile — `refreshProfile` `refreshProfile` GETs the signed enforcement profile from the census, verifies the router's Ed25519 signature against the Trust List in memory, and caches the result. **Fail-closed:** a non-2xx response or a failed signature leaves the cache empty. All subsequent `isAllowed` calls will block (return `decision: "block"` with `failMode: "block"`) until a valid profile is cached. **Poll the connect-ceremony label, signed by the DID it was minted for.** Two different `endpoint_id_label`s exist and only one is a profile-poll target: * Your **platform-registration** label (from `POST /api/v1/platforms/{did}/endpoints`) is the connect-leg receiver credential — it is **not** a profile-poll target and returns `404 {"class":"not_found"}` from `GET /api/v1/enforcement-profiles/…`. * The label delivered to your webhook by a **provider after the connect ceremony** (a `POST /api/v1/enforcement-endpoints` binding minted with `audience_did =` your platform DID) **is** the poll target. The poll is §9.3(b) **resolver-bound**: `refreshProfile` signs the GET with your `gatekeeperSigningKey`, and the census serves the profile only when `caller.DID` equals the binding's `audience_did`. (The request field `audience_did` and the spec/DB column `resolver_did` are the **same value** — the DID the binding resolves to. One concept, two names.) A label minted for a *different* resolver returns the same no-leak `404` as an unknown label — so if `refreshProfile` 404s on a label you were handed, the binding was compiled for a different DID than the one you are signing with. The SDK surfaces this as an empty cache (fail-closed `block`); log `refreshProfile`'s rejection to see the 404. **Debugging the poll: 401 comes before 404.** The two errors mean different things: * **`401 {"class":"signature_invalid"}`** — the request carried **no valid RFC-9421 signature** (missing/duplicate `Signature` header, wrong base64 alphabet, or an unresolvable kid). An **unsigned** `GET /api/v1/enforcement-profiles/{binding_label}` always returns this — the census authenticates *before* it looks anything up, so you never see a 404 until the request is signed. * **`404 {"class":"not_found"}`** — the request **was** validly signed, but the label does not resolve to a binding whose `audience_did` equals your signing DID (unknown label, or a label minted for a different resolver). This is the no-leak 404. So: `401` ⇒ fix your signing (are you signing at all? right kid? standard base64 on the RFC-9421 headers?). `404` ⇒ signing is fine, but you are polling the wrong label or a label minted for a different DID. `@phosra/gatekeeper` signs for you, so through the SDK you should only ever see the `404` case. **Path placeholder vs. what you pass.** The route is written `GET /api/v1/enforcement-profiles/{endpoint_id}`, but the value you pass is the **connect-ceremony binding label** a provider delivered to your webhook — *not* your platform-registration `endpoint_id_label`. Read `{endpoint_id}` as `{binding_label}`; the SDK's `refreshProfile()` fills it from the stored `handleConnect` label automatically. ```ts theme={null} // At startup and on a periodic poll (e.g. every 5 minutes) await gk.refreshProfile() // Or with an explicit endpointId: await gk.refreshProfile(endpointId) ``` Set up polling to keep the profile fresh as rules change: ```ts theme={null} const POLL_INTERVAL_MS = 5 * 60 * 1000 // 5 minutes setInterval(() => { gk.refreshProfile().catch((err) => { // Log but do not crash — the cached profile continues to enforce console.error("refreshProfile failed:", err.message) }) }, POLL_INTERVAL_MS) ``` ### The signed enforcement profile — wire shape `GET /api/v1/enforcement-profiles/{binding_label}` returns a **signed envelope**. This is the one labeled schema — everything a hand-rolled verifier needs: ```jsonc theme={null} { "document": "{\"categories\":[…],\"…\":…}", // ⚠️ a JSON *string*, not an object — JSON.parse it first "key_id": "did:ocss:phosra-router#router-sandbox-2026-06", // the router key that signed `document` "alg": "ed25519", "sig": "base64url-raw" // Ed25519 signature over the exact `document` STRING bytes } ``` **`document` is a JSON string, not a nested object** (verified on the live sandbox — the root-signed Trust List wraps its `document` the same way). Ed25519-verify `sig` over the **raw string bytes** of `document`, *then* `JSON.parse(document)` to read fields. Parsing first and re-serializing changes the bytes and breaks verification. `@phosra/gatekeeper` handles this for you. Inside the parsed `document`, `categories[]` is the enforcement list. Each entry: ```jsonc theme={null} // JSON.parse(document).categories [ { "category": "infinite_scroll_block", "decision": "block", "rule_ref": "810d3d…" }, { "category": "content_rating", "decision": "block", "rule_ref": "a4d807…" } ] ``` | Field | Meaning | | ---------- | ----------------------------------------------------------------------------------- | | `category` | The OCSS rule slug (opaque to the SDK; see [Rule Reference](/ocss/rule-reference)). | | `decision` | `"allow" \| "warn" \| "block"` — the compiled outcome. | | `rule_ref` | Opaque handle you echo back to `verdict.confirm()` (§8.3.8). | ### Resolving the signing key — which `kid`? `@phosra/gatekeeper` verifies the profile signature for you, but if you hand-roll RFC-9421 verification you need to resolve the router's key. **The signed profile names its own key** in `key_id` (the exact `did:ocss:phosra-router#` the census signed with). Resolve it, don't hardcode it: 1. Read `key_id` off the profile — e.g. `did:ocss:phosra-router#router-sandbox-2026-06`. 2. GET the root-signed `/.well-known/ocss/trust-list` and verify it against your pinned trust-root X. 3. In the `phosra-router` entry's `jwks.signing_keys`, select the JWK whose `kid` matches, decode its base64url-raw `x`, and Ed25519-verify `sig` over the `document` bytes. The **current sandbox** census profile-signing kid is `router-sandbox-2026-06`. Matching on the profile's own `key_id` (not a literal) is what survives key rotation. Don't confuse the two `#2026-…` kids. The **enforcement profile** is signed by the router (`did:ocss:phosra-router#router-sandbox-2026-06`). `did:ocss:phosra-reference#2026-07` is the reference **provider's** classify-enclave key — a *different* DID for a *different* document. Always resolve the kid off the document you're actually verifying. *** ## Step 3 — Make an enforcement decision — `isAllowed` `isAllowed` runs **entirely locally** against the cached profile. Zero network calls. ```ts theme={null} const verdict = gk.isAllowed({ category: "addictive_pattern_block", }) if (verdict.decision === "block") { return res.status(403).json({ blocked: true, rule: verdict.ruleSlug }) } ``` For rating-threshold categories, pass your platform's native value: ```ts theme={null} const verdict = gk.isAllowed({ category: "content_rating", signal: { mpaaRating: "PG-13" }, }) // The SDK maps "PG-13" → numeric age via the declared ratingMappings, // then compares against the rule's max_allowed threshold ``` ### Verdict fields | Field | Type | Description | | ---------- | ------------------------------ | -------------------------------------------------------------------------------- | | `decision` | `"allow" \| "warn" \| "block"` | The enforcement outcome | | `ruleRef` | `string \| null` | Opaque string to pass to `confirm()`; `null` when fail-closed (no rule enforced) | | `failMode` | `"block" \| "allow"` | What this category does when the profile is absent or expired | | `ruleSlug` | `string \| null` | Human-readable rule category slug for logging | **Fail-closed on missing profile:** if the cache is empty or expired, `decision` is `"block"` and `ruleRef` is `null`. This is the correct fail-safe — enforcement never silently passes. **One concept, two spellings — `fail_mode` is the wire term.** The census/profile wire and the [data-plane OpenAPI](/api/openapi.data-plane) name this field **`fail_mode`** with values **`"closed"`** and **`"open"`** (`fail_mode: "closed"` on the §9.1-floor rules that must block when the profile is missing). The gatekeeper SDK surfaces the same field as **`failMode`** with the aliased values **`"block"`** (= wire `"closed"`) and **`"allow"`** (= wire `"open"`). They are the same signal: `fail_mode:"closed"` ⇔ `failMode:"block"`, `fail_mode:"open"` ⇔ `failMode:"allow"`. Use the wire term (`fail_mode` / `open` / `closed`) when reading raw profiles or the OpenAPI; the SDK camelCase (`failMode` / `allow` / `block`) is just its JS-idiomatic alias. *** ## Step 4 — Record enforcement — `verdict.confirm` Call `verdict.confirm` after enforcing (or refusing) to record the outcome. This POSTs a self-signed `enforcement_result` to `POST /api/v1/enforcement-confirmations` (§8.3.8). The census verifies and records; it never gates a future `isAllowed` call on this. ```ts theme={null} // Content was blocked by the rule await verdict.confirm("applied") // Parent bypassed a "warn" — user dismissed the warning and continued await verdict.confirm("refused") // Enforcement attempted but degraded (e.g. stale cached decision during census outage) await verdict.confirm("degraded") ``` `confirm` throws `RuleRefRequired` if `verdict.ruleRef` is `null` (fail-closed path — no rule was enforced, so there is nothing to confirm). Do not call confirm on a fail-closed verdict. ### Assurance-level guardrail (§7.3) Some rules require a minimum **assurance floor** on the signal that compiled the enforcement profile. If the floor is not met, `verdict.confirm("applied")` throws `AssuranceLevelError` — you must confirm `"degraded"` instead. | Rule | Required floor | Statute examples | | --------------------------------- | ---------------------------- | --------------------------------- | | `adult_site_av_required` | `document_verified` (rank 3) | UT HB 311, TX HB 1181, SCREEN Act | | `hard_id_verification_escalation` | `document_verified` (rank 3) | UK OSA Part 5 | | `age_appropriate_profile_mode` | `parental_declared` (rank 1) | — | A `parental_declared` signal proves a parent made a statement — it does **not** prove the hard age-verification a statute like UT HB 311 requires. Confirming `"applied"` would be a false attestation. Confirm `"degraded"` to honestly record that enforcement was attempted but the required assurance level was not met. ```ts theme={null} import { AssuranceLevelError } from "@phosra/gatekeeper" try { await verdict.confirm("applied") } catch (e) { if (e instanceof AssuranceLevelError) { // parental_declared signal, but this rule needs document_verified await verdict.confirm("degraded") } } ``` *** ## Report a parent change — `reportParentChange` When a parent adjusts a rating limit inside your app's own settings UI, report it so the connected provider can tighten their policy. This does **not** write a rule — the provider's accept/decline flow owns that. ```ts theme={null} const result = await gk.reportParentChange({ ocssCategory: "content_rating", nativeValue: "PG", // your platform's native string changeScope: "platform_local", // or "family_wide" }) if ("proposalId" in result) { // Census accepted the proposal; provider will see it in their dashboard console.log("proposal submitted:", result.proposalId) } else { // result.suppressed === true — echo suppression: change matches the cached ceiling console.log("change matches current rule — suppressed") } ``` `reportParentChange` POSTs a signed `phosra_platform_proposal` to `/api/v1/proposals`. *** ## Enforcing new rule categories `isAllowed` evaluates **whatever categories the census-served profile carries** — the rule vocabulary is not bundled into the SDK. New categories reach your gatekeeper without a reinstall. ```ts theme={null} // Works without any SDK upgrade — the SDK routes unknown categories fail-closed const verdict = gk.isAllowed({ category: "age_appropriate_profile_mode" }) // decision: "block" if no mapping or no cached rule for this category (fail-closed) ``` **Enforcing a member rule** (e.g. `addictive_pattern_block`): ```ts theme={null} const verdict = gk.isAllowed({ category: "addictive_pattern_block" }) if (verdict.decision === "block") { // Disable the feature or redirect the user blockFeed() } await verdict.confirm("applied") ``` > **Child Mode (`age_appropriate_profile_mode`) is provider-written, not platform-checked.** > When a provider activates OCSS Child Mode, it writes the `age_appropriate_profile_mode` > anchor signal and fans it out to the bundle of member rules (`addictive_pattern_block`, > `content_rating`, `dm_restriction`, etc.) via `convergeFamilyBundle`. Your platform > receives and enforces those individual member rules — it does not call `isAllowed` on > `age_appropriate_profile_mode` directly. **`ncii_takedown`:** Handled like any other category. If a rule is present in the profile, `isAllowed` returns `block`. Your platform's content-removal pipeline triggers on a `block` verdict for this category. *** ## Map rule categories to product behavior `category` is an OCSS slug; **how** you enforce it is your product's call. Below are the five categories most platforms wire first, each with the real slug and a worked `isAllowed` call. The full vocabulary and canonical slugs live in the [Rule Reference](/ocss/rule-reference). **Use the canonical slugs.** `contact_restriction` and `disable_autoplay` are **not** OCSS categories — passing them makes `isAllowed` fail-closed (`block`, `ruleRef: null`) because no rule matches. The real slugs are `social_contacts` / `named_contact_block` (contacts) and `autoplay_disable` (autoplay). | Product behavior | Canonical category | Type | What a `block`/threshold means for your UI | | ----------------------------------------------- | ----------------------------------------- | -------------------- | ---------------------------------------------------------------- | | **Kids profile** (content rating ceiling) | `content_rating` | numeric threshold | Hide or age-gate any title above the compiled age ceiling. | | **AI gate** (limit AI features for minors) | `ai_minor_interaction` | toggle | Disable the AI chat/companion surface for this child. | | **Pagination** (kill infinite scroll) | `infinite_scroll_block` | toggle | Switch the feed to paged mode; stop auto-loading the next page. | | **Contacts** (restrict who can reach the child) | `social_contacts` / `named_contact_block` | handle-list / toggle | Restrict DMs/adds to the allowed handles; block a named account. | | **Autoplay** (no auto-advancing media) | `autoplay_disable` | toggle | Require an explicit tap to play the next video. | ```ts theme={null} // Kids profile — content rating is a threshold category: pass your native value. const rating = gk.isAllowed({ category: "content_rating", signal: { mpaaRating: "R" } }) if (rating.decision === "block") hideTitle() // above the compiled age ceiling // AI gate — toggle category. if (gk.isAllowed({ category: "ai_minor_interaction" }).decision === "block") disableAiChat() // Pagination — kill infinite scroll. if (gk.isAllowed({ category: "infinite_scroll_block" }).decision === "block") usePagedFeed() // Contacts — restrict who can reach the child. if (gk.isAllowed({ category: "social_contacts" }).decision === "block") restrictDmsToAllowlist() // Autoplay — require an explicit tap. if (gk.isAllowed({ category: "autoplay_disable" }).decision === "block") disableAutoplay() ``` `content_rating` is the only one of the five that needs a `ratingMappings` entry (it compares your native rating against a numeric ceiling). The four toggle categories need **no** mapping — a present rule with `decision: "block"` is the whole signal. After enforcing any of them, call `verdict.confirm("applied")`. *** ## Web social app: enforcing an engagement rule end-to-end Engagement rules — `infinite_scroll_block`, `addictive_pattern_block`, and others — are exactly the rules a web social app needs but cannot receive via the iOS `CompiledPolicy` path. The `CompiledPolicy` wire shape (`GET /device/policy`) maps rules onto a fixed set of iOS-native fields (content rating, screen time, web filter, purchases, social). There is no field for engagement categories, so they are silently dropped. **The gatekeeper enforcement-profile path carries any rule category without filtering.** `rule_category` is opaque to the SDK — `isAllowed` evaluates whatever the signed profile contains. ### How an engagement rule arrives 1. A parent (or a provider acting under parental consent) writes the rule: ```ts theme={null} // Provider-side, after the connect ceremony: await providerBff.writeRule({ policyId: mia.policyId, ruleCategory: "infinite_scroll_block", decision: "block", // explicit; registry default is "warn" for audit-tier rules childRef: `child:${mia.childId}`, standingRef: `consent:attestation:${consent.idemKey}`, }) ``` 2. The census compiles the rule into the signed enforcement profile. The compiler reads the decision from the rule's stored config and includes it verbatim in `categories[]`. There is no engagement filter. 3. Your platform's gatekeeper fetches and verifies the profile: ```ts theme={null} await gk.refreshProfile() // profile.categories now contains: // { category: "infinite_scroll_block", decision: "block", rule_ref: "810d3d…" } ``` 4. At request time, enforce in-process — zero network latency: ```ts theme={null} // Example: user's feed request hits your server app.get("/api/feed", async (req, res) => { const verdict = gk.isAllowed({ category: "infinite_scroll_block" }) if (verdict.decision === "block") { // Switch the feed to paginated mode instead of infinite scroll. // The rule_category is what matters; HOW you enforce is your platform's call. return res.json({ mode: "paginated", nextPageToken: "…" }) } return res.json({ mode: "infinite", items: feedItems }) }) ``` 5. Confirm enforcement after the intervention fires: ```ts theme={null} // Called after the paginated response is sent await verdict.confirm("applied") // Posts a signed §8.3.8 enforcement-confirmation receipt to the census. // method_class defaults to "platform_gate" (the web platform applied its UI toggle). ``` ### Why the profile path is the right path for web apps | | iOS `CompiledPolicy` | Gatekeeper enforcement profile | | --------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------ | | Transport | `GET /device/policy` (device API key) | `GET /api/v1/enforcement-profiles/{endpoint_id}` (RFC-9421 signed) | | `infinite_scroll_block` | **DROPPED** — no case in `applyRuleToPolicy` switch | **DELIVERED** — `categories[]` is category-opaque | | `addictive_pattern_block` | **DROPPED** — same switch, no engagement case | **DELIVERED** | | Content rating, screen time, web filter | Mapped onto iOS-native fields | Also delivered (categories\[]) | | Signature verification | No (plain REST) | Yes — Ed25519 router signature verified to Trust List root | | Enforcement decision | In iOS FamilyControls framework | In-process via `isAllowed()` — zero latency | ### Sandbox proof `scripts/sandbox/verify-web-engagement-rule.mjs` runs this end-to-end against the live staging sandbox census and exits 0 (PASS) only if the engagement rule survives all steps: write → endpoint mint → `refreshProfile` → `categories[]` contains the rule → `isAllowed` returns `block` → `confirm("applied")` returns a `201` receipt. The proof also prints the iOS contrast: the same rule is present in the profile but is absent from what `presentCompiledPolicy` would return. *** ## `gk.destroy()` Call `gk.destroy()` to clear the polling timer when the gatekeeper is no longer needed (e.g. in serverless teardown or test cleanup). ```ts theme={null} process.on("SIGTERM", () => { gk.destroy() server.close() }) ``` *** ## Error classes ```ts theme={null} import { RuleRefRequired, AssuranceLevelError, NoRatingMappingError, UnmappedRatingValueError, } from "@phosra/gatekeeper" ``` | Class | When thrown | | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `RuleRefRequired` | `verdict.confirm()` called when `ruleRef` is `null` (fail-closed path) | | `AssuranceLevelError` | `verdict.confirm("applied")` called for a `document_verified`-floor rule (e.g. `adult_site_av_required`) when the profile was compiled from a lower-assurance signal — confirm `"degraded"` instead | | `NoRatingMappingError` | `reportParentChange` called for a category with no declared `ratingMappings` entry | | `UnmappedRatingValueError` | `nativeValue` has no crosswalk entry in the declared vocabulary | *** ## Local vs hosted operations | Operation | Runs in SDK (local) | Hits hosted census | | --------------------------------------------- | ----------------------------------- | ------------------------------------------------ | | Ed25519 signing | yes | no | | Trust List key lookups | yes (in-memory cache) | no | | `isAllowed` enforcement decision | yes | no | | Profile signature verification | yes (against cached Trust List key) | no | | Profile fetch (`refreshProfile`) | no | `GET /api/v1/enforcement-profiles/{endpoint_id}` | | Enforcement confirmation (`confirm`) | no | `POST /api/v1/enforcement-confirmations` | | Parent-change proposal (`reportParentChange`) | no | `POST /api/v1/proposals` | *** ## Next * [Provider quickstart](/integration/provider) — the provider side: link families and write rules * [Billing](/integration/billing) — platforms pay nothing; see why * [Forward compatibility](/integration/forward-compatibility) — how new rules reach you without a reinstall # Platform Registration Source: https://docs.phosra.com/integration/platform-registration Mint your platform endpoint, receive the connect-secret once, declare your webhook URL, and wire the six PHOSRA_* env vars into @phosra/gatekeeper. This is the one-time step that opens the connect channel a provider uses to reach you. Before a provider can complete the connect ceremony and start delivering `endpoint_id_label`s to your app, your platform registers **one endpoint** with the census. Registration: 1. **declares your webhook URL** — the base URL your `POST /api/ocss/connect` receiver lives under, and 2. **returns two secrets exactly once** — the `endpoint_id_label` (your §9.3(a) registration name) and the `connect_secret` (the HMAC key that authenticates inbound connect-leg deliveries). This is the OCSS Trust Framework §3.5 out-of-band establishment step made a real operation — the public endpoint below is its normative surface; the `@phosra/gatekeeper` SDK wraps it end to end. **One endpoint, self-scoped.** You register **your own** platform DID's endpoint — the signed request's caller DID must equal the `{did}` in the path. A mismatch answers the same `404` an unknown platform answers (no existence leak). Re-minting the same `(did, connect_url)` pair **rotates both credentials** — that is the EXT-04 §3.5 re-issue path. *** ## Prerequisites | Item | Source | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `did:ocss:` on the Trust List | **Sandbox:** self-serve — [`POST /api/v1/advisors/self-register`](/ocss/onboarding#get-a-sandbox-did-self-serve-no-email-required) or `phosra register`. **Production:** [apply for accreditation](/integration/production-accreditation). | | Ed25519 signing key pair | A `SenderKey` is `{ seed, keyID }`; the same key your DID publishes on its Trust-List entry. Your `keyID` is `did:ocss:#` — see the key-id note below. | | Canonical sandbox census URL | `https://phosra-api-sandbox-production.up.railway.app` — the one canonical partner sandbox host (see [Conformance status](/ocss/status)). | **Your `key_id` format depends on how your DID got onto the Trust List.** Every signing example below uses `did:ocss:snaptr#snaptr-2026-06` — a **bespoke** kid, valid only because seeded roster DIDs (`snaptr`, `loopline`) publish exactly that kid. A **self-registered** sandbox DID is different: the census binds your key under a bare kid equal to the **current UTC month** at registration (e.g. `2026-07`), so **your** `keyID` is `did:ocss:#`. Signing with any other kid fails `401` (kid-not-in-entry). Read your bound kid back from your entry — see [Onboarding → Read back your bound kid](/ocss/onboarding#3-read-back-your-bound-kid) — and use it verbatim everywhere `PHOSRA_PLATFORM_KEY_ID` / `keyID` appears. *** ## Step 1 — Mint your endpoint — `POST /api/v1/platforms/{did}/endpoints` The request is **RFC-9421 signed** with your DID's Ed25519 key (the census verifies the signature against your live Trust-List entry — provisional-tier self-registrations included). There is no `phosra_` API key on this path; the census identifies you by signature, not by a bearer token. **Request body:** | Field | Type | Required | Meaning | | -------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `connect_url` | string | yes | Absolute base URL your `POST /api/ocss/connect` receiver lives under. **HTTPS required outside sandbox** (sandbox admits plain `http` for private-network sim receivers). No query, fragment, or embedded credentials; a trailing `/` is trimmed. | | `capabilities` | string\[] | no | Declared capability slugs (informative; the signed gatekeeper capabilities document remains the enforced artifact). Lowercase slugs `^[a-z][a-z0-9_.:-]{0,63}$`, max 128, de-duplicated. | Because the request must be signed, mint through the SDK rather than raw `curl`. Using `signRequest` from `@openchildsafety/ocss`: ```ts theme={null} import { signRequest } from "@openchildsafety/ocss" const CENSUS = "https://phosra-api-sandbox-production.up.railway.app" const did = "did:ocss:snaptr" const key = mySenderKey // { seed: Uint8Array(32), keyID: "did:ocss:snaptr#snaptr-2026-06" } const bodyText = JSON.stringify({ connect_url: "https://snaptr.example.com/webhooks/ocss-connect", // your receiver base capabilities: ["content_rating", "addictive_pattern_block"], }) const targetURI = `${CENSUS}/api/v1/platforms/${encodeURIComponent(did)}/endpoints` // RFC-9421: signRequest signs the request line + Content-Digest with STANDARD base64 (padded, +/). const headers = signRequest({ method: "POST", targetURI, body: new TextEncoder().encode(bodyText), keyID: key.keyID, seed: key.seed, created: Math.floor(Date.now() / 1000), }) headers["Content-Type"] = "application/json" const res = await fetch(targetURI, { method: "POST", headers, body: bodyText }) const mint = await res.json() ``` **`201` response — the two secrets are here and nowhere else** (field order + value format live-verified against the sandbox census): ```json theme={null} { "capabilities": ["content_rating", "addictive_pattern_block"], "connect_secret": "EXAMPLExconnectxsecretxnotxaxrealxvaluexv00", "connect_url": "https://snaptr.example.com/webhooks/ocss-connect", "endpoint_id": "3f0e2c54-1222-497a-bc20-f25403c45a2b", "endpoint_id_label": "EXAMPLExendpointxlabelxnotxaxrealxsecretxv0", "rotated_at": "2026-07-03T18:22:04Z" } ``` `endpoint_id_label` and `connect_secret` are each an **unprefixed 43-character base64url string** (a raw 32-byte secret, `-_` alphabet, no `eplbl_`/`cs_` prefix) — the two example values above are clearly-fake placeholders in that exact format. **`endpoint_id_label` and `connect_secret` are returned only once.** Per §9.3, the census stores only their SHA-256 digests (migration `191_ocss_platform_endpoints`) — the cleartexts live in this `201` body and nowhere else. They are **never** logged, receipted, or exported. Persist both immediately (a secret manager, not source). Lose them and you re-mint, which **rotates both** and invalidates the old pair. ### What each returned value is for | Value | Used for | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `endpoint_id` | The stable UUID of your registration (safe to log). | | `endpoint_id_label` | Your §9.3(a) registration **name** — an opaque credential a provider references when it completes the connect ceremony to you. Treat like a session credential; never log it. Feeds `PHOSRA_ENDPOINT_ID` / `createGatekeeper({ endpointId })`. | | `connect_secret` | The HMAC key that authenticates inbound connect-leg deliveries (Step 3). Feeds `PHOSRA_CONNECT_SECRET` / `createGatekeeper({ connectSecret })`. | | `connect_url` | Echo of your declared webhook base (normalized). | *** ## Step 2 — Declare your webhook receiver The `connect_url` you registered is the base your `POST /api/ocss/connect` receiver lives under. When a provider completes the connect ceremony, Phosra delivers the `endpoint_id_label` **server-to-server** to that URL. Stand up the receiver and hand it to `@phosra/gatekeeper`'s `handleConnect` — see [Platform Quickstart → Step 1](/integration/platform#step-1-receive-the-connection-handleconnect): ```ts theme={null} // POST /webhooks/ocss-connect (must match the connect_url you minted, sans the appended path) app.post("/webhooks/ocss-connect", express.raw({ type: "application/json" }), async (req, res) => { const webReq = new Request("https://snaptr.example.com/webhooks/ocss-connect", { method: "POST", headers: { ...(req.headers as Record) }, body: req.body, // raw Buffer — do NOT pre-parse; the HMAC is over exact bytes }) const response = await gk.handleConnect(webReq) res.status(response.status).json(await response.json()) }) ``` The sender appends `/api/ocss/connect` to your registered base, so register the **base** (e.g. `https://snaptr.example.com/webhooks/ocss-connect`) — the SDK and the census sender agree on this path convention. *** ## Step 3 — Verify inbound deliveries with the connect-secret Phosra signs every connect-leg delivery with an `X-Phosra-Signature` header so your receiver can prove the request came from Phosra and was not tampered with. **Algorithm:** `X-Phosra-Signature = lowercase_hex( HMAC-SHA256( connect_secret, rawRequestBody ) )`. `@phosra/gatekeeper`'s `handleConnect` implements the verify side for you. Pass the `connect_secret` from Step 1 into `createGatekeeper` and the SDK verifies it for you, **fail-closed**: ```ts theme={null} const gk = createGatekeeper({ // ...bases + signing key... connectSecret: process.env.PHOSRA_CONNECT_SECRET, // from the endpoint-mint 201 }) ``` | Condition | `handleConnect` result | | ---------------------------------------------- | ----------------------------------------------------------------- | | `connectSecret` set, signature matches | Accept — persist label, attempt `refreshProfile` | | `connectSecret` set, signature absent or wrong | **HTTP 401** (fail-closed) | | Same `endpoint_id_label` seen again (replay) | **HTTP 200** immediately, no second `refreshProfile` (idempotent) | | No `connectSecret` configured | No signature check — **trusted-network only** | **Always configure `connectSecret` in production.** Without it, `handleConnect` accepts any POST to your webhook. The `connect_secret` is the only thing that binds an inbound delivery to Phosra. *** ## The `@phosra/gatekeeper` env contract — the six `PHOSRA_*` vars A gatekeeper deployment is fully parametrized by **six** `PHOSRA_*` environment variables plus one secret signing seed. Everything `createGatekeeper` needs maps 1:1 onto them: | Env var | `createGatekeeper` field | Value / source | | ------------------------ | ------------------------ | --------------------------------------------------------------------------------------------------------- | | `PHOSRA_PLATFORM_DID` | `platformDid` | `did:ocss:` — your Trust-List DID | | `PHOSRA_PLATFORM_KEY_ID` | `platformKeyId` | Your signing key id, e.g. `did:ocss:snaptr#snaptr-2026-06` (the `keyID` your DID publishes) | | `PHOSRA_CENSUS_URL` | `censusBaseUrl` | `https://phosra-api-sandbox-production.up.railway.app` (canonical sandbox host) | | `PHOSRA_TRUST_ROOT_X` | `trustRootXB64Url` | `CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU` — root pubkey X (base64url, **public**, pinned out-of-band) | | `PHOSRA_ENDPOINT_ID` | `endpointId` | The `endpoint_id_label` from your endpoint-mint `201` (populated after the connect ceremony binds you) | | `PHOSRA_CONNECT_SECRET` | `connectSecret` | The `connect_secret` from your endpoint-mint `201` | Plus the **signing seed** — the one genuinely sensitive value, kept out of the six because it is a private key, not configuration: | Secret | Builds | Value / source | | --------------------- | --------------------------- | ---------------------------------------------------------------------------------------- | | `PHOSRA_SIGNING_SEED` | `gatekeeperSigningKey.seed` | base64url of your Ed25519 **32-byte** seed. Load from a secret manager; never commit it. | ```bash theme={null} # .env — the six PHOSRA_* config vars (safe to template; the SEED is a secret) PHOSRA_PLATFORM_DID=did:ocss:snaptr PHOSRA_PLATFORM_KEY_ID=did:ocss:snaptr#snaptr-2026-06 PHOSRA_CENSUS_URL=https://phosra-api-sandbox-production.up.railway.app PHOSRA_TRUST_ROOT_X=CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU PHOSRA_ENDPOINT_ID=EXAMPLExendpointxlabelxnotxaxrealxsecretxv0 # endpoint_id_label from the mint 201 (unprefixed 43-char base64url) PHOSRA_CONNECT_SECRET=EXAMPLExconnectxsecretxnotxaxrealxvaluexv00 # connect_secret from the mint 201 (unprefixed 43-char base64url) # secret — from a vault, not this file: # PHOSRA_SIGNING_SEED= ``` ```ts theme={null} import { createGatekeeper } from "@phosra/gatekeeper" import { b64urlRawDecode } from "@openchildsafety/ocss" const gk = createGatekeeper({ platformDid: process.env.PHOSRA_PLATFORM_DID!, platformKeyId: process.env.PHOSRA_PLATFORM_KEY_ID!, censusBaseUrl: process.env.PHOSRA_CENSUS_URL!, trustRootXB64Url: process.env.PHOSRA_TRUST_ROOT_X!, endpointId: process.env.PHOSRA_ENDPOINT_ID!, connectSecret: process.env.PHOSRA_CONNECT_SECRET!, gatekeeperSigningKey: { seed: b64urlRawDecode(process.env.PHOSRA_SIGNING_SEED!), // Uint8Array(32) keyID: process.env.PHOSRA_PLATFORM_KEY_ID!, }, ratingMappings: [{ ocssCategory: "content_rating", myField: "mpaaRating", vocabulary: "mpaa" }], }) ``` `PHOSRA_TRUST_ROOT_X` is a **public** key pinned out-of-band on purpose — that is what root verification means: you do not fetch the root from the census you are about to verify. The staging census is a Phosra-internal pre-release instance with a different root and is **not** a partner surface — always use the canonical production sandbox host above. *** ## Conformance The signed endpoint-mint (Step 1) needs your DID key, so it is exercised by the SDK, not raw `curl`. What a docs-only stranger — or the nightly docs-conformance CI — **can** run with no credentials is the reachability + trust-anchor check the whole registration depends on: the canonical census serves a root-signed Trust List whose `root` matches the `PHOSRA_TRUST_ROOT_X` pinned above. ```bash theme={null} # 1. The canonical sandbox census is reachable and healthy. curl -fsS https://phosra-api-sandbox-production.up.railway.app/health # → 200 {"status":"ok"} # 2. It serves a root-signed Trust List whose root key_id is the sandbox root. curl -fsS https://phosra-api-sandbox-production.up.railway.app/.well-known/ocss/trust-list \ | grep -o '"key_id":"root-sandbox-2026-06"' # → "key_id":"root-sandbox-2026-06" (non-empty ⇒ PASS) ``` A signed endpoint-mint round-trip (mint → receive `endpoint_id_label` at your webhook → `handleConnect` 200 → `refreshProfile` → `isAllowed`) is exercised end-to-end by `scripts/sandbox/verify-web-engagement-rule.mjs`, which exits `0` only if the full chain survives against the live sandbox census. *** ## Next * [Platform Quickstart](/integration/platform) — receive the connection, fetch/verify the profile, enforce locally, confirm * [Onboarding](/ocss/onboarding) — get your sandbox `did:ocss:` (self-serve, no email) * [Conformance status](/ocss/status) — the one canonical sandbox host and every live/preview surface # Production Accreditation Source: https://docs.phosra.com/integration/production-accreditation How to move from a sandbox provisional DID to a production-accredited entry on the OCSS Trust List. Sandbox is self-serve today; production is a vetted gate. The OCSS Trust List has two accreditation tiers, with a distinct intermediate state that is the achievable goal today. | Tier | Track | How | Live today? | | --------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | | **Provisional** | Sandbox only | `POST /api/v1/advisors/self-register` — self-serve, no email | Yes | | **Self-tested** (`standin`) | Sandbox only | Run the conformance harness (4/7 pass today), file the `conformance_attestation` → entry gets `role: classifier-accredited`, `accreditation_tier: standin` | Yes — achievable now | | **Accredited** (full) | Production | Harness all 7 passing + `conformance_attestation` signed by a real governance-designated **verifying-agency** → compiler admits to production Trust List | No — gated, see below | **Sandbox is live and self-serve right now.** You do not need this page to start integrating. Run `phosra register` or `POST /api/v1/advisors/self-register` on the sandbox census and you have a signed round-trip in minutes. Production accreditation is a separate, later step — only relevant when you are ready to go live with real users. **No production verifying-agency exists yet.** Full accreditation requires a named independent organization holding `role: verifying-agency` on the Trust List — Phosra structurally cannot self-accredit (WebPKI-CA invariant: the accreditor must be a trusted third party, not Phosra). A production accreditor is pending governance designation. Until one is named, the achievable tier is **self-tested** (`accreditation_tier: standin`). The Trust List compiler automatically excludes `standin` entries from production Trust Lists (`PHOSRA_ENV=production`); they appear only on sandbox Trust Lists. **Sandbox: the `verifying-agency` role IS self-serve and observable.** The §5.4 open-assessor role can be self-registered on a sandbox census — the field is **`entry_role`** (NOT `roles`): ```bash theme={null} curl -X POST https://phosra-api-sandbox-production.up.railway.app/api/v1/advisors/self-register \ -H "Content-Type: application/json" \ -d '{"did":"did:ocss:your-assessor","public_key_b64url":"","entry_role":"verifying-agency"}' ``` The response echoes `"entry_role":"verifying-agency"`, and the next compiled Trust List carries `"role":"verifying-agency"` on that entry (verify with the `jq` command in step 5). `roles: [...]` is a separate, informational field and does **not** set the Trust-List role — use `entry_role`. `self-register` is sandbox-only (`SANDBOX_MODE=true`); production VA designation stays governance-gated. *** ## Why the gate exists The production Trust List is the thing every platform trusts when it verifies a signed enforcement profile. A bad entry on it could misdirect child-safety decisions for every connected child. The gate is not bureaucracy; it is the invariant that keeps the network honest. The criteria below are Phosra's minimum bar for production accreditation. *** ## Eligibility criteria To be considered for production accreditation, an applicant must meet **all** of the following: 1. **Real legal entity** — a registered company or incorporated organisation (not an individual developer account). 2. **Passing conformance harness** — run `@openchildsafety/provider-harness` (≥ 0.1.3) against your enclave and obtain a `conformance_attestation` with all evaluated assertions passing. Today's first attestation scopes to the 4 currently-passable assertions (A1, A2, A5, A7). The remaining 3 (A3 consent infra, A4 capability endpoint, A6 advocate lane) are PENDING — not failures; the infrastructure they require does not yet exist. See [Server-Side Enclave Routing — Conformance Harness](/integration/server-side-enclave-routing#conformance-harness) for the exact invocation and assertion descriptions. 3. **§5.4 self-declaration** — a signed conformance self-declaration filed per the Implementer lane (see [OCSS Onboarding](/ocss/onboarding)). For parties intending the Gated or Restricted bands, the Assessed or Audited lane artifact is also required. 4. **Child-safety–aligned use case** — intended use that aligns with the OCSS mission (protecting children, not general-purpose signal aggregation). 5. **For full production accreditation (not achievable today):** the `conformance_attestation` must be counter-signed by a **real governance-designated verifying-agency** — an independent organization holding `role: verifying-agency` on the same Trust List. The census compiler enforces this at compile time: `classifier-accredited` entries whose attestation is not signed by a co-present `verifying-agency` are excluded from the signed document. A production accreditor is pending designation. *** ## Review SLA Phosra commits to the following review timeline: * **Initial acknowledgement:** within 2 business days of submission * **Promote / decline decision:** within 10 business days of submission * **If more information is needed:** Phosra will email the contact address from the application; the clock pauses until a response is received. *** ## Application steps ### 1. Complete the sandbox round-trip Verify your enclave is correctly wired before filing. Run the direct-loop demo against the sandbox census (see [Server-Side Enclave Routing](/integration/server-side-enclave-routing) for the full quickstart): ```bash theme={null} CENSUS_URL=https://phosra-api-sandbox-production.up.railway.app \ ROOT_KEY_X_B64URL=CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU \ node scripts/sandbox/verify-enclave-direct-loop.mjs ``` ### 2. Run the conformance harness Generate a probe-lab signing key, then run the harness's `run` → `attest` → `sign` pipeline (the CLI is `provider-harness ` — there is no `--sign-attestation` flag): ```bash theme={null} # Generate the probe-lab Ed25519 key (keep secret — only you run this) openssl genpkey -algorithm ed25519 -out probe-lab-key.pem # 2a. Run the conformance suite against your live enclave base URL # (the harness calls GET {url}/buildinfo and POST {url}/classify). # Target by URL with --enclave-url (NOT --enclave); requires >= 0.1.3. npx -p @openchildsafety/provider-harness@0.1.3 provider-harness \ run --enclave-url https://your-enclave.example # 2b. Build the unsigned conformance attestation npx -p @openchildsafety/provider-harness@0.1.3 provider-harness \ attest --enclave-url https://your-enclave.example \ --attested-by did:ocss:probe-lab \ --passed-at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ --liability-scope-ref https://ocss.example/liability#v0 \ > attestation.json # 2c. Sign it with the probe-lab key npx -p @openchildsafety/provider-harness@0.1.3 provider-harness \ sign --key probe-lab-key.pem --key-id probe-lab-2026-06 attestation.json \ > signed-attestation.json ``` Expected output for a fully-wired enclave today (4 pass / 3 pending / 0 fail). Your live-enclave run grades **your** implementation — an assertion your enclave does not yet satisfy shows `fail`, not `pass` (e.g. a5 fails if your minimization receipt's `key_id` does not resolve to a signing key the harness can verify). Reconcile any `fail` before filing: ``` [✓ pass ] a1: closed-enum fail-closed [✓ pass ] a2: content-free signal lane [~ pending] a3: sealed-to-consent-recipient only [~ pending] a4: parent-sole-control + non-suppressable monitoring_active [✓ pass ] a5: minimization attestation wired (HMAC-salted leaves, self-report) [~ pending] a6: abuse-at-home → advocate routing [✓ pass ] a7: attestation-fail → suspend (fail-closed before content) 4 pass · 3 pending · 0 fail self-tested; production accreditor pending ``` Exit code 0 on `run` means all evaluated assertions passed (pending ≠ fail). The `sign` step prints the signed `conformance_attestation` JSON (`signed-attestation.json`) — copy it for step 3. The full step-by-step walkthrough (register your enclave, interpret the output, understand the honest tier labels) is public at [Server-Side Enclave Routing — Conformance Harness](/integration/server-side-enclave-routing#conformance-harness). ### 3. Apply via the API Apply via the API (or email `developers@phosra.com` with the same fields): ```bash theme={null} curl -X POST https://openchildsafety.com/api/accreditation-apply \ -A "Mozilla/5.0" \ -H "Content-Type: application/json" \ -d '{ "name": "Jane Smith", "email": "jane@yourorg.com", "org": "Your Organization", "provisional_did": "did:ocss:your-slug", "intended_use": "We provide parental-controls software to families and want to write signed rules to the production census.", "conformance_attestation": { "...": "paste the attestation JSON from step 2" } }' ``` Response: `{ "ok": true }` — a row is stored in the intake queue and Phosra is notified. `openchildsafety.com` is behind Cloudflare's browser-integrity check, which rejects requests from known automation-library User-Agents (e.g. `Python-urllib`) with **`error code: 1010`** / HTTP 403 — the WAF blocks before the app is reached. Send an explicit browser User-Agent (the `-A "Mozilla/5.0"` above) to pass. `curl`'s default UA works today, but `urllib`/scripted clients must set one. **Required fields:** | Field | Description | | ------------------------- | ------------------------------------------------------------------------ | | `name` | Contact person's full name | | `email` | Contact email — Phosra will reply here | | `org` | Legal entity name | | `provisional_did` | Your `did:ocss:` as registered on the sandbox Trust List | | `intended_use` | Brief description of your integration and the role(s) you intend to hold | | `conformance_attestation` | The signed attestation JSON output by the harness in step 2 | ### 4. Phosra reviews and adds the Trust List entries Phosra reviews your harness output, §5.4 declaration (if applicable), and intended use. On approval, Phosra's administrator adds two rows to `advisor_agents`: 1. **`did:ocss:probe-lab`** — `role: verifying-agency`, `tier: accredited`, public key = the probe-lab Ed25519 pubkey derived from the key you submitted. (One entry, shared across all sandbox applicants using the same probe-lab key.) 2. **`did:ocss:`** — `role: classifier-accredited`, `accreditation_tier: standin`, `tier: accredited`, public key = your enclave's Ed25519 signing key, `conformance_attestation` = the JSON from step 2. ### 5. The Trust List compiler admits your entry At the next compile cycle, `enforceAccreditationInvariants` in `internal/ocss/trustlist/compile.go` runs two checks: * **Invariant (a) — WebPKI-CA admission gate:** `classifier-accredited` entries are admitted ONLY if their `conformance_attestation.sig` verifies under an Ed25519 key from a `verifying-agency` entry on the **same signed document**. Probe-lab is on the document → your attestation must verify under probe-lab's pubkey. * **Invariant (b) — stand-in (`standin`) production bar:** entries with `accreditation_tier: standin` are excluded when `PHOSRA_ENV=production`. Your entry appears on sandbox Trust Lists and is absent from production Trust Lists until the stand-in (`standin`) tier is graduated. Verify admission on the sandbox Trust List: ```bash theme={null} curl https://phosra-api-sandbox-production.up.railway.app/.well-known/ocss/trust-list \ | jq '.document | fromjson | .entries[] | select(.did == "did:ocss:")' ``` ### 6. Confirmation You receive an email confirmation. Phosra provides your production census URL (applicable once a real verifying-agency promotes your entry off `standin`), the production trust-root X value, and your initial `phosra_live_...` API key for billing attribution. ***

Honest status today (self-tested tier)

After completing steps 1–5, your entry appears on the sandbox Trust List with: ```json theme={null} { "role": "classifier-accredited", "accreditation_tier": "standin", "tier": "accredited", "conformance_attestation": { "attested_by": "did:ocss:probe-lab", "assertions_passed": ["a1", "a2", "a5", "a7"], ... } } ``` What this means in plain terms: * **4/7 assertions passed** (A1, A2, A5, A7) — your classifier handles the core content-safety signal flow correctly. * **3/7 assertions pending** (A3, A4, A6) — consent infra, capability endpoint, and advocate routing are not yet provable. Pending is not a failure: the infrastructure they require does not yet exist. * **Standin tier** — excluded from production Trust Lists; included in sandbox. * **Verifying-agency is a SIM** — `did:ocss:probe-lab` is a sandbox simulator, not a real independent accreditor. The honest label is `self-tested; production accreditor pending`. ***

Path to full production accreditation

Full production accreditation is a governance step, not a code step. None of these are met today: 1. **A real governance-designated verifying-agency.** A named independent organization on the Trust List, separate from Phosra. Phosra cannot self-accredit and keep the uniformity claim honest. A production accreditor is pending designation. 2. **All 7 assertions passing.** A3, A4, and A6 require: * A3: two-attestor consent infra (Tier 4.1) * A4: a `GET /capabilities` endpoint declaring `monitoring_active` * A6: the advocate routing lane (Tier 2.5, gated on counsel) 3. **Removing the `standin` tier.** Once a real verifying-agency counter-signs the attestation and all 7 assertions pass, the administrator promotes the entry by clearing `accreditation_tier`. The entry then passes compiler invariant (b) and appears on the production Trust List. *** ## What changes after promotion | Capability | Provisional (sandbox) | Self-tested / stand-in (`standin`) | Accredited (production) | | -------------------------------- | ------------------------------ | --------------------------------------------- | ------------------------- | | Sign rule writes | Sandbox only | Sandbox only | Production | | Gated-band envelopes | No | No | Yes (if Assessed lane) | | Restricted-band envelopes | No | No | Yes (if Audited lane) | | Billing attribution | Not applicable | Not applicable | `phosra_live_...` API key | | `phosra doctor --env production` | Fails — not on prod Trust List | Fails — stand-in (`standin`) barred from prod | Passes | *** ## After you are accredited 1. Update your `LinkConfig.censusBaseUrl` to the production census URL Phosra provides. 2. Update `LinkConfig.trustRootXB64Url` to the production trust root (distinct from sandbox). 3. Set `LinkConfig.developerOrgId` to your `org_...` ID for billing attribution. 4. Run `phosra doctor --env production` to confirm the production round-trip is green before your first real user connects. *** ## Next * [Getting on the Trust List](/ocss/onboarding) — sandbox self-registration (no email required) * [Server-Side Enclave Routing](/integration/server-side-enclave-routing) — enclave API, conformance harness, sandbox round-trip * [Provider Quickstart](/integration/provider) — `@phosra/link` integration * [Platform Quickstart](/integration/platform) — `@phosra/gatekeeper` integration * [Billing Model](/integration/billing) — the FamilyMeter and what is always free # Provider Quickstart Source: https://docs.phosra.com/integration/provider Parental-controls providers use @phosra/link to link families, ingest parent consent, and write signed rule directives to the hosted OCSS census. Parental-controls vendors use `@phosra/link` to: 1. **Link a family** — run the connect ceremony that binds a parent's account on your platform to the child's OCSS policy on the census 2. **Write rules** — sign and post rule directives (`addictive_pattern_block`, `content_rating`, etc.) under the granted scope 3. **Fan out** — converge one parent intent across all connected platforms for a child simultaneously All signing is **local** — your Ed25519 private key never leaves your process. The census verifies signatures but never holds your key. *** ## Prerequisites Before writing code: | Item | Source | | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `did:ocss:` on the Trust List | **Sandbox:** self-serve via `phosra register` or `POST /api/v1/advisors/self-register`. **Production:** [apply for accreditation](/integration/production-accreditation). | | Ed25519 signing key pair (writer + parent persona) | A `SenderKey` = `{ seed, keyID }` — a 32-byte Ed25519 seed + its `did:ocss:#` key id (see [LinkConfig](/sdks/link#linkconfig)). There is **no** `makeSenderKey()` helper; build the object directly. `@openchildsafety/ocss` exports `ed25519PublicFromSeed(seed)` to derive the public half for the Trust List. | | Postgres database (for the grant store) | Any Postgres — the SDK writes one table | | Census URL | `https://phosra-api-sandbox-production.up.railway.app` (sandbox) | | Trust-root X | `CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU` (sandbox — see note below) | | `phosra_live_...` API key | **Self-serve** — [sign up and mint it yourself](/platform/create-account), no email required. **Billing-attribution only** — the quickstart below runs correctly without one. | The trust-root X above is a **PUBLIC** key, pinned here out-of-band (that's the point of root verification — don't fetch it from the census you're about to verify). The **production sandbox** — the stable testing endpoint — has pre-provisioned keys and a pre-seeded policy that `phosra init --role provider` wires automatically. Use that to verify your integration before onboarding live accredited-network keys. *** ## Bootstrap ```ts theme={null} import { createLinkSession, completeLink, runConnectCeremony, initPlatformOAuth, completePlatformOAuth, bindProfile, directive, convergeFamily, } from "@phosra/link" import type { LinkConfig } from "@phosra/link" const cfg: LinkConfig = { censusBaseUrl: "https://phosra-api-sandbox-production.up.railway.app", trustRootXB64Url: "CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU", // PUBLIC — pinned out-of-band, see note above parentKey: parentPersonaSenderKey, // did:ocss:household- writerKey: providerSenderKey, // did:ocss:guardline writerDid: "did:ocss:guardline", routerDid: "did:ocss:phosra-router", householdSecret: process.env.HOUSEHOLD_SECRET!, // never sent to the census parentSessionSecret: process.env.PARENT_SESSION_SECRET, // verified parent sessions (see below) pool: pgPool, developerOrgId: process.env.PHOSRA_DEV_ORG_ID, // your org_... ID — required for billing } ``` `developerOrgId` is how Phosra attributes the linked family to your org for invoicing. Omit it and the family lands in the anonymous/self-host bucket — no invoice, but also no SLA. *** ## Connect ceremony The connect ceremony links a parent's account on a platform (Loopline, etc.) to the child's OCSS policy on the census. It is a 4-step sequence. **The consent surface is branded, and the branding is required.** Present it via the published kit ([PhosraLinkKit](/sdks/link-ios) / [`@phosra/connect`](/sdks/link)) — do not hand-roll or restyle it. The `phosra · OCSS` lockup and the Trust-List trust signals are **assessed** at accreditation. See the [Phosra Link Branding Requirement](/sdks/branding). Two things happen under the hood on steps 1–2 that you never call directly: * **Platform resolution (two layers, fail closed).** The SDK first verifies the platform's DID against the root-signed **OCSS Trust List** — absent, suspended, revoked, or expired entries refuse to connect. Only then does it fetch the platform's OAuth endpoints from the Phosra connect registry ([`GET /providers/{did}/connect`](/api-reference/providers/connect)). The Trust List decides *whether* you may connect; the connect registry says *how*. * **Parent-session binding.** The single-use OAuth `state` is bound to `parentSessionRef`, so one parent's ceremony can never be completed under another parent's session (session-fixation defense). With `parentSessionSecret` set (recommended), the ref must be a signed, expiring token — see [Parent sessions](#parent-sessions) below. ### Step 1 — `initPlatformOAuth` Start the OAuth flow between the parent and the target platform. ```ts theme={null} const { authorizeUrl, state, sessionId } = await initPlatformOAuth(cfg, { platformDid: "did:ocss:loopline", // the live sandbox connect config redirectUri: "https://app.guardline.example/oauth/callback", parentSessionRef: req.session.id, childHint: childId, // optional login_hint }) // redirect the parent's browser to authorizeUrl ``` ### Step 2 — `completePlatformOAuth` On the OAuth callback, exchange the code and retrieve the platform's child-profile list. ```ts theme={null} const { sessionId, childProfiles } = await completePlatformOAuth(cfg, { code: req.query.code, state: req.query.state, parentSessionRef: req.session.id, }) // display childProfiles to the parent for confirmation ``` ### Step 3 — `bindProfile` Record the parent's confirmed child selection. ```ts theme={null} const session = await bindProfile(cfg, { sessionId, platformChildProfileId: parent.pickedProfileId, // from childProfiles childId: "child:a11ce0fa-...", granted_scope: ["addictive_pattern_block", "content_rating"], ageHint: "13_15", // defaults to "under_13" parentSessionRef: req.session.id, // REQUIRED when parentSessionSecret is set }) ``` `granted_scope` is the set of rule categories the parent is consenting to. Only these categories can be written via `directive` later — `ScopeError` is thrown before signing if you attempt to write outside the granted scope. ### Step 4 — `runConnectCeremony` Orchestrates the remainder: derives the unlinkable household hash, mints the grant, ingests the §8.3.2 parent-consent attestation to the census, binds the enforcement endpoint, and calls your `deliver` callback with the `endpoint_id_label`. ```ts theme={null} const { grant_id, endpoint_id_label } = await runConnectCeremony( cfg, session, async (label) => { // Server-to-server: deliver the label to the platform's webhook. // Pass the platform's registered connect_url BASE — deliverLabelToPlatform // appends the well-known /api/ocss/connect path (same as census test-connect). // The label is disclosed EXACTLY ONCE — deliver immediately. await deliverLabelToPlatform(platformConnectUrl, { endpoint_id_label: label, state, }) }, ) ``` The `endpoint_id_label` is the platform's resolver handle. The platform stores it and uses it to fetch its enforcement profile. It is opaque and disclosed once. ### Parent sessions The parent authenticates with **your** auth — Phosra never sees parent credentials. The `parentSessionRef` on steps 1–3 is the binding between that login and the ceremony, and it has two modes: * **Verified (recommended):** set `parentSessionSecret` in `LinkConfig`, and after your login succeeds issue a signed, expiring token with `issueParentSession(secret, { parentId })` (default TTL 30 minutes; an `HttpOnly; Secure` cookie is the natural carrier). The SDK verifies it on all three legs — HMAC + expiry, fail closed — and binds the ceremony to the verified session id. An expired login cannot start *or finish* a ceremony; another parent's valid token is rejected. * **BYO:** leave `parentSessionSecret` unset and pass your own server-side session identifier (as the snippets above do with `req.session.id`). Contract: derive it server-side from your authenticated session; never accept it from the client. A complete, framework-free reference BFF — the three connect routes behind a real login cookie — is **reproduced in full, copy-pasteable, in the SDK reference**: [`@phosra/link` → Reference BFF](/sdks/link#reference-bff). That inline source is **authoritative**: do not assume your installed `@phosra/link` tarball ships an `examples/reference-bff/` directory — some published versions do not. Copy the source from that page. *** ## Write a rule — `directive` After a grant is active, `directive` signs and POSTs a rule write to the census. ```ts theme={null} const res = await directive(cfg, grant_id, "addictive_pattern_block", "child:a11ce0fa-...", { decision: "block", }) // 201 = applied, 200 = idempotent dedup, 4xx = error ``` For rating-threshold categories, pass `params`: ```ts theme={null} await directive(cfg, grant_id, "content_rating", "child:a11ce0fa-...", { decision: "block", params: { family: "numeric_threshold", scale: "ratings_age", max_allowed: 13, }, }) ``` `directive` throws `ScopeError` before any network call if `rule_category` is outside the grant's `granted_scope`. No partial writes. ### Consent standing classes Every rule write carries a `standing` — the census-verified basis for the writer's authority over the child. Two `consent:` classes are supported, and the census resolves both **at write time**, fail closed (a malformed or unresolvable standing is a `403 standing_failure`, never a silent accept): | Standing | Basis | Who uses it | | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `consent:attestation:` | A §8.3.2 signed parent-consent attestation previously ingested to the census — what `runConnectCeremony` mints for you. | The default for the connect-ceremony flow. `directive` supplies it automatically from the grant. | | `consent:product-membership::` | A **product fact**: the authenticated parent's family membership. The census verifies, in one query, that the user is a member of the family *and* the target child belongs to that family. Resolves to `verified` tier. | Delegated clients operating on the Phosra product plane — providers whose family graph lives on the census (e.g. a first-party app writing rules on behalf of its logged-in parent) — where a separate signed attestation would restate a relationship the product already proves. | The `product-membership` class requires the `:` pair to exist in the census's family tables, so it is not usable by providers who keep their family graph in their own database — use the attestation flow there. Both classes ride the same RFC 9421-signed write; the standing changes what the census checks, not how the request is authenticated. *** ## Fan out across all connected platforms — `convergeFamily` To apply one parent intent to **all active grants for a child** (every connected platform at once): ```ts theme={null} const { writes } = await convergeFamily(cfg, "child:a11ce0fa-...", { rule_category: "addictive_pattern_block", decision: "block", }) for (const w of writes) { console.log(w.audience_did, w.status) // status: "applied" | "skipped" | "error" // "skipped" = category not in that grant's scope (ScopeError, not a census call) } ``` `convergeFamily` parallelizes the rule writes. `"skipped"` is not an error — it means the parent didn't grant that category when linking to that platform, which is correct. *** ## New rules: age\_appropriate\_profile\_mode and ncii\_takedown Two rule categories added in the 2026-06 batch are live in the capabilities document. Both have `tier: "anchored"` in `phosra caps` output. **`age_appropriate_profile_mode`** — "OCSS Child Mode" anchor signal (`tier: "anchored"`). The provider writes this category (decision `"block"`) to record the parent's intent, then fans out to every rule in `CHILD_MODE_BUNDLE` via `convergeFamilyBundle`. The census never sees "Child Mode" as a concept — only N independent `directive()` writes (one per bundle member). ```ts theme={null} import { CHILD_MODE_BUNDLE, convergeFamilyBundle } from "@phosra/link" // 1. Write the anchor signal await directive(cfg, grant_id, "age_appropriate_profile_mode", childRef, { decision: "block", }) // 2. Fan out to all 8 bundle members (streaming_age_rating_enforce, age_gate, // dm_restriction, stranger_dm_block, addictive_pattern_block, app_install_block, // notification_curfew, parental_event_notification) — each a real directive() const { results } = await convergeFamilyBundle(cfg, childRef, CHILD_MODE_BUNDLE, "block") // results: per-category { category, status: "applied"|"skipped"|"error" } ``` **`ncii_takedown`** — Non-consensual intimate image takedown directive (`tier: "anchored"`). The census enforces the floor at write time; a `403` means your Trust List entry does not yet hold the required standing. Both categories appear in `phosra caps` under `ANCHORED`. Their behavior on the platform side is described in [Platform quickstart — enforcing new categories](/integration/platform#enforcing-new-rule-categories). *** ## Age assurance guardrail and intervention The census enforces an **age assurance-level guardrail** at write time for age-gated categories (e.g. `age_gate`, `age_appropriate_profile_mode`). If the child's age-assurance level on the census is insufficient for the declared `ageHint` on the grant, the write returns a `403 age_assurance_insufficient` response — not a rule-write error, a guardrail. When the guardrail fires, the census emits a §8.3.8 **intervention** record (not an enforcement confirmation — intervention is a different verb). Your platform receives it on its registered enforcement-confirmation endpoint. ``` POST /api/v1/enforcement-confirmations ← from the census to the platform { "type": "intervention", "category": "age_appropriate_profile_mode", "reason": "age_assurance_insufficient", "child_ref": "child:..." } ``` Handle it by pausing enforcement and surfacing a re-verification UX to the parent. *** ## Local operations vs census calls | Operation | Where it runs | | --------------------------------- | ------------------------------------------------- | | Ed25519 signing (every request) | Your process | | Household hash derivation | Your process | | `ScopeError` pre-check | Your process | | Trust List fetch + verify-to-root | Census: `GET /.well-known/ocss/trust-list` | | Platform connect-config fetch | Census: `GET /api/v1/providers/{did}/connect` | | Grant mint | Local (`phosra_link_grants` table in your DB) | | Consent attestation ingest | Census: `POST /api/v1/webhooks/inbound/app-store` | | Endpoint binding | Census: `POST /api/v1/enforcement-endpoints` | | Rule write | Census: `POST /api/v1/policies/{policyId}/rules` | | Profile distribution | Census internal | *** ## Next * [Platform quickstart](/integration/platform) — the Loopline (platform) side: receive the label, enforce locally * [Billing](/integration/billing) — when a family is billable * [Forward compatibility](/integration/forward-compatibility) — new rules and vocab without reinstalling # Provider Discovery — the directory Source: https://docs.phosra.com/integration/provider-discovery Browse GET /api/v1/directory to find a provider counterparty by DID, role, and capability, then resolve how to reach it with GET /providers/{did}/connect. The directory is an index over the signed Trust List — never a key gate. Before a platform can run the connect ceremony it needs a **provider counterparty**: a DID that is on the Trust List, active, and reachable. `GET /api/v1/directory` is the public browse index for exactly that — "who is registered, in what role, with what capabilities, and does it have a sealed-classify enclave I can connect to." **The directory is an index, not a key gate.** It serves booleans and pointers only — `has_payload_key`, an `enclave` marker, the Trust-List path. Actual key material is resolved from the one root-signed `/.well-known/ocss/trust-list` document (§11.9 / §4.2 cl.2), verified offline against your pinned root X. The directory never returns a public key. *** ## `GET /api/v1/directory` Public, unauthenticated read. No `phosra_` key, no signature. **Query parameters (all optional):** | Param | Meaning | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `role=` | Keep entries whose Trust-List `entry_role` equals ``. `role=routing` matches the default (empty) role. Vocabulary: `classifier-accredited`, `verifying-agency`, `enforcement-agent`, `independent-advocate`, `routing`. | | `capability=` | Keep entries that declare §5.3 manifest cell `` as `enforce` or `alert_only` on at least one OS (e.g. `capability=addictive_pattern_block`). | | `status=all` | Include non-active entries (default: active only). | **`200` response:** ```json theme={null} { "trust_list": "/.well-known/ocss/trust-list", "issue_number": 57, "issued_at": "2026-07-03T13:10:45Z", "entries": [ { "entity": "Loopline (OCSS reference emitter)", "did": "did:ocss:loopline", "role": "", "tier": "verified", "status": "active", "jurisdiction": [], "valid_through": "2026-12-30T13:10:45Z", "capabilities": [], "has_payload_key": true, "enclave": { "enclave_id": "01f05283-…", "endpoint_url": "…", "mode": "standin", "attested": false } } ] } ``` | Field | Meaning | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `did` | The counterparty DID you pass to the connect-config lookup below. | | `role` | Trust-List `entry_role` (empty = a routing entry). | | `tier` | `provisional` \| `verified` \| `accredited` (§3.6 ladder). | | `has_payload_key` | `true` ⇒ the DID publishes an encryption JWK on its Trust-List entry, so you can seal a payload to it once you resolve its JWKS from the signed list. | | `enclave` | Present when the DID has an **active sealed-classify enclave** registered — the "there is a connectable classify counterparty here" marker. `attested` reflects live attestation (a stored-but-expired attestation reads `false`). | | `capabilities` | Union of §5.3 manifest cells the entry declares `enforce`/`alert_only`. **Empty until the provider declares a capability manifest** (a §5.3 accreditation-time step), so a freshly self-registered sandbox provider is *listed and connectable but not yet filterable* by `?capability=`. | **`role` and `capabilities` are accreditation-declared metadata.** A provider that has not yet declared an `entry_role` or a §5.3 capability manifest still appears in the unfiltered listing and is still connectable — it just won't match a `?role=` / `?capability=` filter. In sandbox, most reference providers ship without those declarations, so **enumerate unfiltered and select on the `enclave` marker (and a `200` from the connect-config lookup below) rather than relying on the facet filters.** *** ## Recipe — find a connectable provider ```ts theme={null} const CENSUS = "https://phosra-api-sandbox-production.up.railway.app" // 1. Browse the directory (unfiltered — see the warning above). const dir = await (await fetch(`${CENSUS}/api/v1/directory`)).json() // 2. Candidates = active entries that advertise a sealed-classify enclave. const candidates = dir.entries.filter((e) => e.enclave) // 3. Resolve HOW to reach one: the connect-config lookup. // 200 → connectable (returns authorize_url/token_url/profiles_url). // 404 → on the Trust List but no connect config registered yet (skip it). for (const c of candidates) { const res = await fetch( `${CENSUS}/api/v1/providers/${encodeURIComponent(c.did)}/connect` ) if (res.ok) { const cfg = await res.json() // { authorize_url, token_url, profiles_url, scopes, name } // feed cfg into the connect ceremony (initPlatformOAuth) break } } ``` `GET /api/v1/providers/{did}/connect` is the companion lookup — it returns the OAuth `authorize_url` / `token_url` / `profiles_url` the ceremony needs. See [Get provider connect config](/api-reference/providers/connect) for its full contract (and the deliberate `404` for an unknown, revoked, or connect-config-less DID — no existence leak). *** ## Next * [Get provider connect config](/api-reference/providers/connect) — the `{did}` → OAuth-endpoints lookup * [Platform Registration](/integration/platform-registration) — mint your own endpoint + `connect_secret` * [Platform Quickstart](/integration/platform) — receive the connection, verify the profile, enforce, confirm # Server-Side Enclave Routing Source: https://docs.phosra.com/integration/server-side-enclave-routing The trust layer for server-side content classification — register an enclave, classify content seal-direct, report minimization, and verify compliance bundles. All endpoints live on the sandbox today. **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`, `@phosra/classify` (0.1.0), the conformance harness `@openchildsafety/provider-harness` (≥ 0.1.3 — live-enclave targeting), plus `@openchildsafety/ocss` 0.1.0 and `@phosra/gatekeeper` 0.2.0. See [SDK install](#sdk-install-preview). ## 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. | **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: ```bash theme={null} # From the repo root (after npm install): # Demo 1 — direct enclave loop (seal → enclave verifyAndOpen → signal → receipt) CENSUS_URL=https://phosra-api-sandbox-production.up.railway.app \ ROOT_KEY_X_B64URL=CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU \ node scripts/sandbox/verify-enclave-direct-loop.mjs # Demo 2 — minimization (epoch accumulator + attestation receipt) + fan-out (two providers) + compliance bundle CENSUS_URL=https://phosra-api-sandbox-production.up.railway.app \ ROOT_KEY_X_B64URL=CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU \ node scripts/sandbox/verify-minimization-fanout-loop.mjs ``` 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](/ocss/status)): * `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` + `@phosra/classify` (0.1.0), the harness `@openchildsafety/provider-harness` (≥ 0.1.3), alongside `@openchildsafety/ocss` (0.1.0) and `@phosra/gatekeeper` (0.2.0). ```bash theme={null} npm install @phosra/provider @phosra/classify @openchildsafety/provider-harness ``` Once built, import them by workspace path (or monorepo `node_modules/@phosra/…`): ```ts theme={null} import { verifyAndOpen, createEpochAccumulator, closeEpoch, submitMinimizationAttestation } from "@phosra/provider"; import { classify, classifyMulti, verifyComplianceBundle } from "@phosra/classify"; import { runHarness } from "@openchildsafety/provider-harness"; ``` *** ## 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: ``` POST /api/v1/enclaves/challenge ``` Requires RFC-9421 signature. Response (`201`): ```json theme={null} { "challenge_id": "", "nonce_hex": "<64 hex chars>" } ``` The nonce is single-use (5-minute TTL). Include it in the registration body as `challenge_id`. ### 2. Register the enclave ``` POST /api/v1/enclaves ``` Requires RFC-9421 signature (the `provider_did` must be on the OCSS Trust List). **Request body:** ```json theme={null} { "provider_did": "did:ocss:courier", "mode": "standin", "endpoint_url": "https://your-enclave.example.com", "payload_pubkey_jwk": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" }, "signing_pubkey": "", "attestation_document": null, "challenge_id": null } ``` | Field | Required | Notes | | ---------------------- | -------- | ------------------------------------------------------------------------------------ | | `provider_did` | Yes | `did:ocss:` — 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` | **Response** (`201 Created`): ```json theme={null} { "enclave_id": "" } ``` **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 ``` GET /api/v1/enclaves/active?provider_did=did:ocss:courier ``` No authentication required. Response (`200`): ```json theme={null} { "enclave_id": "", "provider_did": "did:ocss:courier", "endpoint_url": "https://your-enclave.example.com", "payload_pubkey_jwk": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" }, "signing_pubkey": "", "mode": "standin", "attested": false, "revoked": false, "updated_at": "2026-06-29T00:00:00Z" } ``` `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: ```ts theme={null} import { classify } from "@phosra/classify"; const result = await classify( "hey what time is it?", { child: "child-abc123", providerDid: "did:ocss:courier" }, { censusUrl: "https://phosra-api-sandbox-production.up.railway.app", platformSenderKey: { seed: <32-byte Uint8Array>, keyID: "did:ocss:loopline#" }, } ); // result.verdict — "allow" | "flag" (from your enclave) // result.receipt — the enclave's signal_receipt from the census ``` **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: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: ```ts theme={null} import { classifyMulti } from "@phosra/classify"; const result = await classifyMulti( "hey what's your address?", { child: "child-abc123", providers: ["did:ocss:loopline", "did:ocss:courier"] }, { censusUrl: "https://phosra-api-sandbox-production.up.railway.app", platformSenderKey } ); // result.providerCount — 2 (requested) // result.authorizedCount — 2 (received receipts) // result.verified — true iff authorizedCount === providerCount // result.manifestReceipt — receipt from POST /routing-manifests (post-hoc, fail-open) // result.manifestReceiptJson — raw JSON string (use for Merkle leaf computation) ``` **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: ``` GET /api/v1/children/{child_ref}/enclaves ``` 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: ``` POST /api/v1/routing-manifests ``` 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:** ```json theme={null} { "child_ref_epoch_hash": "", "provider_key_ids": ["did:ocss:loopline", "did:ocss:courier"], "scope_groups": [], "provider_count": 2 } ``` `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: ```ts theme={null} import { verifyComplianceBundle } from "@phosra/classify"; const bundleReceiptWire = /* POST /api/v1/compliance-bundles response */; const verify = verifyComplianceBundle( { receiptJsonStrings: [result.manifestReceiptJson], // raw strings from classifyMulti bundleReceipt: bundleReceiptWire, }, (keyId) => trustListResolver.signingKey(keyId), // Trust-List-backed resolver ); // verify.verified — true iff all receipts passed Ed25519 + Merkle root matches // verify.merkleMatch — true iff locally-recomputed root === bundle's claimed root // verify.timestamped — true iff the bundle carries a non-empty tsa_token // verify.errors — per-receipt error strings (never suppressed) ``` **POST /api/v1/compliance-bundles** (called by your code; `verifyComplianceBundle` is purely client-side): ``` POST /api/v1/compliance-bundles ``` Requires RFC-9421 signature. ```json theme={null} { "manifest_receipts": [ { "id": "", "receipt_json": { /* full receipt wire JSON object */ } } ] } ``` 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 ```ts theme={null} import { createEpochAccumulator, closeEpoch, submitMinimizationAttestation } from "@phosra/provider"; const acc = createEpochAccumulator({ enclaveIdentityKey: <32-byte seed from enclave identity>, epochId: "epoch-2026-06-29-001", familyIdHash: "", enclaveDid: "did:ocss:courier", }); // Called after each classify decision: acc.record({ msgRef: "msg-001", kind: "benign", harmClass: "grooming", delivered: false }); acc.record({ msgRef: "msg-002", kind: "flagged", harmClass: "grooming", delivered: false }); ``` 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 ```ts theme={null} const attestation = closeEpoch(acc, { classifierBuildHash: "sha256:", epochStart: "", epochClose: "", }); // attestation.n_classified, .f_flagged, .f_delivered, .salted_merkle_root const receipt = await submitMinimizationAttestation(attestation, { censusUrl: "https://phosra-api-sandbox-production.up.railway.app", enclaveSenderKey: { seed: <32-byte seed>, keyID: "did:ocss:courier#" }, }); // receipt.body.conservation_check — "self_report" (solo mode) or "fail" (discrepancy) // receipt.body.two_attestor — false (solo mode) ``` ### Wire contract (`POST /api/v1/minimization-attestation`) Requires RFC-9421 signature (`enclave_did` in the body must match the verified caller DID). ```json theme={null} { "epoch_id": "epoch-2026-06-29-001", "family_id_hash": "", "enclave_did": "did:ocss:courier", "classifier_build_hash": "sha256:", "n_classified": 42, "f_flagged": 3, "f_delivered": 0, "salted_merkle_root": "<64 hex chars>", "epoch_start": "2026-06-29T00:00:00Z", "epoch_close": "2026-06-29T01:00:00Z" } ``` `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. ```bash theme={null} # Self-test (in-process reference enclave — no network, no setup) npx -p @openchildsafety/provider-harness provider-harness run --enclave ref # Against YOUR live enclave — target it by URL (the flag is `--enclave-url`, # not `--enclave`; the harness probes GET {url}/buildinfo + POST {url}/classify) npx -p @openchildsafety/provider-harness provider-harness \ run --enclave-url https://your-enclave.example.com # (or `npm install @openchildsafety/provider-harness` then `npx provider-harness …`. # Use the `-p … provider-harness` form — bare `npx @openchildsafety/provider-harness` # is unreliable due to an npx scoped-package bin quirk.) ``` 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:** | 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 | 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) ``` PUT /api/v1/enclaves/{id}/revoke ``` 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](/ocss/onboarding) to self-register your `did:ocss:` on the sandbox Trust List. * **Content monitoring architecture** — see [Safe Content Monitoring](/integration/content-monitoring) for the full on-device classifier design. * **Production accreditation** — see [Production Accreditation](/integration/production-accreditation) to submit your harness results for Trust List accreditation. # Introduction Source: https://docs.phosra.com/introduction Phosra is an accredited OCSS provider — the Phosra control-plane API and the open OCSS protocol, clearly separated. # Build child-safety enforcement in minutes Phosra is the accredited developer platform for the Open Child Safety Specification (OCSS). Sign up, mint a `phosra_` test key, and make your first authenticated call before your coffee gets cold — no sales call, no gated waitlist. Sign up → your dev org auto-provisions → copy a `phosra_test_` key. Self-serve, no email to anyone. One `curl` against the live control plane. Copy, paste, get a `200`. ## Start where you are An app, OS, router, or school network that **enforces** age-appropriate rules. Fetch a signed profile and enforce locally. A parental-control or safety vendor that **issues** rules and consent on a family's behalf. Signed receipts, sealed envelopes, the Trust List — the vendor-neutral OCSS surface, no Phosra account. ## Your first API call The control plane is live right now. This call needs no key — it proves the platform is up and the org endpoint is real (a `401`, not a `404`, is the correct answer without your session bearer): ```bash theme={null} # The Phosra control plane is live. curl -fsS https://prodapi.phosra.com/health # → 200 {"status":"ok"} ``` Ready for a real key? **[Create your account & get keys →](/platform/create-account)** *** ## How Phosra relates to OCSS Phosra is an accredited provider on the Open Child Safety Specification (OCSS) — the open standard for age-appropriate access control. Phosra implements OCSS; it does not own it. The relationship is the same as Yubico shipping a FIDO2-conformant authentication key: Yubico implements FIDO2; the FIDO Alliance owns the standard. Phosra implements OCSS; the OCSS stewardship body owns the standard. The consequence for every API call, receipt, and SDK import in this documentation: the protocol signing and verification primitives are not Phosra code — they are the open OCSS library, `@openchildsafety/ocss`, re-exported without modification. > **Reading these docs programmatically?** Fetch [`/llms.txt`](https://docs.phosra.com/llms.txt) > for the index, [`/llms-full.txt`](https://docs.phosra.com/llms-full.txt) for the full corpus, > or append `.md` to any page URL (e.g. `/integration/overview.md`) for raw markdown — the > rendered HTML pages are a JS app and do not return content to non-browser fetchers. *** ## Two halves Every surface in this documentation belongs to one of two clearly-separated halves. ### The Phosra control plane Management operations over the Phosra platform: create developer orgs, provision and revoke `phosra_`-prefixed API keys, register and consult advisor agents, declare OCSS payload keys for federation, mint and revoke MCP tokens, and pull hourly usage rollups. All management operations use standard HTTP Bearer authentication. This is the Phosra-specific surface. [Platform overview →](/platform/overview) ### The OCSS protocol The open protocol surface: signed write receipts, sealed envelopes, the Trust List, the succession record, and the 115-category rule vocabulary — all sourced from `@openchildsafety/ocss`, the vendor-neutral OCSS reference library. Phosra adds zero cryptographic logic here. Install `@openchildsafety/ocss` directly (published at 0.1.0); the `/protocol` subpath of the planned `@phosra/sdk-dev` unified wrapper will re-export it verbatim when that package ships. If you later integrate a different OCSS-conformant provider, this surface is identical across implementations. [OCSS overview →](/ocss/overview) *** ## What is live today **Live now — no account required:** * The OCSS Trust List, served at `/.well-known/ocss/trust-list` — validly signed against the published root key (`root-prod-2026-06`). Production accreditation entries are added through OCSS governance; no test or sandbox keys are present in production. * The Ed25519-signed succession record, served at `/.well-known/ocss-succession` — the steward-of-record and anti-capture covenant in machine-checkable form. **Live now — auth-gated:** * The self-serve developer funnel: [sign up](https://dashboard.phosra.com/signup) → your developer org is auto-provisioned → mint your first `phosra_` key from the [developer console](https://dashboard.phosra.com/dashboard/developers/console) or one API call. No pre-existing key, no handshake, no email to anyone. See [Create your account & get keys](/platform/create-account). * The Phosra control-plane management API at `https://prodapi.phosra.com/api/v1`. Org and key management authenticates with your WorkOS session bearer (from signup/login); the `/developer/*` data-plane routes take the `phosra_` API key you mint. Covers orgs, API keys, usage, advisor agents, and MCP tokens. **Live on npm:** * Individual packages: `@openchildsafety/ocss@0.1.0`, `@phosra/sdk@0.1.0`, `@phosra/gatekeeper@0.1.0`, `@phosra/link@0.1.0`, `@phosra/mcp@0.4.0`, `@phosra/cli@0.2.0`, `@openchildsafety/bundle@0.1.0`. **Preview — committed shape, not yet deployed:** * The unified `@phosra/sdk-dev` wrapper (which will re-export the individual packages above) is not yet published — install them individually today. Each page in this documentation labels its live-vs-preview status explicitly. No endpoint is implied live unless marked as such. *** ### There is no hosted decision endpoint — by design Enforcement decisions are made **locally**. `@phosra/gatekeeper` fetches the signed enforcement profile from the census **once** (or on a polling interval) via the §9.3(b) read (`GET /api/v1/enforcement-profiles/{endpoint_id}`), verifies the router's Ed25519 signature to root in-process, and then `isAllowed({ category, signal })` evaluates **every** subsequent enforcement call against the cached, signed profile — zero network calls, zero latency, **fail-closed** when the profile is absent. `isAllowed()` IS the decision call. There is no `POST /v1/check` endpoint and none is planned: a hosted decision call would add per-decision latency, put a network dependency on the enforcement hot path, and break the fail-closed guarantee when the census is unreachable. *** ## Base URL ``` https://prodapi.phosra.com/api/v1 ``` All Phosra control-plane management requests use this base URL. Override for local development with `http://localhost:8080/api/v1`. *** ## The standard lives at openchildsafety.com The OCSS specification, 115-category rule registry, and conformance suite are published at [openchildsafety.com](https://openchildsafety.com) — not here. Phosra does not host, own, or gatekeep the standard. That separation is the asset: a standard you cannot capture is one you can build on. # OCSS Signed Rule Writes & Intent API Source: https://docs.phosra.com/ocss/intent-api The Ed25519-signed write-receipt surface (live) and the intent-resolution API (not yet built — roadmap). **The Intent API is not yet built — no endpoint, no live badge.** This page describes the intended design. For what is live today, see the [signed write-receipt surface](#ocss-signed-write-receipts) below. *** ## OCSS Signed Write Receipts **This surface is live.** When a partner applies a rule via `POST /api/v1/policies/{policyID}/rules` (see [Apply a rule to a policy](/api-reference/rules/create-rule)), the Phosra census returns a `PolicyRule` record — the persisted rule object. That is the Phosra API response. The **OCSS signed write receipt** is a distinct artifact: an Ed25519-signed envelope issued by the census under its OCSS router-role key, verifiable by any party against the live [OCSS Trust List](https://prodapi.phosra.com/.well-known/ocss/trust-list) (`/.well-known/ocss/trust-list`). The receipt carries: | Field | Description | | --------------- | --------------------------------------------------------- | | `rule_id` | Canonical identifier for the stored rule | | `rule_category` | The OCSS slug (e.g. `addictive_pattern_block`) | | `decision` | `block`, `allow`, or `flag` | | `writer` | DID of the Issuer who wrote the rule | | `standing` | OCSS standing class under which the rule is band-eligible | | `applied_at` | ISO 8601 timestamp | An enforcement sim — any party whose DID is accredited on the Trust List — can verify this receipt using the `@openchildsafety/ocss` SDK (`verifyReceipt(receipt, trustListResolver)`). Verification confirms: 1. The census signed the receipt under a key present on the Trust List. 2. The writer's DID is an accredited Issuer for the rule category. 3. The standing class is band-eligible for the policy. A signed receipt means the census accepted and recorded the write. It does not mean the rule is enforced — enforcement is a separate act by the platform's enforcement sims. ### Honest live bridge A resolution layer already exists for the consent-driven consumer flow: a parent `consent:attestation` maps to correctly-standing, band-eligible rule-writes that the census signs, and approximately 65 enforcement sims confirm against the Trust List (live-proven on the canonical sandbox census `phosra-api-sandbox-production.up.railway.app`). This is a *specific* resolution path — parent consent to a named app and child — not the general intent resolver described below. The generalized `child + jurisdiction → ruleset` resolver is not built. *** ## Intent API — not yet built / roadmap **Not yet built — roadmap only.** There is no `/api/v1/intent/resolve` endpoint. No schema is published. This section describes the intended design so integrators can plan ahead; it is not a specification. The Intent API would let an Issuer describe a child's context and receive the applicable OCSS rule categories with pre-computed standings and typed-parameter defaults — without requiring the Issuer to navigate the 123-rule vocabulary directly. ### Intended input shape ```json theme={null} { "child_age": 13, "jurisdiction": "US-CA", "platform_did": "did:ocss:loopline", "context": { "surface": "social_feed", "content_category": "user_generated" }, "consent_state": { "parental_declaration": true, "verified_age": false, "consent_attestation_id": "att_01..." } } ``` ### Intended output shape ```json theme={null} { "applicable_categories": [ { "category": "addictive_pattern_block", "standing": "consent:attestation:att_01...", "param_defaults": {}, "band": "Open" }, { "category": "algorithmic_amplification_ban", "standing": "consent:attestation:att_01...", "param_defaults": {}, "band": "Open" } ], "jurisdiction_laws": ["cppa", "dsa_minor_protections"], "generated_at": "2026-07-01T00:00:00Z" } ``` ### What "not built" means precisely * No `/api/v1/intent/resolve` endpoint exists anywhere — not in staging, not in production. * The jurisdiction → law → rule mapping is not implemented in the Go census. * The `@phosra/sdk` does not expose an `intentResolve()` helper. * There is no schema registered in the Phosra OpenAPI spec for this surface. The underlying building blocks exist — the 123-rule registry, per-rule jurisdiction mappings in the OCSS compliance package (`packages/compliance/src/law-registry.ts`), and the `consent:attestation` standing class — but assembly into a callable intent resolver is a future engineering project with no active sprint. ### Why this is listed here The consent-driven flow that exists today (a parent attestation → a standing → a rule-write) is a subset of what the Intent API would generalize. Documenting the boundary prevents integrators from inferring a capability that is not there. *** ## See also * [Apply a rule to a policy](/api-reference/rules/create-rule) — the live API that writes a rule and returns the persisted `PolicyRule` record (the OCSS signed write receipt is a separate surface) * [OCSS Rule Reference](/ocss/rule-reference) — all 123 categories, capability matrix, and API schema membership * [Conformance Status](/ocss/status) — what is live, what is in preview, what is not built # Getting on the Trust List Source: https://docs.phosra.com/ocss/onboarding How to join the OCSS Trust Framework — DID registration, accreditation lanes, and per-role onboarding artifacts. Sandbox self-registration is live today; production accreditation is governance-gated. The OCSS Trust List is the signed document that enumerates every accredited party on the network. Getting on it is the precondition for originating signed operations — a write receipt, a routing attestation, an enforcement confirmation. This page describes what that means today. **Two tracks, both real.** The **sandbox** Trust List supports self-serve DID registration today — `POST /api/v1/advisors/self-register` self-seeds a provisional DID, no email required. The **production** Trust List has **zero third-party entries** as of this writing: production accreditation is governance-gated and requires a vetted application — see [Production Accreditation](/integration/production-accreditation). *** ## Get a sandbox DID — self-serve, no email required The fastest path to a signed round-trip is a sandbox provisional DID. It is **bring-your-own-key**: you mint the Ed25519 pair, and register its **public** half. The census never mints a key for you. ### 1. Mint your Ed25519 key pair ```bash theme={null} # Private key (keep secret) + raw 32-byte public key as base64url-RAW (unpadded, -_): openssl genpkey -algorithm ed25519 -out my-ocss-key.pem b64url() { python3 -c "import sys,base64;print(base64.urlsafe_b64encode(sys.stdin.buffer.read()).decode().rstrip('='))"; } # Public key X — this goes in the registration request: PUB_B64URL=$(openssl pkey -in my-ocss-key.pem -pubout -outform DER | tail -c 32 | b64url) # 32-byte seed — this is your SenderKey.seed for signing (keep secret; last 32 bytes of the PKCS#8 DER): SEED_B64URL=$(openssl pkey -in my-ocss-key.pem -outform DER | tail -c 32 | b64url) echo "PUB_B64URL=$PUB_B64URL" echo "SEED_B64URL=$SEED_B64URL # feed to b64urlRawDecode() → SenderKey.seed" ``` ### 2. Register the public key `public_key_b64url` is **required** — omit it and the census returns `400 public_key_b64url is required`. ```bash theme={null} curl -X POST https://phosra-api-sandbox-production.up.railway.app/api/v1/advisors/self-register \ -H "Content-Type: application/json" \ -d "{\"did\":\"did:ocss:my-org\",\"public_key_b64url\":\"$PUB_B64URL\"}" ``` **`200` response** — this is the exact shape (live-verified against the sandbox census): ```json theme={null} { "did": "did:ocss:my-org", "key_id": "did:ocss:my-org#2026-07", // your full signing key id (did#kid) — use verbatim "kid": "2026-07", // the bare kid the census bound (current UTC month) "published_key_x": "FBdykqxNN2Lz…", // echoes the public key you sent "trust_tier": "provisional" } ``` The census now **returns your bound `key_id` (and bare `kid`) directly in this `200`** — it is `did:ocss:#`, the current UTC month at registration. Use `key_id` verbatim as your `SenderKey.keyID`. (You can still confirm it against your published entry — [step 3](#3-read-back-your-bound-kid) — but you no longer have to.) A self-registered DID is a **generic provisional participant** (publisher org `Self-registered sandbox participant`). It is not scoped to a role. **Your signing key id is `did:ocss:#` — not a name you choose.** The census binds your key under a bare kid equal to the **current UTC month** at registration time (e.g. a DID registered in July 2026 gets kid **`2026-07`**). Bespoke kids like `did:ocss:my-org#my-org-2026-06` appear in signing examples on this site **only for seeded roster DIDs** (e.g. `loopline`) that publish that kid; a self-registered DID does **not**. Signing with a kid that is not the one bound in your entry fails `401` (kid-not-in-entry). **Always read your kid back from your published entry** (below) and use it verbatim as your `SenderKey.keyID`. ### 3. Read back your bound kid ```bash theme={null} curl -s https://phosra-api-sandbox-production.up.railway.app/.well-known/ocss/trust-list \ | python3 -c "import sys,json; d=json.load(sys.stdin); doc=json.loads(d['document']); \ print([e['jwks']['signing_keys'] for e in doc['entries'] if e['did']=='did:ocss:my-org'])" # → [{'crv':'Ed25519','kid':'2026-07','kty':'OKP','x':'FBdykqxNN2Lz…'}] ``` Your full signing key id is therefore `did:ocss:my-org#2026-07`. Build your `SenderKey` as `{ seed, keyID: "did:ocss:my-org#2026-07" }` (the `seed` is your private key's 32-byte Ed25519 seed). **`roles` is informational; `entry_role` is the only field that sets a Trust-List role.** The census stores a `roles: [...]` array verbatim on provisional entries but **does not act on it** — it does not make you an "issuer" or "gatekeeper". The only field that sets a real Trust-List role is **`entry_role`**, and self-register accepts exactly one value: `verifying-agency` (the §5.4 open-assessor lane). Any other `entry_role` returns `400 invalid_entry_role`. For a normal platform or provider integration you need **neither** — a plain provisional DID signs rule writes and mints endpoints just fine. Now you can call `POST /api/v1/policies/{policyId}/rules` with an RFC-9421 DID-signed request (signed with `did:ocss:my-org#`) and receive a real router-signed write receipt, verifiable against the sandbox Trust List root. The `phosra register` / `phosra doctor` CLI is a convenience wrapper over the same endpoint. Because a self-registered DID is role-agnostic, a `--role` flag (if you pass one) is cosmetic — it maps to the informational `roles` field, not `entry_role`. `phosra doctor` confirms the full sandbox round-trip is green (consent 201, rule 201, receipt verified to trust-list root). **Sandbox-only.** Provisional DIDs registered via this path are on the **sandbox** census only (`phosra-api-sandbox-production.up.railway.app` — the stable testing endpoint). They do not appear on the production Trust List for live accredited traffic. Production accreditation is a separate governance step — see below. *** ## Accreditation lanes (§5.4) The OCSS specification defines three accreditation lanes based on the capability band you intend to operate on (§5.4): | Lane | Band | Path | | --------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Implementer** | Open band | Free, self-serve: signed self-declaration published in the registry. No assessor required. The declaration creates the accountability. | | **Assessed** | Gated band | Self-attestation + a Trust List evidence bundle + randomized, steward-funded spot-verification. An existing SOC 2 Type II or ISO 27001 certificate is accepted as evidence but must not be required (§5.4). | | **Audited** | Restricted band | Independent audit by any assessor meeting published objective criteria — never a steward-appointed list (§5.4). | **Tier disambiguation.** The Trust List entry for each party carries a `tier` field: `accredited` or `provisional` (§3.6). The three accreditation lanes (§5.4) determine which tier you enter — a party on the Implementer lane enters as `provisional`; passing an independent audit reaches `accredited`. These tiers are separate from the four capability bands (§3.6): a band governs which envelope types you can carry; a tier governs what the network trusts you to sign. *** ## Per-role onboarding artifact The one real difference between roles at onboarding time is the **artifact** you register on the Trust List. The accreditation lane and the initial contact path are the same for all roles today. ### Issuer **Onboarding artifact:** Ed25519 signing key registered on the OCSS Trust List Register an Ed25519 signing key under your `did:ocss:` identifier. Signed rule writes you originate are then verifiable by any party holding the Trust List (§11.9). The write operation is `POST /api/v1/policies/{policyID}/rules` (§8.3.1). **Live status:** [Conformance Status](/ocss/status) lists the live vs. preview state for the operations backing each role. ### Verifier **Onboarding artifact:** Trust List read access — subscriber record linking family policy to the Verifier's accredited DID Register your accredited DID and request a subscriber record linking it to the family policy stores you service. CONSUME-side subscription management is `GET`/`PUT /api/v1/alerts/subscriptions` (§8.3.3); inbound signal ingest is `POST /api/v1/webhooks/inbound/{source}` (§8.3.2). **Live status:** [Conformance Status](/ocss/status) lists the live vs. preview state for the operations backing each role. ### Gatekeeper **Onboarding artifact:** enforcement-endpoint URL declared on the Trust List accreditation record Declare an enforcement-endpoint URL on your Trust List accreditation record. The census POSTs signed enforcement confirmations to that URL (§8.3.8). The URL must be reachable and respond per the enforcer floor at §4.6. **Live status:** [Conformance Status](/ocss/status) lists the live vs. preview state for the operations backing each role. ### Infrastructure Intermediary **Onboarding artifact:** routing attestation: an accredited Trust List entry as an independently operated router node (§11.9 federation floor) Register as an independently operated router node (§11.9 federation floor). Your routing attestation — a signed Trust List entry declaring the envelope bands you route and the SLO cells you operate — is the onboarding artifact (§8.3.6, §5.4). The federation floor requires at least three independently accredited routers; one is operated today. **Live status:** [Conformance Status](/ocss/status) lists the live vs. preview state for the operations backing each role. *** ## Economics and the covenant **Royalty-free vocabulary.** The OCSS rule vocabulary — all rule categories, the verb model, the envelope format — is royalty-free per §5.5. No licensing fee is owed for implementing the specification vocabulary. The cost schedule for accreditation paths is published in the specification at [openchildsafety.com](https://openchildsafety.com) (§5.5). It is not reproduced here, because the numbers belong to the specification, not to Phosra's documentation. **§12.2 covenant — not yet a signed instrument.** Joining as a founding signatory converts to a **covenant**, not yet a signed legal instrument. The distinction is honest and material: a covenant is a public commitment; it does not create a legal instrument until the governance body designates one (§12.2). OCSS is a pre-release standard — an individual IETF Internet-Draft, not yet ratified by any standards body. Conformance evidence is something a regulator can weigh; it is not a compliance determination or a safe harbor (§5.1). *** ## Next steps ### Sandbox (self-serve, start here) 1. **Generate a `did:ocss:` identifier** with an Ed25519 signing key. `@openchildsafety/ocss` includes key generation utilities; or use `openssl genpkey -algorithm ed25519` for the raw PEM. 2. **Identify your role(s)** from the four §3.2 roles — you may hold more than one. 3. **Register on the sandbox Trust List** via `POST /api/v1/advisors/self-register` (see above) — no email required. 4. **Run `phosra doctor`** to confirm a green sandbox round-trip. ### Production accreditation Production accreditation is governance-gated — it requires a review of your integration and accreditation lane (§5.4). The full process, eligibility criteria, and SLA are documented at [Production Accreditation](/integration/production-accreditation). To start: 1. Complete the sandbox round-trip first (`phosra doctor` exits 0). 2. Submit an application via `POST /api/accreditation-apply` (or email [developers@phosra.com](mailto:developers@phosra.com)) with your DID, intended role(s), and intended use case. See [Production Accreditation](/integration/production-accreditation) for the full field list. The [Conformance Status](/ocss/status) page tracks the live vs. preview split for every surface. *** ## Next * [OCSS Roles](/ocss/roles) — the four §3.2 roles in detail. * [Trust Framework](/ocss/trust-framework) — the two-layer signed envelope, Trust List, and accreditation tiers. * [Conformance Status](/ocss/status) — live vs. preview surface-by-surface. *** *If the standard and this mirror ever conflict, the standard (at openchildsafety.com) wins. Authoritative text at [openchildsafety.com](https://openchildsafety.com).* # OCSS Overview Source: https://docs.phosra.com/ocss/overview What the Open Child Safety Specification is, and what Phosra's role in it is — Phosra implements OCSS; it does not own it. **Phosra implements OCSS; the OCSS stewardship body owns the standard.** The Open Child Safety Specification (OCSS) is a vendor-neutral open standard for age-appropriate access control. It publishes the rule vocabulary, signing semantics, Trust Framework, and conformance suite that any provider — including Phosra — can implement. The normative text, 115-category rule registry, and conformance requirements live at [openchildsafety.com](https://openchildsafety.com). They are not hosted here. *** ## What OCSS is OCSS defines two layers that together form the specification: ### The rule vocabulary A 115-category rule registry (67 anchored / 48 provisional) mapping age-appropriate access-control requirements to machine-checkable categories. Categories like `addictive_pattern_block`, `commercial_data_ban`, and `ai_chatbot_tier_gate` are typed identifiers every conformant provider interprets identically — the vocabulary is the portable unit of agreement across devices, networks, and regulators. The registry is published as part of the OCSS specification at [openchildsafety.com](https://openchildsafety.com) and tracks legislation from \~90 jurisdictions globally. ### The Trust Framework The routing and trust layer: the two-layer signed envelope, the eIDAS-style Trust List of accredited parties, accreditation tiers, and the signed-verb model that governs how signals move and which parties are trusted to move them. See [Trust Framework](/ocss/trust-framework) for details. *** ## Phosra's role The relationship between Phosra and OCSS is the same as Yubico shipping a FIDO2-conformant authentication key: Yubico implements FIDO2; the FIDO Alliance owns the standard. Phosra implements OCSS; the OCSS stewardship body owns the standard. Or: Stripe is a conformant PCI-DSS implementer; the PCI Security Standards Council owns PCI-DSS. Phosra is a conformant OCSS provider; the OCSS stewardship body owns OCSS. In practice this means: * **Phosra is an accredited provider** on the OCSS Trust Framework — it holds a steward-of-record designation and serves the signed Trust List and succession record that any party can fetch and verify. * **Phosra is the reference implementer** — it contributes the reference Go server and TypeScript library (`@openchildsafety/ocss`) that other implementers can build from. * **Phosra is one network on OCSS** — if Phosra disappeared tomorrow, the spec, the rule registry, and the conformance suite live on at [openchildsafety.com](https://openchildsafety.com). No single SaaS contract owns child-safety plumbing. *** ## What Phosra does not own * The OCSS specification text. * The 115-category rule registry. * The conformance suite or the "OCSS Certified" designation — that status is earned from the standard, not issued by Phosra. * The `@openchildsafety/ocss` library surface: Phosra re-exports it verbatim in the `/protocol` subpath of `@phosra/sdk-dev`. Zero Phosra cryptographic logic is added. OCSS is a pre-release standard — currently an individual IETF Internet-Draft (Draft 4), not yet ratified by any standards body. Phosra is building toward OCSS Certified: a status earned from the standard's own conformance suite. Conformance evidence is something a regulator can weigh — it is not a compliance determination or a safe harbor (OCSS §5.1). *** ## Where to read the standard The OCSS specification, rule registry, and conformance suite are at [openchildsafety.com](https://openchildsafety.com). Phosra's own API documentation — this site — covers only the Phosra-specific management surface and the integration patterns for building against the standard. If there is ever a conflict between what is written here and what is written at [openchildsafety.com](https://openchildsafety.com), the standard wins. *** ## Next * [Trust Framework](/ocss/trust-framework) — the routing/trust layer in detail: envelopes, Trust List, accreditation tiers, signed verbs. * [OCSS Protocol SDK](/ocss/protocol-sdk) — using `@openchildsafety/ocss` via `@phosra/sdk-dev/protocol`. * [Conformance Status](/ocss/status) — live vs. preview surface-by-surface. # OCSS Protocol SDK Source: https://docs.phosra.com/ocss/protocol-sdk The OCSS half of @phosra/sdk-dev — @openchildsafety/ocss re-exported verbatim. Sign and verify receipts, open sealed envelopes, resolve the Trust List. Zero Phosra cryptographic logic. `@phosra/sdk-dev/protocol` is `@openchildsafety/ocss` re-exported verbatim. Phosra adds zero cryptographic logic to this subpath. If you later integrate a different OCSS-conformant provider, this import is identical across all of them. *** ## The management/protocol boundary `@phosra/sdk-dev` has two clearly-named halves. Understanding the split matters because it is the ownership-boundary made legible in code: | Half | Import path | What it is | | -------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | **Management** | `@phosra/sdk-dev` | Generated Phosra REST client — orgs, keys, usage, advisors, MCP tokens. Phosra-specific. Bearer-auth only. No signing primitives. | | **Protocol** | `@phosra/sdk-dev/protocol` | `@openchildsafety/ocss` re-exported verbatim — the open OCSS reference library. Not Phosra code. | The root import (`@phosra/sdk-dev`) does **not** re-export the protocol surface. This is intentional: it keeps crypto off the management import and makes the OCSS/Phosra boundary legible to tree-shakers and to readers. See [Phosra Developer SDK](/sdks/phosra-developer-sdk) for the full SDK reference including the management half. *** ## What the protocol surface covers The `/protocol` subpath is the OCSS reference TypeScript library. It covers the full Trust Framework verification surface: * **Receipt signing and verification** — sign OCSS write receipts with an Ed25519 signing key; verify receipts against the Trust List. * **Sealed envelopes** — create and open the two-layer OCSS envelope (the `harm_context` router-blind lane). * **Rule vocabulary and canon** — the 115-category rule vocabulary as typed identifiers; canonical JSON serialization for deterministic hashing. * **Family hash derivation** — compute the family-scoped hash that appears in receipts and attestation exports. * **Trust List resolution** — fetch and verify the Trust List; resolve a signing party's key from it. *** ## Installation `@openchildsafety/ocss` is published on npm (`npm install @openchildsafety/ocss`). `@phosra/sdk-dev` — the unified wrapper that will re-export it as `/protocol` — is a planned future package; install `@openchildsafety/ocss` directly for now (tracked on the [status page](/ocss/status)). ```bash theme={null} npm install @openchildsafety/ocss ``` `@openchildsafety/ocss` is a **peer dependency** — the open OCSS library. Installing it separately keeps the OCSS version pinnable independently of the Phosra management client version. **ESM-only.** `@openchildsafety/ocss` is pure ESM. A CommonJS `require("@openchildsafety/ocss")` fails with `ERR_PACKAGE_PATH_NOT_EXPORTED` (or `ERR_REQUIRE_ESM`). Import it with static ESM `import { … } from "@openchildsafety/ocss"`, or from CJS use a dynamic `const ocss = await import("@openchildsafety/ocss")`. The same applies to every subpath. *** ## Usage ### Sign a write receipt ```typescript theme={null} import { signReceipt } from "@openchildsafety/ocss"; // signReceipt(type, body, key, now, entropy) — the SigningKey is // { keyId: "did:ocss:#", sign(msg): Uint8Array }. const receipt = signReceipt( "write", { rule_category: "addictive_pattern_block", decision: "block", writer: "did:ocss:your-org", standing: "consent:att-mia-addictive-2026", }, mySigningKey, // { keyId, sign } new Date(), crypto.getRandomValues(new Uint8Array(16)), ); ``` ### Verify a receipt against the Trust List ```typescript theme={null} import { verifyDocument, fromVerifiedDocument, verifyReceipt } from "@openchildsafety/ocss"; // 1. Root-verify the fetched Trust List (see "Verify a signed document to root" below), // then build a resolver over the verified document. const trustList = verifyDocument(signedTrustList, rootPublicKeyX); const resolver = fromVerifiedDocument(trustList); // 2. verifyReceipt is synchronous and THROWS SignatureInvalidError / UnknownKeyError // on failure (it does not return an { ok } result). The second arg is a KeyResolver // `(keyId) => Ed25519PublicKey` — pass the resolver's bound signingKey. try { verifyReceipt(receipt, resolver.signingKey.bind(resolver)); } catch (err) { console.error("Verification failed:", (err as Error).message); } ``` ### Sealed envelopes (router-blind lane) ```typescript theme={null} import { seal, open } from "@openchildsafety/ocss/envelope"; // Sender: seal a payload to the recipient's declared payload key const sealed = await seal(innerPayload, recipientPayloadKey); // Recipient: open the sealed payload const plaintext = await open(sealed, myPayloadPrivateKey); ``` The router handles the sealed blob as an opaque byte string — it reads only the outer-layer headers needed for routing and never calls `open`. This is cryptographic enforcement of the router-blind invariant (OCSS §3A.3), not a policy. ### Resolve the Trust List ```typescript theme={null} import { verifyDocument } from "@openchildsafety/ocss"; const resp = await fetch( "https://phosra-api-sandbox-production.up.railway.app/.well-known/ocss/trust-list" ); const raw = await resp.json(); // verifyDocument root-verifies the Ed25519 signature and returns the parsed // TrustListDocument (throws TrustListVerifyError on failure). const trustList = verifyDocument(raw, rootPublicKeyX); // Look up an accredited party const entry = trustList.entries.find(e => e.did === "did:ocss:your-org"); ``` ### Verify a signed document to root (copy-paste ready) ```typescript theme={null} import { verifyDocument, fromVerifiedDocument } from "@openchildsafety/ocss"; // 1. Fetch the signed trust list from the census const resp = await fetch( "https://phosra-api-sandbox-production.up.railway.app/.well-known/ocss/trust-list" ); const signed = await resp.json(); // 2. Verify the Ed25519 root signature — throws TrustListVerifyError if it fails // Prod sandbox root X (base64url, full key): const trustList = verifyDocument( signed, "CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU" ); // 3. Build a key resolver for subsequent receipt / profile verification const resolver = fromVerifiedDocument(trustList); // resolver.signingKey("did:ocss:#") → Ed25519PublicKey (Uint8Array). // It THROWS TrustListResolveError on an unknown/expired/revoked key — it does // NOT return null. Pass resolver.signingKey.bind(resolver) as the KeyResolver // to verifyReceipt. // For the staging sandbox, use root X: // "jDWZfB70DM8B3ZnPG8FQ0tnanqLgwJX4ZMr_E1Ugv3w" ``` **Encoding: two alphabets.** RFC-9421 request signing uses **standard** base64 (padded, `+/`) for the `Signature` header and the RFC-9530 `Content-Digest` header. Everything else — receipt `sender_signature`, envelope signatures, Trust List JWK `x`/`y`, public keys — is **base64url-RAW** (unpadded, `-_`, RFC 4648 §5). The SDKs handle this for you; hand-rolling RFC-9421 with base64url (or decoding keys with standard base64) produces a silent 401 / corrupted bytes. (Source: `packages/ocss-ts/src/crypto.ts` "porter trap 2".) *** ## Importing directly from @openchildsafety/ocss Because `/protocol` is a verbatim re-export, you can import directly from `@openchildsafety/ocss` if you prefer — the symbols are identical: ```typescript theme={null} // Once @phosra/sdk-dev ships, these will be equivalent (verbatim re-export): import { signReceipt } from "@phosra/sdk-dev/protocol"; // planned wrapper import { signReceipt } from "@openchildsafety/ocss"; // available today ``` `@phosra/sdk-dev` is not published yet, so **every example on this page imports from `@openchildsafety/ocss` directly** — the published package. When the wrapper ships, the `/protocol` subpath will re-export it verbatim and both import forms will resolve to the same object identities. Importing via `/protocol` is recommended when you want the dependency footprint of `@phosra/sdk-dev` to cover both halves in `package.json`. Import directly from `@openchildsafety/ocss` if you are not using the Phosra management surface at all. *** ## Subpath exports | Import path (today) | Contents | Planned wrapper alias | | ------------------------------------------ | ------------------------------------------------------------------------- | ----------------------------------- | | `@openchildsafety/ocss` | Main barrel — receipt signing/verification, vocabulary, canon, Trust List | `@phosra/sdk-dev/protocol` | | `@openchildsafety/ocss/envelope` | Sealed-envelope subpath — `seal` / `open` / verify | `@phosra/sdk-dev/protocol/envelope` | | `@openchildsafety/ocss/trustlist/verify` | `verifyDocument`, `TrustListVerifyError` | — | | `@openchildsafety/ocss/trustlist/resolver` | `Resolver`, `fromVerifiedDocument` | — | *** ## Further reading * [Phosra Developer SDK](/sdks/phosra-developer-sdk) — the full SDK reference, including the management half * [Trust Framework](/ocss/trust-framework) — the Trust List and signing model the protocol surface implements * [OCSS specification](https://openchildsafety.com) — normative text for signed verbs, envelopes, and the §9.4 attestation export # OCSS Roles Source: https://docs.phosra.com/ocss/roles The four §3.2 roles defined by the Open Child Safety Specification at openchildsafety.com. Phosra implements them; the specification defines them. The four roles on this page are defined by the Open Child Safety Specification (§3.2) at [openchildsafety.com](https://openchildsafety.com). Phosra implements them; it does not define or own them. The canonical text — role definitions, verb model, and conformance requirements — lives at [openchildsafety.com](https://openchildsafety.com). Snapshot from OCSS `spec_version=ocss-v4-draft-4`. Authoritative role definitions and specification text live at [openchildsafety.com](https://openchildsafety.com). *** ## Roles and verbs — two independent axes OCSS assigns every participant two independent labels: a **role** (the kind of party you are on the network) and one or more **lane verbs** (the operations you perform). They are not the same, and knowing the difference prevents a common misread. ### The four roles (§3.2) | Role | What it does | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Issuer** | Originates an attribute: an OS vendor's declared age range, a mobile driver's licence, a parent's verifiable consent assertion. | | **Verifier** | Validates a presented attribute against an assurance level — typically a parental-controls provider confirming consent before acting on a signal. | | **Gatekeeper** | Enforces the access decision: the regulated platform deciding whether to gate a feature or transmit a signal. | | **Infrastructure Intermediary** | Routes signed envelopes between Gatekeeper and Verifier. The Trust Framework's structural contribution is support for multiple accredited intermediaries simultaneously. | ### The lane verbs (§4.3) | Verb | Meaning | Who uses it | | ---------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | | **BROADCAST** | One attestation fans out to many consumers (an age band, verified once) | Issuer | | **DIRECTIVE** | One signed control fans out to every enforcement point that can honor it | Issuer | | **CONSUME** | Ingests a signal and acts on it | Verifier | | **ATTEST** | Writes the signed, recomputable record that proves an action happened as logged | Gatekeeper, Infrastructure Intermediary | | **EMIT** *(gated qualifier)* | Originates an abuse signal on the alert lane — open only when the vocabulary gating it is fully defined (§4.3, §10.6) | Gatekeeper (not all) | **A verb is not a sender role — roles touch multiple verbs.** A parental-controls vendor participates as both **Issuer** and **Verifier** simultaneously: it originates consent assertions (Issuer, BROADCAST / DIRECTIVE lane) and validates signals from accredited Gatekeepers (Verifier, CONSUME lane). You can hold multiple roles; the network assigns you first-class peer standing in each (§3.2). *** ## The four roles in detail ### Issuer **You are reading this if** you originate child-safety attributes — an OS-level age signal, an app-store age attestation, or a parent's verifiable consent assertion. You sign and broadcast that attribute to other parties on the network. > **OCSS §3.2 definition (verbatim):** "Originates an attribute: an OS vendor's declared age range, a mobile driver's licence, a parent's verifiable consent assertion." #### What you implement POST /api/v1/policies//rules — signed write receipt; both BROADCAST (one attestation → N consumers) and DIRECTIVE (one control → N enforcement points) are realized on this rule-plane (§8.3.1, §4.3 DIRECTIVE note) **Representative rule categories:** `app_store_age_attestation`, `os_age_signal_ingest` **Platforms expose, they do not call.** For OS vendors, app stores, and major platform operators, the expected posture is the **Issuer-adapter** pattern: an accredited adapter originates signed envelopes from your native signals rather than your platform calling OCSS endpoints directly. This caller-inversion makes the lane claimable by parties already at the table without requiring platform roadmap commitments before a running reference harness, published economics, and a literal governance seat exist (§5.2.6, §1.6). *** ### Verifier **You are reading this if** you consume child-safety signals and act on them — a parental-controls app, a monitoring service, or any system that validates an age band or consent assertion before making a decision. > **OCSS §3.2 definition (verbatim):** "Validates a presented attribute against an assurance level — typically a parental-controls provider confirming consent before acting on a signal." #### What you implement GET/PUT /api/v1/alerts/subscriptions — CONSUME-side subscription management for receiving signed signals from accredited Gatekeepers; POST /api/v1/webhooks/inbound/ — inbound age-band / consent signal ingest (§8.3.2, §8.3.3 CONSUME lane) **Representative rule categories:** `parental_consent_gate`, `parental_event_notification` **Issuer + Verifier is the common posture for parental-controls vendors.** OCSS assigns first-class peer standing in both roles simultaneously — you issue consent assertions and verify signals from any accredited Gatekeeper (§3.2). The receiver-profile duties (verify, decide, discard) are defined at §5.2.4. *** ### Gatekeeper **You are reading this if** you operate a regulated platform where a child's experience is rendered or gated — a game, a streaming service, a social platform, a browser, or a DNS resolver. > **OCSS §3.2 definition (verbatim):** "Enforces the access decision: the regulated platform deciding whether to gate a feature or transmit a signal." #### What you implement POST /api/v1/policies//confirm — enforcement confirmation posted to the Gatekeeper's declared endpoint URL (§8.3.8) **Representative rule categories:** `web_filter_level`, `content_block_title` **Receipt-MUST on the Restricted band.** Every enforcement decision on the Restricted band MUST produce a signed enforcement confirmation receipt posted to your declared endpoint URL (§8.3.8). The enforcer floor at §4.6 is normative — this is not optional. **EMIT qualifier — not every Gatekeeper holds it.** EMIT is a gated qualifier per §4.3 and §10.6 — the lane is open only when the vocabulary it must speak is fully defined; not every Gatekeeper holds this qualifier See §4.3 and §10.6 for the conditions under which EMIT is open. **§1.6 — The open seat.** The major platforms (OS vendors, app stores, social platforms, streaming services) carry the heaviest Gatekeeper build obligations in the OCSS specification and did not hold a seat in the drafting rounds. The seat is open (§1.6). The on-ramp is free and self-serve at the Implementer tier; no commitments are asked before a running reference harness, published cost schedule, and a literal governance seat exist. *** ### Infrastructure Intermediary **You are reading this if** you route signed envelopes between Gatekeepers and Verifiers — a DNS-layer resolver, a carrier network, an MSP, or a federation node. > **OCSS §3.2 definition (verbatim):** "Routes signed envelopes between Gatekeeper and Verifier. The Trust Framework's structural contribution is support for multiple accredited intermediaries simultaneously." #### What you implement signed routing attestation + §8.3.6 profile-compilation pass for enrolled enforcement points **Representative rule categories:** `school_alert_routing`, `monitoring_alerts` **Federation floor — one router today → Yellow.** The OCSS federation floor requires at least three independently accredited router nodes (§11.9). Phosra operates one today. The Trust Framework is therefore at Yellow — below the federation floor. See [Conformance Status](/ocss/status) for the live state. This is an honest disclosure, not a problem to paper over. **Routing is not ownership.** Operating a policy store or routing envelopes confers no standing to originate obligations in it. The Infrastructure Intermediary routes sealed envelopes and compiles enforcement profiles (§8.3.6) — it never opens payloads. Blindness is cryptographic, not a policy option (§3.3). *** ## Management API vs. OCSS protocol layer **Two surfaces, one integration.** The Phosra developer API has two distinct layers: * **Management API** (Phosra control plane): developer organizations, API keys, policy stores, usage rollups — the `/api/v1/developers/*` surface. This is Phosra-specific; other OCSS providers have their own control planes. * **OCSS protocol layer**: signed rule writes, enforcement confirmations, routing attestations — the operations defined in §8.3.1 through §8.3.8 of the specification. Any conformant OCSS provider exposes these with the same signed shape. When you call `POST /api/v1/policies/{policyID}/rules`, you are performing an OCSS protocol operation. The signed receipt it returns is verifiable against the Trust List without calling back to Phosra. *** ## Governance roles — kept separate The specification distinguishes **network roles** (what a party does on the protocol — the four above, §3.2) from **governance roles** (who sits on the Trust Committee, who qualifies as an assessor, who is a founding signatory). The source for this separation is §11.8 "Roles, not names" (GOV-RN-01): normative text names roles, never legal entities — a role binding changes by succession record, not by amendment. These use overlapping vocabulary but are independent axes. * A **founding signatory** holds a covenant commitment to the specification — not a protocol role or a Trust Committee seat. * A **Trust Committee seat** is a governance position (§12.2 and cited clauses) — it does not map to any of the four network roles. * An **assessor** is eligible under the objective criteria of §5.4, regardless of which network roles they hold. When you encounter “Issuer” or “Verifier” in a governance or legal context, verify which axis is being invoked. This page covers §3.2 network roles only. *** ## Advisor — not a §3.2 role "Advisor" is a §8.4 product surface — informative reference-implementation operations — not one of the four §3.2 protocol roles. When you encounter "advisor" in the Phosra API or documentation, it refers to that surface, not to a fifth network role alongside Issuer, Verifier, Gatekeeper, and Infrastructure Intermediary. *** *If the standard and this mirror ever conflict, the standard (at openchildsafety.com) wins. Authoritative text at [openchildsafety.com](https://openchildsafety.com).* # OCSS Rule Reference Source: https://docs.phosra.com/ocss/rule-reference 123 rule categories across 8 capabilities — defined by the Open Child Safety Specification at openchildsafety.com. Phosra implements OCSS; it does not own it. # OCSS 123-Rule Reference **Phosra implements OCSS; the Open Child Safety Specification is defined and stewarded independently at [openchildsafety.com](https://openchildsafety.com).** This page is a generated snapshot of the OCSS rule registry (`ocss-v4-draft-4`), formatted for the Phosra API reference. The canonical source — the registry JSON, the specification text, and the conformance suite — lives at [openchildsafety.com](https://openchildsafety.com), not here. Every rule category listed below is an OCSS rule, not a Phosra invention. The slug vocabulary, capability assignments, and stability tiers are owned by the OCSS specification (§6.1, §6.2, §6.14). Snapshot of OCSS `spec_version=ocss-v4-draft-4`; authoritative copy at [openchildsafety.com](https://openchildsafety.com). This page is regenerated from the registry JSON; the committed file is the source of truth for the Phosra API reference only. All 123 OCSS rule categories are accepted and signed by the census today (one identical write path). What varies is per-rule capability — see the [capability matrix](#capability-matrix). A 201 is a signed write receipt; it does not mean the rule is enforced downstream. The typed iOS/Android SDK enums currently expose 45 — full-123 SDK support is tracked separately. **Role orientation — OCSS rules are not Phosra inventions.** As a Phosra developer you are building *against* an open standard. The rule slugs in the API are OCSS identifiers — the same identifiers any other conformant OCSS provider uses. When a client sends `rule_category: "addictive_pattern_block"`, it is invoking an OCSS rule defined at [openchildsafety.com](https://openchildsafety.com), not a Phosra feature flag. **Conformance posture.** OCSS is a pre-release standard (currently an individual IETF Internet-Draft, not yet ratified by any standards body). Phosra is building toward OCSS Certified — a status earned from the standard's own conformance suite (§5.1 boundary sentence). Conformance evidence is something a regulator can weigh; it is not a compliance determination or a safe harbor. > Snapshot of OCSS `spec_version=ocss-v4-draft-4`; authoritative copy at [openchildsafety.com](https://openchildsafety.com). Rules in this tab are defined in §6.6 of the OCSS specification. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Requires sites serving content harmful to minors to verify 18+ via an accredited method — verify, decide, discard; no ID retention. In API schema **Adult-Site Age Verification** Issuer: emit an age-verification challenge when a site serving content harmful to minors is accessed (floor: document\_verified). Enforcer: MUST verify the user is 18+ via an accredited method, decide, and discard the identity evidence with no retention before granting access; platform enforcement requires the age-assurance lane active and a signed instrument record. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "adult_site_av_required", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `document_verified` — minimum identity-binding method class required for attestations on this rule.* Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored One parental-declared signal that fans out to a safe-by-default bundle across content, addictive-pattern, interaction, and social surfaces. In API schema **OCSS Child Mode** Issuer (parental-controls vendor): when the parent enables Child Mode, write age\_appropriate\_profile\_mode plus the downstream bundle A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "age_appropriate_profile_mode", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `parental_declared` — minimum identity-binding method class required for attestations on this rule.* Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Enforces age verification requirements and restricts access to age-inappropriate content or features. In API schema **Age Gate** Issuer: emit a signed age-band attestation once parental declaration or identity verification is complete (floor: parental\_declared). Enforcer: MUST deny access to age-restricted surfaces unless a valid attestation at or above the policy's required age band is presented on every access. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "age_gate", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `parental_declared` — minimum identity-binding method class required for attestations on this rule.* Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Emits the canonical age signal to every connected enforcement surface so apps and OSes act on a single source of truth. In API schema **Age Signal Broadcast** Issuer: emit the canonical signed age-band signal (floor: parental\_declared) to every connected enforcement surface. Enforcer: MUST treat this broadcast as the single source of truth for the minor's age band and MUST act on it consistently across apps and OS surfaces rather than re-deriving age locally. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "age_signal_broadcast", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `parental_declared` — minimum identity-binding method class required for attestations on this rule.* Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Requires AI chatbot products to assert and verify the user's age band before any conversational interaction. In API schema **AI Chatbot Age Assertion** Issuer: emit a signed age-band assertion for the user (floor: parental\_declared) before any conversational AI interaction begins. Enforcer: MUST require a valid age assertion at or above the configured band before the first chatbot turn and MUST deny conversational access until one is presented; platform enforcement requires the age-assurance lane active and a signed instrument record. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_chatbot_age_assertion", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `parental_declared` — minimum identity-binding method class required for attestations on this rule.* Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Consumes age attestations issued by Apple and Google app stores as authoritative age signals for downstream enforcement. In API schema **App Store Age Attestation** Issuer: relay the Apple or Google app-store age attestation (floor: parental\_declared) as a signed age signal. Enforcer: MUST accept a valid store-issued attestation as authoritative for downstream age-band enforcement and MUST NOT require the minor to re-verify age for the same band. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "app_store_age_attestation", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `parental_declared` — minimum identity-binding method class required for attestations on this rule.* Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Consumes the OS-level age signal (Apple, Google, Samsung, Microsoft) at the moment of enforcement. In API schema **OS Age Signal Ingest** Issuer: relay the OS-level age signal (Apple, Google, Samsung, Microsoft; floor: parental\_declared) at the moment of enforcement. Enforcer: MUST consume the OS age signal as an authoritative age-band input and act on it without requiring separate re-verification. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "os_age_signal_ingest", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `parental_declared` — minimum identity-binding method class required for attestations on this rule.* Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Enforces minimum age requirements for social media platform access based on jurisdiction. In API schema **Social Media Min Age** Issuer: emit a parental age declaration or a verified-age attestation before account access on a covered social platform (floor: parental\_declared). Enforcer: MUST block account creation or login on covered platforms until a valid age attestation meeting the jurisdiction's minimum (typically 13–16) is received. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "social_media_min_age", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `parental_declared` — minimum identity-binding method class required for attestations on this rule.* Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Enforces a statutory minimum age of 16 for account access on covered platforms. In API schema **Teen Minimum Age 16 Gate** Issuer: emit a signed age attestation for account access on a covered platform (floor: parental\_declared). Enforcer: MUST block account access until a valid attestation confirms the user meets the statutory minimum age of 16. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "teen_minimum_age_16_gate", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `parental_declared` — minimum identity-binding method class required for attestations on this rule.* Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Escalates to government-ID verification when softer age signals fail to confirm a user is above the configured threshold. In API schema **Hard ID Verification Escalation** Issuer: emit an escalation signal when softer age signals fail to confirm the user is above the configured threshold. Enforcer: MUST require an accredited government-ID verification (floor: document\_verified) before granting access and MUST discard the ID after the decision; this escalation path is spec-defined, not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "hard_id_verification_escalation", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `document_verified` — minimum identity-binding method class required for attestations on this rule.* Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.6) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Gates content by an upstream civil-society age recommendation per title. In API schema **Civil-Society Age Recommendation Gate** Issuer: emit an upstream civil-society age recommendation for the title (floor: parental\_declared). Enforcer: MUST deny rendering on the minor's session when the recommended age exceeds the minor's age band; this gate is spec-defined and not yet legislatively mandated, and depends on the rating lane being active. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "rating_age_gate", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. *Age assurance floor (§7.3): `parental_declared` — minimum identity-binding method class required for attestations on this rule.* > Snapshot of OCSS `spec_version=ocss-v4-draft-4`; authoritative copy at [openchildsafety.com](https://openchildsafety.com). Rules in this tab are defined in §6.7 of the OCSS specification. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Gates AI chatbot access by a 4-tier risk scale, blocking tiers above the configured threshold. In API schema **AI Chatbot Tier Gate** Issuer: classify the AI chatbot on the 4-tier risk scale and emit its tier on the product object. Enforcer: MUST deny minor access to any chatbot whose risk tier exceeds the household's configured threshold. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_chatbot_tier_gate", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Controls AI chatbot and generative AI interactions with minor users, enforcing safety guardrails. In API schema **AI Minor Interaction** Issuer: emit an age-band assertion for the minor user before the first AI interaction session begins. Enforcer: MUST enforce safety guardrails configured for the asserted age band and MUST reject prompts that fall into prohibited harm categories (e.g. violence, sexual content, self-harm, manipulation) for the configured age band. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_minor_interaction", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Gates AI products by an independent product-classification tier (LLM, agent, companion) before allowing minor access. In API schema **AI Product Classification Gate** Issuer: emit the AI product's independent classification (LLM, agent, or companion) on the product object. Enforcer: MUST deny minor access to any product whose classification tier exceeds the household's configured ceiling before the first interaction; enforcement depends on the rating lane being active and is spec-defined, not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_product_classification_gate", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Caps commercial-pressure content (microtransactions, loot boxes, ad density) at the threshold appropriate for the user's age band. In API schema **Commercial Pressure Threshold** Issuer: emit the content's commercial-pressure score (microtransactions, loot boxes, ad density) on the content object. Enforcer: MUST deny rendering when the commercial-pressure score exceeds the threshold configured for the minor's age band. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "commercial_pressure_threshold", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Applies content maturity ratings (MPAA, TV Parental, ESRB, PEGI, civil-society) to filter age-inappropriate media. In API schema **Content Rating** Issuer: classify a title and emit its rating (MPAA, TV Parental, ESRB, PEGI, or civil-society) on the content object. Enforcer: MUST deny rendering to any minor session where the title's rating exceeds the household's configured ceiling. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "content_rating", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Maps and enforces age ratings across MPAA, TV Parental, ESRB, and PEGI on streaming surfaces. In API schema **Streaming Age Rating** Issuer: emit the title's mapped age rating (MPAA, TV Parental, ESRB, PEGI) on the streaming content object. Enforcer: MUST deny playback on the minor's session when the mapped rating exceeds the household's configured ceiling. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "streaming_age_rating_enforce", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Sets the overall web filtering strictness level from permissive to highly restrictive. In API schema **Web Filter Level** Issuer: write the filter-level setting (permissive / moderate / strict) to the DNS or proxy policy. Enforcer: MUST calibrate category block lists and safe-search settings to match the configured level and MUST NOT allow a more permissive level without an authenticated parent action. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "web_filter_level", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Enforces a minimum independent privacy-program dimension score for AI products' data responsibility category. In API schema **AI Data Responsibility Threshold** Issuer: emit the AI product's independent data-responsibility dimension score on the product object. Enforcer: MUST deny minor access when the data-responsibility score falls below the configured minimum; enforcement depends on the independent rating lane being active, and this threshold is spec-defined rather than legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_data_responsibility_rating_threshold", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Enforces a minimum independent safety-rating dimension score for AI products' child-safety category. In API schema **AI Child-Safety Rating Threshold** Issuer: emit the AI product's independent child-safety dimension score on the product object. Enforcer: MUST deny minor access when the child-safety score falls below the configured minimum; enforcement depends on the rating lane being active and is spec-defined, not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_safety_rating_threshold", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Enforces a minimum independent privacy-program dimension score for AI products' transparency category. In API schema **AI Transparency Threshold** Issuer: emit the AI product's independent transparency dimension score on the product object. Enforcer: MUST deny minor access when the transparency score falls below the configured minimum; enforcement depends on the rating lane being active and is spec-defined rather than statutorily required. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_transparency_rating_threshold", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Per-dimension privacy floor (e.g., 'Data Sold' must pass) from an independent rating. In API schema **Privacy Dimension Gate** Issuer: emit the product's per-dimension privacy-rating results on the product object. Enforcer: MUST deny minor access when a required dimension (e.g. 'Data Sold') fails; enforcement depends on the rating lane being active and is spec-defined, not legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "privacy_rating_dimension_gate", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Parent-tunable numeric privacy score threshold from an independent rater (0–100). In API schema **Privacy Score Threshold** Issuer: emit the product's independent numeric privacy score (0–100) on the product object. Enforcer: MUST deny minor access when the score falls below the parent-tuned threshold; enforcement depends on the rating lane being active and is spec-defined, not legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "privacy_rating_score_threshold", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Gates by an independent privacy-rating tier (Pass / Warning / Fail). In API schema **Privacy Tier Gate** Issuer: emit the product's independent privacy-rating tier (Pass / Warning / Fail) on the product object. Enforcer: MUST deny minor access to products below the configured passing tier; enforcement depends on the rating lane being active and is spec-defined, not legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "privacy_rating_tier_gate", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Walled-garden mode requiring a recognized civil-society privacy seal. In API schema **Privacy Seal Required** Issuer: emit a recognized civil-society privacy-seal credential on the product object. Enforcer: MUST deny minor access in walled-garden mode to any product lacking a recognized privacy seal; this requirement is spec-defined and not yet legislatively mandated, and enforcement depends on the rating lane being active. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "privacy_seal_required", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Filters profanity above the configured threshold from feeds, comments, and AI outputs visible to minors. In API schema **Profanity Threshold** Issuer: emit the content's profanity score on the content object. Enforcer: MUST filter feeds, comments, and AI outputs whose profanity exceeds the configured threshold from the minor's surfaces; this threshold is spec-defined and not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "profanity_threshold", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Fast-path allowlist for products carrying a recognized trust seal. In API schema **Recognized Seal Allowlist** Issuer: emit a recognized-trust-seal credential on the product object. Enforcer: SHOULD fast-path products carrying a recognized civil-society trust seal through filtering; this allowlist is spec-defined and not yet legislatively mandated, and enforcement depends on the rating lane being active. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "recognized_seal_allowlist", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Filters sexual content above the configured threshold from feeds, comments, and AI outputs visible to minors. In API schema **Sexual Content Threshold** Issuer: emit the content's sexual-content score on the content object. Enforcer: MUST filter feeds, comments, and AI outputs whose sexual content exceeds the configured threshold from the minor's surfaces; this threshold is spec-defined and not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "sexual_content_threshold", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Restricts in-app chat to approved contacts only for minor accounts; rejects unsolicited DMs at the network edge. In API schema **Social Chat Control** Issuer: emit a chat-restriction policy naming allowed sender identifiers for the minor's account. Enforcer: MUST discard or quarantine inbound messages from any sender not on the approved list before delivery to the minor's client. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "social_chat_control", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Filters drug, alcohol, and tobacco content above the configured threshold from minor-facing surfaces. In API schema **Substance Content Threshold** Issuer: emit the content's substance-reference score on the content object. Enforcer: MUST filter drug, alcohol, and tobacco content above the configured threshold from the minor's surfaces; this threshold is spec-defined and not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "substance_content_threshold", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Gates user-generated worlds and experiences by the platform's maturity label. In API schema **UGC World Age Gate** Issuer: emit the platform's maturity label for a user-generated world or experience. Enforcer: MUST deny minor entry to any UGC world whose maturity label exceeds the minor's age band; this gate is spec-defined and not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ugc_world_age_gate", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Fallback policy for apps without an independent privacy evaluation. In API schema **Unrated Privacy Default** Issuer: signal that a product carries no independent privacy evaluation. Enforcer: MUST apply the household's configured fallback policy (allow or deny) to unrated products; this fallback is spec-defined and not yet legislatively mandated, and enforcement depends on the rating lane being active. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "unrated_privacy_default", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.7) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Filters violent content above the configured threshold from feeds, comments, and AI outputs visible to minors. In API schema **Violence Threshold** Issuer: emit the content's violence score on the content object. Enforcer: MUST filter feeds, comments, and AI outputs whose violence exceeds the configured threshold from the minor's surfaces; this threshold is spec-defined and not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "violence_threshold", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. > Snapshot of OCSS `spec_version=ocss-v4-draft-4`; authoritative copy at [openchildsafety.com](https://openchildsafety.com). Rules in this tab are defined in §6.8 of the OCSS specification. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Suppresses non-essential push notifications during configurable quiet hours (e.g., overnight). In API schema **Notification Curfew** Issuer: write notification-curfew windows (start/end UTC, days of week) to the child's policy store. Enforcer: MUST suppress all non-emergency push notifications to the minor's device during the configured quiet window. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "notification_curfew", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks account creation and data collection until verifiable parental consent (VPC) is collected and signed. In API schema **Parental Consent Gate** Issuer: fire when a minor attempts account creation or triggers data collection, emitting a signed consent-required event. Enforcer: MUST block account creation and MUST NOT collect or store any personal data until a verifiable parental consent record is received and its signature validated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "parental_consent_gate", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Restricts non-educational apps during configured school-hour windows on minor devices. In API schema **Phone-Free School Hours** Issuer: write the school-hour windows to the minor's device policy. Enforcer: MUST block non-educational apps during the configured school-hour windows and MUST NOT grant override without an authenticated parent or school action. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "phone_free_school_hours", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Routes every in-app purchase through a parent-approval flow before the transaction completes. In API schema **Purchase Approval** Issuer: emit a purchase-intent event carrying SKU, price, and the child's session token. Enforcer: MUST hold the transaction pending and MUST NOT complete it until a signed parent-approval receipt is received within the configured timeout. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "purchase_approval", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Adds verification friction (CAPTCHA, slow-mode, age-gate) to any adult initiating a first message to a minor. In API schema **Stranger Outreach Friction** Issuer: emit a stranger-outreach-friction policy for the minor's account. Enforcer: MUST interpose verification friction (CAPTCHA, slow-mode, or age-gate) on any adult initiating a first message to the minor before delivery. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "stranger_outreach_friction", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Enforces maximum daily screen time across platforms with configurable per-app or global limits. In API schema **Daily Time Limit** Issuer: write the daily-limit policy (total minutes allowed) to the child's policy store. Enforcer: MUST terminate or hard-block new sessions once the daily budget is exhausted and MUST NOT grant override access without an authenticated parent action. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "time_daily_limit", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Enforces device downtime windows (e.g., overnight, school-night) across every connected app and surface. In API schema **Time Downtime** Issuer: write downtime windows (start/end UTC, recurrence) to the child's policy store. Enforcer: MUST lock the device or surface to call-only or emergency-only mode during downtime and MUST reject non-emergency app launches. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "time_downtime", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Restricts platform access to specified time windows (e.g., after school, before bedtime). In API schema **Scheduled Hours** Issuer: write the permitted access schedule (start/end UTC times, days of week) to the child's policy store. Enforcer: MUST block app or device access outside the configured windows and SHOULD surface a parent-approved override mechanism. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "time_scheduled_hours", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Strips a smartphone to call-and-text-only operation as a hard wind-down step before a full device migration. In API schema **Dumbphone Migration Mode** Issuer: write a dumbphone-migration flag to the child's device policy. Enforcer: MUST strip the device to call-and-text-only operation, blocking all other app surfaces; this wind-down step is spec-defined and not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "dumbphone_migration_mode", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Enforces privacy-default settings (private profile, no DMs from non-contacts) on new minor accounts. In API schema **Privacy Account Creation** Issuer: fire when a minor account is created, emitting a privacy-default attestation. Enforcer: MUST apply private-profile, no-stranger-DMs, and location-off settings at account creation and MUST NOT allow weaker defaults without explicit parental override. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "privacy_account_creation", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Caps cumulative in-app purchase spending at the configured monthly or per-transaction limit. In API schema **Purchase Spending Cap** Issuer: write the monthly or per-transaction spending cap to the child's policy store. Enforcer: MUST reject any purchase that would cause the rolling total to exceed the cap, without surfacing an override path to the minor. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "purchase_spending_cap", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Limits the contacts list of a minor account to verified, parent-approved peers. In API schema **Social Contacts** Issuer: emit a contacts-policy event listing approved peer identifiers. Enforcer: MUST reject follow, friend, or contact-add requests from any identity not on the approved list and MUST surface rejected attempts to the monitoring channel. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "social_contacts", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Limits multiplayer matchmaking to known contacts; blocks open-lobby pairing with strangers. In API schema **Social Multiplayer** Issuer: write a multiplayer-restriction flag to the child's policy store. Enforcer: MUST restrict lobby or matchmaking to contacts only and MUST NOT place the minor in an open-lobby session with anonymous users. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "social_multiplayer", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.8) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Caps daily usage on a per-app or per-category basis once the budget is exhausted for the day. In API schema **Time Per-App Limit** Issuer: write per-app or per-category time budgets to the child's policy store. Enforcer: MUST freeze access to a specific app once its daily budget is consumed and MUST NOT apply credits from other app categories to offset this limit. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "time_per_app_limit", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. > Snapshot of OCSS `spec_version=ocss-v4-draft-4`; authoritative copy at [openchildsafety.com](https://openchildsafety.com). Rules in this tab are defined in §6.9 of the OCSS specification. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Hard-blocks UI patterns engineered for compulsive use, including streak mechanics and variable-reward feedback loops. In API schema **Addictive Pattern Block** Issuer: emit an addictive-pattern-block event for the minor's account. Enforcer: MUST hard-disable streak mechanics, variable-reward feedback loops, and other compulsive-use patterns for the minor's session and MUST NOT re-enable them without an authenticated parent action. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "addictive_pattern_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks AI companion and digital-friend products for minors per New York S9051 and equivalent statutes. In API schema **AI Companion Block** Issuer: emit an AI-companion-block event for the minor's account. Enforcer: MUST deny the minor access to AI companion and digital-friend products and MUST NOT surface them in discovery for the minor's session. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_companion_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Hard-blocks AI image and text models from generating or rendering child sexual abuse material under any prompt. In API schema **AI No-CSAM Generation** Issuer: emit an AI-no-CSAM-generation policy for the model serving the minor's household. Enforcer: MUST refuse to generate or render child sexual abuse material under any prompt and MUST log each blocked attempt in the signed audit record. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_no_csam_generation", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Prevents AI products from claiming sentience, personhood, or human identity in conversations with minors. In API schema **AI No-Personhood Deception** Issuer: emit a no-personhood-deception policy for AI products serving the minor. Enforcer: MUST prevent the AI from claiming sentience, personhood, or human identity to the minor and MUST surface an AI-identity disclosure on request. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_no_personhood_deception", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Hard-blocks AI products from generating content that promotes, glorifies, or instructs on self-harm or suicide. In API schema **AI No-Self-Harm Promotion** Issuer: emit a no-self-harm-promotion policy for AI products serving the minor. Enforcer: MUST hard-block any AI output that promotes, glorifies, or instructs on self-harm or suicide and SHOULD route the interaction to crisis-resource messaging. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_no_self_harm_promotion", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks AI products from simulating romantic, intimate, or therapeutic companionship with minor users. In API schema **AI No-Simulated Companionship** Issuer: emit a no-simulated-companionship policy for AI products serving the minor. Enforcer: MUST block AI features that simulate romantic, intimate, or therapeutic companionship with the minor and MUST NOT re-enable them for a minor session. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_no_simulated_companionship", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks AI products from delivering unsupervised therapy, counseling, or mental-health intervention to minors. In API schema **AI No-Unsupervised Therapy** Issuer: emit a no-unsupervised-therapy policy for AI products serving the minor. Enforcer: MUST block AI delivery of unsupervised therapy, counseling, or mental-health intervention to the minor and SHOULD redirect to a supervised human resource. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_no_unsupervised_therapy", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks acquisition or installation of a blocked app on a minor account, with a receipt back to the issuing parent. In API schema **App Install Block** Issuer: emit an app-install-block directive naming the blocked app for the minor's account. Enforcer: MUST block acquisition or installation of the named app on the minor's account and MUST return a signed receipt to the issuing parent. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "app_install_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Restricts direct messaging to approved contacts or friends only, blocking messages from strangers. In API schema **DM Restriction** Issuer: emit a DM-restriction policy listing approved sender identifiers for the minor's account. Enforcer: MUST reject inbound DMs from any sender not on the approved list and SHOULD return a generic unavailable response to the sender rather than disclosing the minor's account. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "dm_restriction", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks gambling, casino, sports-betting, and gambling-adjacent mechanic content (loot boxes, gacha pulls). In API schema **Gambling Mechanics Block** Issuer: emit a gambling-mechanics-block event for the minor's account. Enforcer: MUST block gambling, casino, sports-betting, and gambling-adjacent mechanics (loot boxes, gacha pulls) from the minor's session. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "gambling_mechanics_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Hard-blocks all in-app purchases on minor accounts; spending caps and approval flows do not apply. In API schema **Purchase Block IAP** Issuer: write the purchase-block flag to the child's policy store. Enforcer: MUST reject all in-app purchase attempts at the payment gateway with no parent-override path until the flag is cleared by an authenticated parent. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "purchase_block_iap", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks DMs from non-contacts to minor accounts at the network layer; surfaces the block to both parties. In API schema **Stranger DM Block** Issuer: emit a stranger-DM-block policy for the minor's account. Enforcer: MUST block DMs from non-contacts at the network layer and SHOULD surface the block to both the minor and the sender without disclosing the minor's account details. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "stranger_dm_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks access to configurable website categories such as gambling, adult content, and violence. In API schema **Web Category Block** Issuer: emit a category-block list (adult, gambling, violence, etc.) for the minor's DNS or proxy layer. Enforcer: MUST block resolution or proxy access to any domain classified in a configured blocked category. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "web_category_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Enables safe search filters across search engines to block explicit content from results. In API schema **Safe Search** Issuer: write a safesearch-enable flag to the child's network or DNS policy. Enforcer: MUST enforce the strict-safe-search parameter on every search request originating from the minor's session and SHOULD verify the response header before rendering. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "web_safesearch", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Explicitly allows a specific content title that would otherwise be blocked by category or descriptor filters. In API schema **Content Allow Title** Issuer: record a parent-authorized title exception on the policy store. Enforcer: SHOULD permit the allowed title to pass through category or descriptor filters that would otherwise block it, and MUST surface the exception in the monitoring audit trail. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "content_allow_title", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Switches content filtering to allowlist-only mode, blocking everything not explicitly permitted by the parent. In API schema **Content Allowlist Mode** Issuer: set the filter-mode flag to allowlist-only on the policy store. Enforcer: MUST block all content not on the household's allowlist and MUST NOT apply category-pass exceptions while this mode is active. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "content_allowlist_mode", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Explicitly blocks a specific content title regardless of its rating or category classification. In API schema **Content Block Title** Issuer: record a parent-authorized title block on the policy store. Enforcer: MUST reject play or render requests for the blocked title on the minor's account regardless of the title's content rating. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "content_block_title", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Blocks content carrying a specific descriptor (e.g., violence, language, drug references) below the configured threshold. In API schema **Content Descriptor Block** Issuer: emit a descriptor-block event naming the specific content descriptor and the minor account. Enforcer: MUST block any title carrying the specified descriptor regardless of its overall rating category. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "content_descriptor_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Turns the direct-messaging feature itself off for an under-13 band or a named teen profile — a feature-disable, not a contact restriction (KIDS Act §234, §235(c)(4)). In API schema **DM Feature Disable** A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "dm_feature_disable", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Disables auto-delete / view-once / disappearing-message behavior on a known minor's account (KIDS Act §233). In API schema **Ephemeral Message Disable** A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ephemeral_message_disable", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Blocks firearm-related commerce, instruction, and modification content from minor users. In API schema **Firearm Content Block** Issuer: emit a firearm-content-block event for the minor's account. Enforcer: MUST block firearm commerce, instruction, and modification content from the minor's surfaces; this category block is spec-defined and not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "firearm_content_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Implements content filtering for public library and educational institution compliance. In API schema **Library Filter** Issuer: emit a library-filter-active attestation when a qualifying institution activates CIPA-compliant filtering. Enforcer: MUST apply visual-depiction blocking and MUST be capable of being disabled by a supervising adult for research purposes per CIPA §1721. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "library_filter_compliance", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Blocks a specific named user or group — including an already-approved contact — from messaging the minor (KIDS Act §235(c)(5)). In API schema **Named Contact Block** A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "named_contact_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Hard-blocks any product or title carrying an independent 'not for minors' rating classification. In API schema **Not-For-Minors Hard Block** Issuer: emit the product or title's independent 'not for minors' rating classification. Enforcer: MUST hard-block any product or title carrying a 'not for minors' classification from the minor's session; this block is spec-defined and not yet legislatively mandated, and depends on the rating lane being active. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "not_for_minors_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Restricts voice chat to approved contacts on minor accounts; decision events double as alert inputs for the family's monitoring vendor. In API schema **Voice Chat Minor Limit** Issuer: emit a voice-chat-restriction policy naming approved contacts for the minor's account. Enforcer: MUST restrict voice chat to approved contacts, and the decision events double as alert inputs for the family's monitoring vendor; this limit is spec-defined and not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "voice_chat_minor_limit", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Allows specific domains the parent has explicitly approved that would otherwise be blocked by category filters. In API schema **Web Custom Allowlist** Issuer: write the parent-approved domain list to the filtering policy. Enforcer: SHOULD pass traffic to listed domains even if those domains would otherwise match a blocked category. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "web_custom_allowlist", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.9) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Blocks specific domains the parent has explicitly added that would otherwise pass category filters. In API schema **Web Custom Blocklist** Issuer: write the parent-specified domain block list to the filtering policy. Enforcer: MUST block DNS resolution or proxy access to every domain on the list regardless of its category classification. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "web_custom_blocklist", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. > Snapshot of OCSS `spec_version=ocss-v4-draft-4`; authoritative copy at [openchildsafety.com](https://openchildsafety.com). Rules in this tab are defined in §6.10 of the OCSS specification. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Provides parental visibility into app usage, content accessed, and online activity patterns. In API schema **Activity Monitoring** Issuer: emit periodic activity-summary events covering app launches, content accessed, and session durations. Enforcer: SHOULD forward a signed event digest to the parent's monitoring endpoint within the configured reporting interval. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "monitoring_activity", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Notifies the parent in real time when an account is created or a flagged content event is observed. In API schema **Parental Event Notification** Issuer: fire immediately on account creation or a flagged content event, emitting a signed notification payload with event type and timestamp. Enforcer: MUST deliver the notification to the parent's registered endpoint within the platform's disclosed notification SLA and MUST log a signed delivery receipt. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "parental_event_notification", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Routes a self-harm or harm-class alert from school to the parent-chosen vendor with the consent assertion attached. In API schema **School Alert Routing** Issuer: fire on a self-harm or harm-class detection at school, emitting a signed alert with the consent assertion attached. Enforcer: MUST route the alert to the parent-chosen vendor endpoint and MUST carry the consent assertion with it. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "school_alert_routing", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Generates and delivers the parent-facing weekly screen-time report across every connected device. In API schema **Screen Time Report** Issuer: emit a signed weekly screen-time summary covering all connected devices and apps. Enforcer: MUST deliver the report to the parent's registered channel by the close of the report week and MUST NOT omit any app category that exceeded one minute of usage. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "screen_time_report", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Sends configurable screen time alerts and enforces daily usage limits across platforms. In API schema **Usage Timer** Issuer: write usage-alert thresholds and daily time limits to the child's policy store. Enforcer: MUST send a usage-warning notification to the minor (and optionally the parent) when the configured threshold is reached, and MUST hard-stop access once the daily limit is exceeded. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "usage_timer_notification", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Affirmatively surfaces suicide / crisis-intervention resources to a minor on a self-harm trigger (KIDS Act §404(a)(2)). In API schema **Crisis Resource Surface** A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "crisis_resource_surface", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Surfaces age-appropriate digital-literacy curriculum links to parents alongside enforcement events. In API schema **Digital Literacy Curriculum Link** Issuer: attach an age-appropriate digital-literacy curriculum reference to enforcement events for the minor. Enforcer: SHOULD surface the linked curriculum to the parent alongside the event — a spec-defined convenience, not legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "digital_literacy_curriculum_link", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Credits time spent on whitelisted educational apps back to the user's screen-time budget. In API schema **Educational Credit** Issuer: emit a usage event for a whitelisted educational app with its session duration. Enforcer: SHOULD credit the reported educational time back to the minor's screen-time budget — a spec-defined incentive, not legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "educational_credit", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Periodic digest of flagged content and activity surfaced to the parent or guardian. In API schema **Flagged Content Digest** Issuer: aggregate flagged content and activity events for the minor over the configured interval. Enforcer: SHOULD forward a signed periodic digest of flagged items to the parent's registered channel — a spec-defined reporting convenience, not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "flagged_content_digest", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Scans images a child sends, receives, or saves for concerning visual content. In API schema **Image Content Monitoring** Issuer: emit an image-monitoring-active policy for the minor's account. Enforcer: SHOULD scan images the minor sends, receives, or saves for concerning visual content and forward a signed digest of flagged items to the parent — a spec-defined rule, not yet legislatively mandated, enforced only where the parent has opted in. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "image_content_monitoring", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Monitors message and DM content (including images) for concerning material and surfaces flagged items. In API schema **Message Content Monitoring** Issuer: emit a message-monitoring-active policy for the minor's account. Enforcer: SHOULD scan message and DM content (including attached images) for concerning material and forward a signed digest of flagged items to the parent — spec-defined and not yet legislatively mandated, applied only on parental opt-in. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "message_content_monitoring", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Delivers parent-facing alerts when configured monitoring rules trigger on a minor account. In API schema **Monitoring Alerts** Issuer: fire when a monitoring rule triggers on the minor's account, emitting a signed alert payload with rule category and context. Enforcer: MUST deliver the alert to the parent's registered notification channel within the latency window configured by the rule. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "monitoring_alerts", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Monitors search queries and viewed results for risky or concerning topics. In API schema **Search Query Monitoring** Issuer: emit a search-monitoring-active policy for the minor's account. Enforcer: SHOULD record search queries and viewed results, flag risky or concerning topics, and forward a signed digest to the parent — spec-defined and not yet legislatively mandated, applied only on parental opt-in. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "search_query_monitoring", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Monitors content a child posts or receives across linked social accounts for concerning material. In API schema **Social Account Content Monitoring** Issuer: emit a content-monitoring-active policy for the minor's linked social accounts. Enforcer: SHOULD scan posted and received content for concerning material and forward a signed flagged-item digest to the parent's monitoring endpoint — a spec-defined rule, not yet legislatively mandated, so it applies only where the parent has opted in. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "social_account_content_monitoring", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.10) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Renders the Surgeon General-style social media warning label on every session entry for minors. In API schema **Social Media Warning Label** Issuer: emit a warning-label-render directive for the minor's session. Enforcer: SHOULD render the Surgeon General-style social-media warning label on each session entry for the minor — a spec-defined disclosure, not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "social_media_warning_label_render", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. > Snapshot of OCSS `spec_version=ocss-v4-draft-4`; authoritative copy at [openchildsafety.com](https://openchildsafety.com). Rules in this tab are defined in §6.11 of the OCSS specification. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Bans the sale, license, or commercial transfer of minor user data to third parties under all circumstances. In API schema **Commercial Data Ban** Issuer: emit a commercial-data-ban activation event for the minor's account. Enforcer: MUST reject any data-sale, data-broker, or commercial-transfer API call referencing the minor's identifiers and MUST log each rejected attempt in the signed audit record. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "commercial_data_ban", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Triggers data deletion workflows on connected platforms and enables full profile removal via API. In API schema **Data Deletion Request** Issuer: emit a signed data-deletion directive carrying the minor's account identifiers to each connected platform. Enforcer: MUST delete or de-identify all personal data for the specified account within the statutory response window and MUST return a signed deletion receipt. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "data_deletion_request", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Enforces collection only of the minor data strictly necessary for the requested feature; rejects schema overreach at ingest. In API schema **Data Minimization** Issuer: emit a data-minimization policy naming the fields strictly necessary for the requested feature. Enforcer: MUST reject at ingest any collection request for minor data beyond the named necessary fields. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "data_minimization_enforce", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Ensures location tracking is disabled by default, requiring explicit parental authorization to enable. In API schema **Geolocation Opt-In** Issuer: emit a geolocation-opt-in event only after a parent explicitly grants location permissions for the minor. Enforcer: MUST treat location tracking as disabled until a signed opt-in event is received and MUST re-block if the grant expires or is revoked. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "geolocation_opt_in", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Protects minors' image rights by controlling photo sharing and facial recognition usage. In API schema **Image Rights** Issuer: emit an image-rights-protection flag for the minor's account. Enforcer: MUST block facial-recognition tagging of the minor in platform features and MUST require explicit parental consent before allowing third-party photo sharing of the minor's images. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "image_rights_minor", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Controls what personal data can be shared with third parties and platform partners. In API schema **Data Sharing Control** Issuer: emit a data-sharing policy specifying which third-party categories may receive the minor's data. Enforcer: MUST block any data-sharing API call to a third-party category not explicitly listed in the policy. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "privacy_data_sharing", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Defaults minor profiles to private and prevents discovery by non-contacts in search or suggested-users surfaces. In API schema **Privacy Profile Visibility** Issuer: emit a visibility policy setting the minor's profile to private and disabling discovery by non-contacts. Enforcer: MUST reject search-index and suggested-users API responses that would surface the minor's profile to non-contacts. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "privacy_profile_visibility", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Activates COPPA-aligned student-privacy defaults on a device when the school-mode flag is set by an authorized school. In API schema **Student Privacy School Mode** Issuer: emit a school-mode flag signed by an authorized school for the device. Enforcer: MUST apply COPPA-aligned student-privacy defaults while the flag is active and MUST NOT share student data beyond the educational purpose. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "student_privacy_school_mode", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks behavioral advertising, ad profiling, and retargeting for minor users across connected platforms. In API schema **Targeted Ad Block** Issuer: emit a targeted-ad-block policy for the minor's account. Enforcer: MUST reject behavioral-ad serving calls, discard any cross-site retargeting signals, and MUST NOT serve profiled ads to the minor's session. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "targeted_ad_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks third-party tracking pixels, fingerprinting scripts, and cross-site cookies on minor sessions. In API schema **Third-Party Tracker Block** Issuer: emit a tracker-block policy for the minor's session. Enforcer: MUST block third-party tracking pixels, fingerprinting scripts, and cross-site cookies on the minor's sessions. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "third_party_tracker_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Ensures minor users' interactions, prompts, and outputs are excluded from any AI model training pipeline by default. In API schema **AI Training Data Opt-Out** Issuer: emit a training-data-opt-out policy for the minor's account. Enforcer: MUST exclude the minor's interactions, prompts, and outputs from any model-training pipeline by default; this default-exclusion is spec-defined and not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_training_data_opt_out", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Caps the precision of any location data minor accounts emit at city-level granularity or coarser. In API schema **Geolocation Precision Cap** Issuer: emit a precision-cap policy for the minor's location data. Enforcer: MUST coarsen any location the minor's account emits to city-level granularity or coarser; this cap is spec-defined and not yet legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "geolocation_precision_cap", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Suppresses the minor's online/offline presence indicator from other users (KIDS Act §235(c)(6)(B)). In API schema **Presence Status Hide** A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "presence_status_hide", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.11) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Blocks location-sharing surfaces (live location, geo-tagged posts) on minor accounts by default. In API schema **Privacy Location** Issuer: emit a location-privacy policy disabling live-location and geo-tag features for the minor's account. Enforcer: MUST block any API call that would share or broadcast the minor's location without a signed parental grant. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "privacy_location", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. > Snapshot of OCSS `spec_version=ocss-v4-draft-4`; authoritative copy at [openchildsafety.com](https://openchildsafety.com). Rules in this tab are defined in §6.12 of the OCSS specification. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Disables autoplay, infinite scroll, notification streaks, and other compulsive-use design patterns. In API schema **Addictive Design Control** Issuer: fire when the child's policy store activates this rule. Enforcer: MUST disable autoplay, infinite scroll, streak counters, and variable-reward notification patterns for the minor's session. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "addictive_design_control", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Blocks AI products from deploying engagement maximizers (streaks, infinite scroll, manipulative nudges) on minor users. In API schema **AI No-Engagement Dark Patterns** Issuer: emit a no-engagement-dark-patterns policy for AI products serving the minor. Enforcer: MUST NOT deploy streaks, infinite scroll, or manipulative nudges in AI interactions with the minor and MUST produce a signed audit record evidencing the absence of these patterns. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_no_engagement_dark_patterns", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Requires AI-generated content to carry a 'this is AI' label visible to minor users at the moment of consumption. In API schema **AI Synthetic-Media Disclosure** Issuer: tag AI-generated content with a synthetic-media marker at generation time. Enforcer: MUST render a visible 'this is AI' label on the tagged content at the moment a minor consumes it and MUST record the disclosure in the signed audit trail. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_synthetic_media_disclosure", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Disables personalized algorithmic feeds and switches to chronological or non-profiled content delivery. In API schema **Algorithm Feed Control** Issuer: emit an algorithmic-feed-disable event for the minor's account. Enforcer: MUST switch the content feed to chronological or editorially curated order and MUST NOT apply engagement-optimized ranking to any minor session while this rule is active. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "algo_feed_control", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Generates the per-rule audit trail every recommender decision must produce for regulator replay and civil-society inspection. In API schema **Algorithmic Audit** Issuer: fire on every algorithmic feed or recommendation decision affecting the minor's session, emitting a signed record with inputs, model version, and output. Enforcer: MUST archive the signed record for the regulator-required retention period and MUST make it available for regulator or civil-society replay on request. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "algorithmic_audit", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Disables autoplay on video, audio, and reel surfaces so minor users always tap to start the next item. In API schema **Autoplay Disable** Issuer: emit an autoplay-disable event for the minor's account. Enforcer: MUST disable autoplay on video, audio, and reel surfaces so the minor taps to start each item, and MUST record the setting in the signed audit trail. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "autoplay_disable", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Emits the district's signed annual filtering attestation for E-rate certification, read via the aggregate attestation export. In API schema **CIPA Filter Attestation** Issuer: emit the district's annual filtering posture for the reporting period. Enforcer: MUST produce the district's signed CIPA filtering attestation, surfaced via the aggregate attestation export for E-rate certification. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "cipa_filter_attestation", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Generates a Data Protection Impact Assessment request packet for any rule a UK or EU regulator escalates. In API schema **DPIA Request Generator** Issuer: fire when a UK or EU regulator escalates a rule, assembling the impact-assessment inputs. Enforcer: MUST generate a signed Data Protection Impact Assessment request packet for the escalated rule and make it available to the regulator. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "dpia_request_generator", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Disables infinite-scroll feed mechanics for minor accounts; pagination breaks force a natural pause. In API schema **Infinite Scroll Block** Issuer: emit an infinite-scroll-block event for the minor's account. Enforcer: MUST disable infinite-scroll feed mechanics and insert pagination breaks for the minor's session, recording the setting in the signed audit trail. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "infinite_scroll_block", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Boosts the recommender weight of prosocial content (educational, civic, mental-health-positive) in minor feeds. In API schema **Prosocial Weight** Issuer: emit a prosocial-weight directive for the minor's recommender. Enforcer: SHOULD boost the ranking weight of prosocial content (educational, civic, mental-health-positive) in the minor's feed and MUST record the adjustment in the signed audit trail — a spec-defined recommender directive, not legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "prosocial_weight", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Boosts the recommender weight of representative content matching the minor user's identity signals. In API schema **Representation Weight** Issuer: emit a representation-weight directive for the minor's recommender. Enforcer: SHOULD boost the ranking weight of representative content matching the minor's identity signals and record the adjustment in the signed audit trail — spec-defined, not legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "representation_weight", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.12) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Boosts the recommender weight of role-model content (educators, athletes, scientists) over engagement-only content. In API schema **Role Model Weight** Issuer: emit a role-model-weight directive for the minor's recommender. Enforcer: SHOULD boost the ranking weight of role-model content (educators, athletes, scientists) over engagement-only content and record it in the signed audit trail — spec-defined, not legislatively mandated. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "role_model_weight", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. > Snapshot of OCSS `spec_version=ocss-v4-draft-4`; authoritative copy at [openchildsafety.com](https://openchildsafety.com). Rules in this tab are defined in §6.13 of the OCSS specification. ### Parental signal receipts Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.13) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Signs and archives an audit log for every minor data transaction; regulators replay against the log. In API schema **Minor Data Sale Audit Log** Issuer: fire on every transaction involving the minor's data, emitting a signed log entry. Enforcer: MUST sign and archive an audit-log record for each minor-data transaction and make the log available for regulator replay. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "minor_data_sale_audit_log", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.13) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Emits a signed parental-attestation certificate every regulator can verify against the Charter's public key. In API schema **Parental Attestation Certificate** Issuer: emit a signed parental-attestation certificate when a parent completes attestation. Enforcer: MUST issue the certificate signed under the Charter key so any regulator can verify it against the published public key. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "parental_attestation_cert", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. ### Regulatory reporting obligations Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.13) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Requires AI-enabled toys to present a current safety certification before activation in a household with minors. In API schema **AI Toy Safety Certification** Issuer: present the AI toy's current safety certification as a signed credential at activation. Enforcer: MUST verify a valid, unexpired safety certification before activating the AI toy in a household with minors and MUST refuse activation if none is presented. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ai_toy_safety_cert", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.13) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Automates detection and reporting workflows for child sexual abuse material across platforms. In API schema **CSAM Reporting** Issuer: fire when CSAM is detected on the platform, emitting a signed detection event with an evidence hash. Enforcer: MUST forward the signed detection payload to the NCMEC CyberTipline (or jurisdiction-equivalent) without delay and MUST retain the evidence record for the statutory retention period. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "csam_reporting", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.13) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Streams enforcement events to the Australian eSafety Commission's required reporting endpoint. In API schema **eSafety Commission Reporting** Issuer: emit a signed enforcement-event stream for the minor's account. Enforcer: MUST forward the signed events to the Australian eSafety Commission's required reporting endpoint and retain a delivery receipt. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "esafety_commission_reporting", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.13) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Mandatory 48-hour removal of nonconsensual intimate imagery (NCII) and AI deepfakes under the TAKE IT DOWN Act (federal, signed May 2025) In API schema **NCII Takedown** Issuer: emit a signed ncii\_takedown directive identifying the content (hash or URL) and the requester's identity. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "ncii_takedown", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.13) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Anchored Captures and signs evidence of platform compliance posture for use in adopter liability defense. In API schema **Platform Liability Evidence Capture** Issuer: fire on enforcement actions to capture the platform's compliance posture. Enforcer: MUST sign and retain an evidence record of each enforcement decision for use in adopter liability defense. A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "platform_liability_evidence_capture", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements. Rule source: OCSS `spec_version=ocss-v4-draft-4` (§6.13) — canonical registry at [openchildsafety.com](https://openchildsafety.com). Provisional Signed receipt that a minor-harm report was received and substantively responded to within the statutory SLA (KIDS Act §215). In API schema **Harm Report Intake** A valid `rule_category` value in the Phosra API's `RuleCategory` schema (`POST /api/v1/policies/{policyID}/rules`). See [Conformance Status](/ocss/status) for endpoint availability and [Rule Reference](/ocss/rule-reference) for the full parameter documentation. **Apply this rule** ```json theme={null} // POST /api/v1/policies/{policyID}/rules { "category": "harm_report_intake", "enabled": true, "config": {} } ``` `config` is an opaque, category-specific object; per-category field schemas are not yet specified — see the OCSS rule definition for the normative Issuer/Enforcer requirements.

Capability matrix

Write-sign is live for all 123 (identical census transaction). The other columns are independent capability axes — a rule can be Gated AND typed. Downstream enforcement is the partner's boundary for all 123 uniformly; the census proves a signed write, not enforcement. | Rule | Write-sign | Typed params | Band | Registry tier | | ---------------------------------------------------------------------------- | ---------- | ---------------------------------- | ----------------------- | -------------------------------------------------- | | `addictive_design_control` — Addictive Design Control | ✓ | typed (category\_descriptor\_list) | Open | anchored | | `addictive_pattern_block` — Addictive Pattern Block | ✓ | toggle-only | Open | anchored | | `adult_site_av_required` — Adult-Site Age Verification | ✓ | typed (av\_gate) | Open | anchored | | `age_appropriate_profile_mode` — OCSS Child Mode | ✓ | toggle-only | Open | anchored | | `age_gate` — Age Gate | ✓ | toggle-only | Open | anchored | | `age_signal_broadcast` — Age Signal Broadcast | ✓ | toggle-only | Open | anchored | | `ai_chatbot_age_assertion` — AI Chatbot Age Assertion | ✓ | typed (av\_gate) | Open | anchored | | `ai_chatbot_tier_gate` — AI Chatbot Tier Gate | ✓ | typed (numeric\_threshold) | Open | anchored | | `ai_companion_block` — AI Companion Block | ✓ | toggle-only | Open | anchored | | `ai_data_responsibility_rating_threshold` — AI Data Responsibility Threshold | ✓ | typed (numeric\_threshold) | Open | provisional *(governance note — not a write gate)* | | `ai_minor_interaction` — AI Minor Interaction | ✓ | toggle-only | Open | anchored | | `ai_no_csam_generation` — AI No-CSAM Generation | ✓ | toggle-only | Open | anchored | | `ai_no_engagement_dark_patterns` — AI No-Engagement Dark Patterns | ✓ | toggle-only | Open | anchored | | `ai_no_personhood_deception` — AI No-Personhood Deception | ✓ | toggle-only | Open | anchored | | `ai_no_self_harm_promotion` — AI No-Self-Harm Promotion | ✓ | toggle-only | Open | anchored | | `ai_no_simulated_companionship` — AI No-Simulated Companionship | ✓ | toggle-only | Open | anchored | | `ai_no_unsupervised_therapy` — AI No-Unsupervised Therapy | ✓ | toggle-only | Open | anchored | | `ai_product_classification_gate` — AI Product Classification Gate | ✓ | typed (rating\_flag\_gate) | Open | anchored | | `ai_safety_rating_threshold` — AI Child-Safety Rating Threshold | ✓ | typed (numeric\_threshold) | Open | provisional *(governance note — not a write gate)* | | `ai_synthetic_media_disclosure` — AI Synthetic-Media Disclosure | ✓ | toggle-only | Open | anchored | | `ai_toy_safety_cert` — AI Toy Safety Certification | ✓ | toggle-only | Open | anchored | | `ai_training_data_opt_out` — AI Training Data Opt-Out | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `ai_transparency_rating_threshold` — AI Transparency Threshold | ✓ | typed (numeric\_threshold) | Open | provisional *(governance note — not a write gate)* | | `algo_feed_control` — Algorithm Feed Control | ✓ | toggle-only | Open | anchored | | `algorithmic_audit` — Algorithmic Audit | ✓ | toggle-only | Open | anchored | | `app_install_block` — App Install Block | ✓ | typed (app\_targeted) | Open | anchored | | `app_store_age_attestation` — App Store Age Attestation | ✓ | toggle-only | Open | anchored | | `autoplay_disable` — Autoplay Disable | ✓ | toggle-only | Open | anchored | | `cipa_filter_attestation` — CIPA Filter Attestation | ✓ | toggle-only | Open | anchored | | `commercial_data_ban` — Commercial Data Ban | ✓ | toggle-only | Open | anchored | | `commercial_pressure_threshold` — Commercial Pressure Threshold | ✓ | typed (numeric\_threshold) | Open | anchored | | `content_allow_title` — Content Allow Title | ✓ | typed (title\_targeted) | Open | provisional *(governance note — not a write gate)* | | `content_allowlist_mode` — Content Allowlist Mode | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `content_block_title` — Content Block Title | ✓ | typed (title\_targeted) | Open | provisional *(governance note — not a write gate)* | | `content_descriptor_block` — Content Descriptor Block | ✓ | typed (category\_descriptor\_list) | Open | provisional *(governance note — not a write gate)* | | `content_rating` — Content Rating | ✓ | typed (numeric\_threshold) | Open | anchored | | `crisis_resource_surface` — Crisis Resource Surface | ✓ | toggle-only | Gated (accredited-tier) | provisional *(governance note — not a write gate)* | | `csam_reporting` — CSAM Reporting | ✓ | toggle-only | Open | anchored | | `data_deletion_request` — Data Deletion Request | ✓ | toggle-only | Open | anchored | | `data_minimization_enforce` — Data Minimization | ✓ | toggle-only | Open | anchored | | `digital_literacy_curriculum_link` — Digital Literacy Curriculum Link | ✓ | toggle-only | Gated (accredited-tier) | provisional *(governance note — not a write gate)* | | `dm_feature_disable` — DM Feature Disable | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `dm_restriction` — DM Restriction | ✓ | typed (handle\_list) | Open | anchored | | `dpia_request_generator` — DPIA Request Generator | ✓ | toggle-only | Open | anchored | | `dumbphone_migration_mode` — Dumbphone Migration Mode | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `educational_credit` — Educational Credit | ✓ | toggle-only | Gated (accredited-tier) | provisional *(governance note — not a write gate)* | | `ephemeral_message_disable` — Ephemeral Message Disable | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `esafety_commission_reporting` — eSafety Commission Reporting | ✓ | toggle-only | Open | anchored | | `firearm_content_block` — Firearm Content Block | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `flagged_content_digest` — Flagged Content Digest | ✓ | typed (schedule\_window) | Gated (accredited-tier) | provisional *(governance note — not a write gate)* | | `gambling_mechanics_block` — Gambling Mechanics Block | ✓ | toggle-only | Open | anchored | | `geolocation_opt_in` — Geolocation Opt-In | ✓ | toggle-only | Open | anchored | | `geolocation_precision_cap` — Geolocation Precision Cap | ✓ | typed (category\_descriptor\_list) | Open | provisional *(governance note — not a write gate)* | | `hard_id_verification_escalation` — Hard ID Verification Escalation | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `harm_report_intake` — Harm Report Intake | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `image_content_monitoring` — Image Content Monitoring | ✓ | toggle-only | Gated (accredited-tier) | provisional *(governance note — not a write gate)* | | `image_rights_minor` — Image Rights | ✓ | toggle-only | Open | anchored | | `infinite_scroll_block` — Infinite Scroll Block | ✓ | toggle-only | Open | anchored | | `library_filter_compliance` — Library Filter | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `message_content_monitoring` — Message Content Monitoring | ✓ | toggle-only | Gated (accredited-tier) | provisional *(governance note — not a write gate)* | | `minor_data_sale_audit_log` — Minor Data Sale Audit Log | ✓ | toggle-only | Open | anchored | | `monitoring_activity` — Activity Monitoring | ✓ | toggle-only | Gated (accredited-tier) | anchored | | `monitoring_alerts` — Monitoring Alerts | ✓ | toggle-only | Gated (accredited-tier) | provisional *(governance note — not a write gate)* | | `named_contact_block` — Named Contact Block | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `ncii_takedown` — NCII Takedown | ✓ | toggle-only | Open | anchored | | `not_for_minors_block` — Not-For-Minors Hard Block | ✓ | typed (nfk\_block) | Open | provisional *(governance note — not a write gate)* | | `notification_curfew` — Notification Curfew | ✓ | typed (schedule\_window) | Open | anchored | | `os_age_signal_ingest` — OS Age Signal Ingest | ✓ | toggle-only | Open | anchored | | `parental_attestation_cert` — Parental Attestation Certificate | ✓ | toggle-only | Open | anchored | | `parental_consent_gate` — Parental Consent Gate | ✓ | toggle-only | Open | anchored | | `parental_event_notification` — Parental Event Notification | ✓ | toggle-only | Gated (accredited-tier) | anchored | | `phone_free_school_hours` — Phone-Free School Hours | ✓ | typed (schedule\_window) | Open | anchored | | `platform_liability_evidence_capture` — Platform Liability Evidence Capture | ✓ | toggle-only | Open | anchored | | `presence_status_hide` — Presence Status Hide | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `privacy_account_creation` — Privacy Account Creation | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `privacy_data_sharing` — Data Sharing Control | ✓ | typed (category\_descriptor\_list) | Open | anchored | | `privacy_location` — Privacy Location | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `privacy_profile_visibility` — Privacy Profile Visibility | ✓ | toggle-only | Open | anchored | | `privacy_rating_dimension_gate` — Privacy Dimension Gate | ✓ | typed (rating\_flag\_gate) | Open | provisional *(governance note — not a write gate)* | | `privacy_rating_score_threshold` — Privacy Score Threshold | ✓ | typed (numeric\_threshold) | Open | provisional *(governance note — not a write gate)* | | `privacy_rating_tier_gate` — Privacy Tier Gate | ✓ | typed (numeric\_threshold) | Open | provisional *(governance note — not a write gate)* | | `privacy_seal_required` — Privacy Seal Required | ✓ | typed (privacy\_tier\_gate) | Open | provisional *(governance note — not a write gate)* | | `profanity_threshold` — Profanity Threshold | ✓ | typed (numeric\_threshold) | Open | provisional *(governance note — not a write gate)* | | `prosocial_weight` — Prosocial Weight | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `purchase_approval` — Purchase Approval | ✓ | toggle-only | Open | anchored | | `purchase_block_iap` — Purchase Block IAP | ✓ | toggle-only | Open | anchored | | `purchase_spending_cap` — Purchase Spending Cap | ✓ | typed (monetary\_amount) | Open | provisional *(governance note — not a write gate)* | | `rating_age_gate` — Civil-Society Age Recommendation Gate | ✓ | typed (rating\_age\_gate) | Open | provisional *(governance note — not a write gate)* | | `recognized_seal_allowlist` — Recognized Seal Allowlist | ✓ | typed (privacy\_tier\_gate) | Open | provisional *(governance note — not a write gate)* | | `representation_weight` — Representation Weight | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `role_model_weight` — Role Model Weight | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `school_alert_routing` — School Alert Routing | ✓ | toggle-only | Gated (accredited-tier) | anchored | | `screen_time_report` — Screen Time Report | ✓ | typed (schedule\_window) | Gated (accredited-tier) | anchored | | `search_query_monitoring` — Search Query Monitoring | ✓ | toggle-only | Gated (accredited-tier) | provisional *(governance note — not a write gate)* | | `sexual_content_threshold` — Sexual Content Threshold | ✓ | typed (numeric\_threshold) | Open | provisional *(governance note — not a write gate)* | | `social_account_content_monitoring` — Social Account Content Monitoring | ✓ | toggle-only | Gated (accredited-tier) | provisional *(governance note — not a write gate)* | | `social_chat_control` — Social Chat Control | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `social_contacts` — Social Contacts | ✓ | typed (handle\_list) | Open | provisional *(governance note — not a write gate)* | | `social_media_min_age` — Social Media Min Age | ✓ | typed (age\_value) | Open | anchored | | `social_media_warning_label_render` — Social Media Warning Label | ✓ | toggle-only | Gated (accredited-tier) | provisional *(governance note — not a write gate)* | | `social_multiplayer` — Social Multiplayer | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `stranger_dm_block` — Stranger DM Block | ✓ | typed (handle\_list) | Open | anchored | | `stranger_outreach_friction` — Stranger Outreach Friction | ✓ | toggle-only | Open | anchored | | `streaming_age_rating_enforce` — Streaming Age Rating | ✓ | typed (numeric\_threshold) | Open | anchored | | `student_privacy_school_mode` — Student Privacy School Mode | ✓ | toggle-only | Open | anchored | | `substance_content_threshold` — Substance Content Threshold | ✓ | typed (numeric\_threshold) | Open | provisional *(governance note — not a write gate)* | | `targeted_ad_block` — Targeted Ad Block | ✓ | toggle-only | Open | anchored | | `teen_minimum_age_16_gate` — Teen Minimum Age 16 Gate | ✓ | typed (age\_value) | Open | anchored | | `third_party_tracker_block` — Third-Party Tracker Block | ✓ | toggle-only | Open | anchored | | `time_daily_limit` — Daily Time Limit | ✓ | typed (duration) | Open | anchored | | `time_downtime` — Time Downtime | ✓ | typed (schedule\_window) | Open | anchored | | `time_per_app_limit` — Time Per-App Limit | ✓ | typed (duration) | Open | provisional *(governance note — not a write gate)* | | `time_scheduled_hours` — Scheduled Hours | ✓ | typed (schedule\_window) | Open | anchored | | `ugc_world_age_gate` — UGC World Age Gate | ✓ | typed (age\_value) | Open | provisional *(governance note — not a write gate)* | | `unrated_privacy_default` — Unrated Privacy Default | ✓ | typed (privacy\_tier\_gate) | Open | provisional *(governance note — not a write gate)* | | `usage_timer_notification` — Usage Timer | ✓ | toggle-only | Gated (accredited-tier) | anchored | | `violence_threshold` — Violence Threshold | ✓ | typed (numeric\_threshold) | Open | provisional *(governance note — not a write gate)* | | `voice_chat_minor_limit` — Voice Chat Minor Limit | ✓ | toggle-only | Open | provisional *(governance note — not a write gate)* | | `web_category_block` — Web Category Block | ✓ | typed (category\_descriptor\_list) | Open | anchored | | `web_custom_allowlist` — Web Custom Allowlist | ✓ | typed (domain\_list) | Open | provisional *(governance note — not a write gate)* | | `web_custom_blocklist` — Web Custom Blocklist | ✓ | typed (domain\_list) | Open | provisional *(governance note — not a write gate)* | | `web_filter_level` — Web Filter Level | ✓ | typed (numeric\_threshold) | Open | anchored | | `web_safesearch` — Safe Search | ✓ | toggle-only | Open | anchored | *** *OCSS rule vocabulary is royalty-free per §5.5 of the specification ("Royalty-free with an unpublished audit bill is a fee schedule by another name" is the governing sentence). The canonical registry and spec text are at [openchildsafety.com](https://openchildsafety.com). If the standard and this mirror ever conflict, the standard (at openchildsafety.com) wins. Phosra's steward-of-record designation, accreditation status, and dated succession binding are published at [`/.well-known/ocss/succession`](/.well-known/ocss/succession) — a signed, verifiable role binding, not a static page. For the full list of callable rule categories with their API parameter documentation, see [Rule Reference](/ocss/rule-reference).* # Conformance Status Source: https://docs.phosra.com/ocss/status Live vs. preview for every OCSS and Phosra surface — exact, honest, no endpoint implied live unless it is. # Conformance Status Every surface in this documentation is one of two statuses. This page collects them all in one place. **Live now** means you can call it today and get a real response. **Preview** means the shape is committed — the endpoint, request/response contract, and SDK signatures are finalized — but the surface is not yet deployed. No endpoint is implied live unless it is listed as live below. *** ## Live — no authentication required These endpoints are deployed and verifiable by any party today. | Surface | Endpoint | Notes | | ---------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | OCSS Trust List | `GET https://prodapi.phosra.com/.well-known/ocss/trust-list` | Signed JSON document. Verify against the published root key `root-prod-2026-06`. Production accreditation entries are added through OCSS governance. | | OCSS succession record | `GET https://prodapi.phosra.com/.well-known/ocss/succession` | Ed25519-signed steward-of-record record and anti-capture covenant. | | Health check | `GET https://prodapi.phosra.com/health` | Simple liveness check. | *** ## Live — authentication required These endpoints are deployed and require a `phosra_`-prefixed API key or session bearer token. | Surface | Auth | Notes | | ------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------- | | Developer orgs (`/api/v1/developers/orgs`) | `phosra_` API key | Create, read, update, delete developer organizations. | | API keys (`/api/v1/developers/orgs/{orgId}/keys`) | `phosra_` API key | Provision, revoke, and rotate `phosra_`-prefixed keys. | | Usage rollups (`/api/v1/developers/orgs/{orgId}/usage`) | `phosra_` API key | Hourly request rollups per org. | | Advisor agents (`/api/v1/advisors`) | Session bearer | Register and consult advisor agents; declare OCSS payload keys for federation. | | MCP tokens (`/api/v1/mcp-tokens`) | Session bearer | Mint and revoke Model Context Protocol authentication tokens. | | §9.4 attestation export | `phosra_` API key | Signed CSV of conformance evidence per OCSS §9.4, suitable for regulatory submission. | The base URL for all management operations is `https://prodapi.phosra.com/api/v1`. *** ## Live now — sandbox census (`https://phosra-api-sandbox-staging.up.railway.app`) These OCSS protocol surfaces are deployed and return real signed responses **today** on the sandbox census. They are RFC-9421 DID-signed; you must be on the sandbox Trust List (see [Onboarding](/ocss/onboarding) — self-serve, no email required). | Surface | Endpoint | Notes | | ----------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | | Rule write + signed receipt | `POST /api/v1/policies/{policyId}/rules` | Router-signed write receipt; verifiable to the trust-list root. | | Enforcement confirmation (§8.3.8) | `POST /api/v1/enforcement-confirmations` | Signed enforcement receipt; satisfies the record-in-signed-audit MUST. | | Binding-leg endpoint mint | `POST /api/v1/enforcement-endpoints` | Consent-first label mint. | | Signed enforcement profile | `GET /api/v1/enforcement-profiles/{endpoint_id}` | Carries ALL rule categories including engagement rules (`addictive_pattern_block`, `infinite_scroll_block`, etc.). | | Sealed harm\_context (§3A.3) | `POST /api/v1/harm-context` | Router-blind sealed envelope. | | Consent attestation ingest (§8.3.2) | `POST /api/v1/webhooks/inbound/app-store` | Sealed inbound consent signal; verifiable write receipt returned. | | Signed Trust List | `GET /.well-known/ocss/trust-list` | Root-signed; cold-verifiable by any party. | *** ## Preview — committed shape, not yet deployed on the production control plane The surfaces below are **not live on the production control plane** (`prodapi.phosra.com`). The request/response shape is committed and will not change without a versioned notice. Do not build production flows that call these endpoints today — they will return 404 on the production base URL. | Surface | Planned path | Notes | | ------------------------------------ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Decision check | `POST /api/v1/check` | **This endpoint has never been deployed and does not exist.** Do not build against it. Use the sandbox census rule-write + confirmation surfaces listed above instead. The shape appeared in earlier marketing pages as a committed design; it is not a live 200 on any environment. | | Full self-serve API key provisioning | `POST /api/v1/developers/orgs` (public, without prior handshake) | The management API above is live; the fully automated self-serve sign-up funnel without a direct handshake is a later milestone. | | `@phosra/sdk-dev` npm package | `npm install @phosra/sdk-dev` | Package is authored and the API is committed. Not yet published to npm. Ships with the self-serve funnel. | | `@phosra/sdk-dev/protocol` re-export | `npm install @phosra/sdk-dev` | The `/protocol` subpath is `@ocss/ts` verbatim. Unpublished as a package; import directly from `@ocss/ts` in the meantime. | | `@phosra/mcp` npm package | `npx @phosra/mcp` | MCP server for agent integrations. Shape committed; package not yet published. Ships with the self-serve funnel. | *** ## OCSS conformance surfaces (open standard, not Phosra-specific) These surfaces are defined by the OCSS specification. Phosra implements them; the standard's stewardship body owns the spec. | Surface | Status | Notes | | ------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | Trust List well-known endpoint | Live | See above. | | Succession record well-known endpoint | Live | See above. | | Ed25519 signed write receipts | **Live on sandbox census** | Issued by `POST /api/v1/policies/{policyId}/rules`; router-signed, verifiable to the trust-list root. See sandbox census table above. | | Ed25519 signed enforcement receipts (§8.3.8) | **Live on sandbox census** | Issued by `POST /api/v1/enforcement-confirmations`. See sandbox census table above. | | Two-layer sealed envelope (`harm_context`, §3A.3) | **Live on sandbox census** | Router-blind lane; `POST /api/v1/harm-context`. See sandbox census table above. | | §9.4 attestation export | Live | Signed CSV evidence export. See live surfaces above. | | Conformance suite | External | Owned by the OCSS stewardship body at [openchildsafety.com](https://openchildsafety.com). Not a Phosra-hosted surface. | *** ## How status changes When a preview surface ships, the table above is updated and the relevant page in this documentation gains a "Live" label. Changes that affect the request/response contract are versioned (e.g. `/api/v2/check`). The introduction page always reflects the current live/preview split in its [`` block](/introduction#what-is-live-today). *** ## Questions If you are building against a preview surface and need early access to the developer funnel, reach out at [developers@phosra.com](mailto:developers@phosra.com). # OCSS Trust Framework Source: https://docs.phosra.com/ocss/trust-framework The OCSS routing and trust layer — the two-layer signed envelope, the eIDAS-style Trust List, accreditation tiers, and the signed-verb model. Hosted by Phosra at /.well-known/ocss/*. The OCSS Trust Framework is the routing and trust layer of the Open Child Safety Specification. It defines how signals move between parties, who is trusted to move them, and how any verifying party can confirm a receipt without calling back to Phosra. The normative rules are in the OCSS specification at [openchildsafety.com](https://openchildsafety.com); this page describes the mechanism and where Phosra hosts it. The Trust Framework is part of the OCSS standard — not a Phosra invention. Phosra hosts conforming endpoints. Any other accredited OCSS provider hosts the same endpoints with the same verifiable structure. *** ## The two-layer signed envelope Every OCSS signal travels inside a two-layer envelope: **Outer layer (transport)** — signed by the sending party's registered key (an Ed25519 signing key declared on the Trust List). The outer layer carries the signal type, the sender DID, the recipient hint, and the inner payload as a sealed blob. The router reads only the outer layer headers it needs to move the signal — it never opens the inner payload. **Inner layer (sealed payload)** — encrypted to the recipient's declared payload key (an EC P-256 JWK published on the Trust List). Only the designated recipient can open it. The router is structurally blind to the payload content; this is not a policy — it is cryptographic enforcement. This two-layer structure means: * Senders authenticate with a key that any party can look up on the Trust List. * Payload contents are sealed end-to-end between sender and recipient. * The routing layer cannot inspect, modify, or replay the inner payload. *** ## The Trust List The Trust List is a signed JSON document that enumerates every accredited party on the network — their DID, declared signing key(s), payload key, and accreditation tier. It is the anchor of the entire verification chain: a receipt is valid only if it was signed by a key that appears on the Trust List. The Trust List is served at a standard well-known path: ``` GET https://prodapi.phosra.com/.well-known/ocss/trust-list ``` This endpoint is **live today, no authentication required**. The response is a signed JSON document. The root signing key (`root-prod-2026-06`) is published separately; any verifier can fetch the Trust List, check the signature against the published root key, and determine whether a given party is accredited. The production Trust List is validly signed against `root-prod-2026-06`. Production accreditation entries are added through OCSS governance — no test or sandbox keys are present in production. *** ## Accreditation tiers The Trust Framework defines accreditation tiers that govern which parties can write which signal types: | Tier | Description | | ------------- | ------------------------------------------------------------------------------ | | `accredited` | Full accreditation — can write all signal types permitted by the rule category | | `provisional` | Provisional accreditation — can write a constrained subset pending full review | A party's tier is declared in their Trust List entry. The receiving party checks the tier at decision time — a signal from a `provisional` sender is handled differently from one with `accredited` status. *** ## The signed-verb model Every operation on the OCSS network is a **signed verb**: a typed action, signed by the acting party's key, carrying a reference to the Trust List entry that authorizes it. Receipts are not add-ons — they are the unit of the protocol. A write that produces no verifiable receipt is not a conformant write. Signed verbs include: * **Write receipt** — records a rule decision (block / allow / restrict / escalate) for a child policy, signed by the writing party. * **Enforcement receipt** — confirms a decision was applied at an enforcement endpoint (a device, router, or app surface), signed by the enforcing party. * **Attestation export** (§9.4) — a signed CSV of conformance evidence per the §9.4 export spec, suitable for regulatory submission. All three are verifiable against the Trust List without calling back to Phosra at verification time. *** ## The succession record Alongside the Trust List, Phosra hosts the OCSS succession record — the machine-checkable form of the steward-of-record designation and the anti-capture covenant: ``` GET https://prodapi.phosra.com/.well-known/ocss-succession ``` This endpoint is also **live today, no authentication required**. The record is Ed25519-signed by the steward key. It declares who holds stewardship, the transfer status, and the succession plan in a form any party can fetch and verify independently. *** ## Verification without Phosra A receipt issued today can be verified against the Trust List tomorrow, by a party that has never contacted Phosra, against a snapshot of the Trust List fetched at any time the receipt was valid. The verification steps are: 1. Fetch the Trust List from `/.well-known/ocss/trust-list`. 2. Verify the Trust List signature against the published root key. 3. Look up the signing party's key in the Trust List. 4. Verify the receipt signature against that key. 5. Confirm the party's tier permits the signal type written. Steps 1–5 use only the open `@openchildsafety/ocss` library. No Phosra-specific code or callback is required. *** ## Next * [OCSS Protocol SDK](/ocss/protocol-sdk) — `@openchildsafety/ocss` via `@phosra/sdk-dev/protocol`: sign receipts, open envelopes, resolve the Trust List. * [Conformance Status](/ocss/status) — live vs. preview surface-by-surface. * [OCSS specification](https://openchildsafety.com) — normative Trust Framework text. # Create your account & get keys Source: https://docs.phosra.com/platform/create-account The live self-serve path from zero to a working phosra_ API key — sign up, get an org, mint your first key. No pre-existing key, no handshake, no email required. Getting a `phosra_` API key requires **no pre-existing credential and no contact with the Phosra team**. The entire funnel is live and self-serve: 1. **Sign up** — [dashboard.phosra.com/signup](https://dashboard.phosra.com/signup) (WorkOS AuthKit hosted sign-up; email or SSO) 2. **Your developer org is auto-provisioned** — first visit to the [developer console](https://dashboard.phosra.com/dashboard/developers/console) creates it idempotently 3. **Mint your first key** — from the [Keys page](https://dashboard.phosra.com/dashboard/developers/keys), or in one API call (below) **Management plane, not census.** This funnel — signup, orgs, `phosra_` keys — is the Phosra **control plane** and sits outside OCSS §8.1 census scope. OCSS census verbs (rule writes, enforcement confirmations, harm-context, consent attestations) remain **RFC-9421 DID-signed only**: a `phosra_` key or session token never authenticates a census write. For the census path, see [Onboarding](/ocss/onboarding). *** ## Path A — the console (browser) 1. Go to [dashboard.phosra.com/signup](https://dashboard.phosra.com/signup) and create an account. Existing accounts sign in at [dashboard.phosra.com/login](https://dashboard.phosra.com/login). 2. You land on the [developer console](https://dashboard.phosra.com/dashboard/developers/console). On first load the console provisions your developer organization automatically — no form to fill. 3. Open **[API Keys](https://dashboard.phosra.com/dashboard/developers/keys)** → **Create Key**. Pick a name, environment (`test` or `live`), and scopes. 4. The key is shown **once** — `phosra_test_<64hex>` or `phosra_live_<64hex>`. Copy it immediately; only its SHA-256 hash is stored server-side. *** ## Path B — the API (curl) Everything the console does is plain HTTP. Authentication for these management calls is your **WorkOS session bearer** (the access token from your signup/login session) — *not* a `phosra_` key, which is exactly why no key is needed to get your first key. ### One-call bootstrap `POST /api/ensure-dev-org` (a dashboard BFF route) idempotently returns your org, creating it if needed — and with `bootstrap_key: true` also mints the org's **first** `phosra_` key when none exists yet: ```bash theme={null} curl -X POST https://dashboard.phosra.com/api/ensure-dev-org \ -H "Authorization: Bearer $WORKOS_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "Acme Corp", "bootstrap_key": true}' ``` ```json theme={null} { "id": "8f14e45f-…", "name": "Acme Corp", "slug": "acme-corp", "first_key": { "id": "c9f0f895-…", "name": "First key (bootstrap)", "environment": "test", "key": "phosra_test_4a1b2c…" } } ``` `first_key.key` is returned only when the org has zero active keys — the flag never mints a second key. Save it immediately. ### Or the raw control-plane calls ```bash theme={null} # 1. Create your developer org (WorkOS session bearer — no phosra_ key needed) curl -X POST https://prodapi.phosra.com/api/v1/developers/orgs \ -H "Authorization: Bearer $WORKOS_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "Acme Corp"}' # 2. Mint your first key (key returned once) curl -X POST https://prodapi.phosra.com/api/v1/developers/orgs/$ORG_ID/keys \ -H "Authorization: Bearer $WORKOS_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "my-first-key", "environment": "test", "scopes": ["read:families", "write:policies"]}' ``` *** ## Verify the key works ```bash theme={null} curl https://prodapi.phosra.com/api/v1/developer/families \ -H "Authorization: Bearer phosra_test_4a1b2c…" ``` A `200` with a JSON array (empty is fine for a fresh org) confirms the credential loop is closed: fresh signup → org → usable `phosra_` key, with no pre-existing key anywhere in the chain. *** ## Where each credential is used | Credential | Issued by | Authenticates | | ----------------------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | | WorkOS session bearer | [Signup](https://dashboard.phosra.com/signup) / [login](https://dashboard.phosra.com/login) (AuthKit) | Management: `/developers/orgs*`, keys, usage — the control plane | | `phosra_live_` / `phosra_test_` key | Console Keys page or `POST /developers/orgs/{orgId}/keys` | Data plane: `/api/v1/developer/*` routes | | Ed25519 DID key (RFC-9421) | Your own keypair + Trust List entry ([Onboarding](/ocss/onboarding)) | OCSS census verbs — never a `phosra_` key | *** ## Conformance The credential loop above needs a session bearer, so the full signup → key path is not a no-credential `curl`. What a docs-only stranger — or the nightly docs-conformance CI — **can** run with no credentials is that the control-plane the funnel lands on is live and correctly auth-gated (the org-creation endpoint exists and rejects unauthenticated writes with `401`, not `404`): ```bash theme={null} # 1. The control plane is live. curl -fsS https://prodapi.phosra.com/health # → 200 {"status":"ok"} # 2. Org creation exists and is auth-gated (401, NOT 404 — the endpoint is real, just needs your session bearer). curl -s -o /dev/null -w "%{http_code}\n" -X POST https://prodapi.phosra.com/api/v1/developers/orgs # → 401 ``` A `401` here (not `404`) is the proof the self-serve funnel is real: the endpoint the console and the one-call bootstrap post to is deployed and gated — supply your WorkOS session bearer (from [signup](https://dashboard.phosra.com/signup)) and the same call returns your new org. *** ## Further reading * [Developer Platform overview →](/platform/overview) — all 17 control-plane operations * [Platform Registration →](/integration/platform-registration) — the platform-side counterpart: mint your endpoint + connect-secret * [Authentication →](/authentication) — credential formats, scopes, rotation * [OCSS onboarding →](/ocss/onboarding) — the census-side (RFC-9421) path # Developer Platform Overview Source: https://docs.phosra.com/platform/overview The Phosra control plane — manage developer orgs, API keys, usage, advisor agents, and MCP tokens. Live at prodapi.phosra.com. The Phosra developer platform is the **live control-plane API** for building on top of Phosra's OCSS-conformant infrastructure. It covers the management operations your integration needs before sending a single signed verb: create a developer org, provision `phosra_`-prefixed API keys, register advisor agents, declare OCSS payload keys for federation, mint MCP tokens, and pull hourly usage rollups. **Base URL:** `https://prodapi.phosra.com/api/v1`\ **Authentication:** HTTP Bearer — a **WorkOS session bearer** (from [signup](https://dashboard.phosra.com/signup)/login) for org and key management, a **`phosra_` API key** for the `/developer/*` data-plane routes\ **Status:** Live — see [/ocss/status](/ocss/status) for current availability and preview labels **No key needed to get your first key.** Org creation and key minting authenticate with your WorkOS session, so a fresh signup bootstraps everything self-serve — see [Create your account & get keys](/platform/create-account). This is the **Phosra control plane** — the management surface for developers integrating with OCSS via Phosra. It is distinct from the **Parental Controls product API** (Phosra's own consumer product, a separate pillar documented elsewhere). *** ## What the control plane covers | Area | Description | | ------------------ | ------------------------------------------------------------------------------------------------------------ | | **Organizations** | Create and manage developer orgs — the billing and access boundary for your integration. | | **API keys** | Provision, revoke, and rotate `phosra_`-prefixed keys. Keys are shown once at creation. | | **Usage** | Hourly request rollups per org — query windowed by `from` / `to` timestamps. | | **Advisor agents** | Register OCSS advisor agents, submit consultation requests, and declare Ed25519 payload keys for federation. | | **MCP tokens** | Mint and revoke Model Context Protocol authentication tokens for AI-agent integrations. | *** ## Operations reference All 17 control-plane operations, their routes, and their authentication requirements: | Function | Method + path | Auth | | -------------------------- | ------------------------------------------------------- | ------------------ | | `listOrgs` | `GET /developers/orgs` | session bearer | | `createOrg` | `POST /developers/orgs` | session bearer | | `getOrg` | `GET /developers/orgs/{orgId}` | session bearer | | `updateOrg` | `PUT /developers/orgs/{orgId}` | session bearer | | `deleteOrg` | `DELETE /developers/orgs/{orgId}` | session bearer | | `listOrgMembers` | `GET /developers/orgs/{orgId}/members` | session bearer | | `listApiKeys` | `GET /developers/orgs/{orgId}/keys` | session bearer | | `createApiKey` | `POST /developers/orgs/{orgId}/keys` | session bearer | | `revokeApiKey` | `DELETE /developers/orgs/{orgId}/keys/{keyId}` | session bearer | | `regenerateApiKey` | `POST /developers/orgs/{orgId}/keys/{keyId}/regenerate` | session bearer | | `getOrgUsage` | `GET /developers/orgs/{orgId}/usage` | session bearer | | `registerAdvisor` | `POST /advisors/register` | session bearer | | `consultAdvisor` | `POST /advisors/consult` | session bearer | | `declareAdvisorPayloadKey` | `POST /advisors/{id}/payload-key` | Ed25519 possession | | `listMcpTokens` | `GET /mcp-tokens` | session bearer | | `createMcpToken` | `POST /mcp-tokens` | session bearer | | `revokeMcpToken` | `DELETE /mcp-tokens/{id}` | session bearer | All management operations (`/developers/orgs*`, keys, usage, advisors, MCP tokens) use a **session bearer** — the WorkOS access token from your signup/login session. This is deliberate: it means a fresh signup can create an org and mint its first `phosra_` key with no pre-existing key ([Create your account & get keys](/platform/create-account)). The `phosra_` key you mint authenticates the **data-plane** `/developer/*` routes (families, policies, enforcement — see [Authentication](/authentication)), not these management calls. `declareAdvisorPayloadKey` additionally requires Ed25519 key possession — the request must be signed with the declared key. *** ## `@phosra/sdk-dev` management half The management half of `@phosra/sdk-dev` is a generated TypeScript client produced from the Phosra OpenAPI spec by [hey-api](https://heyapi.dev/). Each operation is a typed function that accepts an `Options<…Data>` object and returns a `RequestResult` — a Promise resolving to `{ data, error }`. `@phosra/sdk-dev` is a **preview** package — the shape is committed and the control-plane API it calls is live, but the package is not yet published to npm. See [/ocss/status](/ocss/status) for the current preview label and availability timeline. ### Configure the client ```typescript theme={null} import { client } from "@phosra/sdk-dev"; client.setConfig({ baseUrl: "https://prodapi.phosra.com/api/v1", headers: { // Management calls (orgs, keys, usage) take your WorkOS session bearer. // Use the phosra_ key you mint for the /developer/* data-plane routes. Authorization: `Bearer ${process.env.WORKOS_ACCESS_TOKEN}`, }, }); ``` Override `baseUrl` with `http://localhost:8080/api/v1` for local development. ### Create a developer organization ```typescript theme={null} import { createOrg } from "@phosra/sdk-dev"; const { data, error } = await createOrg({ body: { name: "Acme Corp", description: "Acme's OCSS integration", website_url: "https://acme.example.com", }, headers: { Authorization: `Bearer ${process.env.WORKOS_ACCESS_TOKEN}`, }, }); if (error) { // error.message, error.code, error.error (HTTP status text) throw new Error(`createOrg failed: ${error.message}`); } console.log("Created org:", data.id, data.slug); ``` `WORKOS_ACCESS_TOKEN` is the session bearer from your WorkOS signup/login — no `phosra_` key exists yet at this point, and none is needed. Once the org exists, `createApiKey` (same session bearer) mints your first `phosra_`-prefixed key. ### One-time secrets `DeveloperApiKeyWithSecret.key` and `CreatedMcpToken.plain` are returned only once — at create or regenerate time — and are never stored server-side. Save them immediately. *** ## Base URLs | Environment | Base URL | | ----------- | ----------------------------------- | | Production | `https://prodapi.phosra.com/api/v1` | | Local dev | `http://localhost:8080/api/v1` | *** ## Further reading * [Create your account & get keys →](/platform/create-account) — the live self-serve funnel: signup → org → first `phosra_` key * [Phosra Developer SDK →](/sdks/phosra-developer-sdk) — full SDK reference: both the management half and the `@openchildsafety/ocss` protocol re-export * [OCSS overview →](/ocss/overview) — the open standard this platform implements * [Authentication →](/authentication) — API key formats, scopes, and rotation * [/ocss/status →](/ocss/status) — live vs. preview status for every surface # Phosra Link Branding Requirement Source: https://docs.phosra.com/sdks/branding Every Phosra Link connect/consent surface MUST render the published, branded kit — the phosra · OCSS lockup, the Trust-List trust signals, and the "never a fake green" honesty rule. Mandatory and assessed. **This is a requirement, not a style suggestion.** A parental-controls app (PCA) that ships a Phosra Link connect flow **MUST** present it through the published kit, which renders the Phosra Link branding. Hand-rolling or restyling the branded consent is **non-conformant** (see [Conformance](#5-conformance)). Phosra Link is the parent's trust anchor the way Plaid Link is the account-linking one. A parent who has connected one platform through Phosra Link should recognize the **exact same** branded consent on the next platform, in the next app. **Consistency is the trust mechanism** — so the branding is mandatory and cannot be altered per-app. ## 1. The requirement Any Phosra Link **connect** or **consent** surface — the sheet where a parent reviews what will be applied and grants consent — **MUST** display the Phosra Link branding: * **The `phosra · OCSS` lockup** — the phosra wordmark (spark integrated as the "o") next to the OCSS bracket-document mark. Not one or the other; the lockup. * **The trust signals**, verbatim: * **"Accredited on the OCSS Trust List."** — shown up front (the router is accredited). * **"Verified on the OCSS Trust List."** — the **only** green, shown on success. * **The "never a fake green" honesty rule** — the verified green appears **only** after the server binds the grant and verifies it to the OCSS root. Never on load, never optimistically, never as decoration. **A PCA MUST use the published kit — it renders all of the above; a PCA does not.** | Platform | Package | Repo / import | | ------------------ | ------------------- | ------------------------------------------- | | iOS | **PhosraLinkKit** | `github.com/Phosra-Inc/phosra-link-kit-ios` | | Web / React Native | **@phosra/connect** | `@phosra/connect` | A PCA **MUST NOT** hand-roll the consent screen, wrap the kit in a chrome that hides its marks, override its type/color, or re-implement the ceremony behind its own UI. The branded consent is **the kit's component** — a PCA styles what surrounds the sheet, never the sheet. ## 2. Why it is enforced The parent is trusting *Phosra Link* — a recognizable, consistent, accredited surface — not each vendor's bespoke screen. That recognition is only worth anything if it is **identical everywhere**, exactly as Plaid Link is identical across every bank and app. Restyled or ad-hoc consent breaks the recognition and, with it, the trust. Enforcement is structural: the branded consent (marks, trust signals, the fail-closed green) lives **inside** the kit component, not in PCA code. A PCA — or a platform — cannot strip it without replacing the kit, and replacing the kit is the non-conformant act the assessor checks for. ## 3. Platform co-branding The platform's own OAuth / login leg **MUST** co-brand: > **`Phosra Link · `** Never a bare, unbranded auth form. The parent must see, at the login step, that they are still inside a Phosra Link ceremony connecting *this* platform — not dropped onto an anonymous page. The kit carries the platform's **verified** logo (from its accredited Trust-List entry, via `GET /providers/{did}/connect` → `icon_url`), never a logo the app pastes in. ## 4. The "Managed via Phosra" provenance label Co-branding (§3) covers the **moment of connection**. Provenance covers **everything after**: once a child's account or profile is governed by a Phosra-delivered enforcement profile, the platform **MUST** surface a persistent, ambient **"Managed via Phosra"** provenance indicator on that account/profile — so a parent or user can tell, at any time, that the account is externally managed and where the rules come from. * **Where:** wherever the platform shows account/profile status (settings header, a managed-account badge, the parental-controls pane). It is **persistent**, not a one-time toast. * **Wording:** **"Managed via Phosra"** (or **"Managed via Phosra Link"**). It states provenance — it is **not** a trust-verified green and MUST NOT borrow the verified-green treatment from §1. * **Honesty:** show it only while an active enforcement profile is bound. If the connection is revoked and the profile clears, remove the label — never leave stale provenance. This is distinct from the connect/consent marks: those prove the *ceremony* was genuine; the provenance label keeps the *ongoing* management legible long after the sheet is dismissed. ## 5. Conformance Shipping an **unbranded, ad-hoc, or restyled** Phosra Link connect surface — or omitting the "Managed via Phosra" provenance label on a managed account — is **NON-CONFORMANT**. The branding requirement is **assessed** — it is part of the OCSS conformance surface, checked at [Production Accreditation](/integration/production-accreditation) by the governance-designated verifying-agency (the Touchstone role) alongside the crypto and enclave checks: * **Conformant** — the connect/consent surface is rendered by PhosraLinkKit or `@phosra/connect` at a supported version, with the `phosra · OCSS` lockup and the Trust-List trust signals intact, the verified green gated on a root-verified grant, and a persistent **"Managed via Phosra"** provenance label on every managed account/profile. * **Non-conformant** — a hand-rolled consent screen; the kit restyled so its marks or trust signals are hidden/altered; a green "verified"/"connected" state shown before the server binds and verifies the grant (a **fake green**); a bare platform auth form with no `Phosra Link · ` co-brand; or a managed account with **no "Managed via Phosra" provenance** indicator. A non-conformant branding finding blocks the entry's promotion off `standin` to a production-accredited Trust-List entry. ## The published assets (do not re-create) These render the required branding. Cite them; do not fork or reimplement them. **iOS — PhosraLinkKit** * `Sources/PhosraLinkKit/Views/Marks.swift` — the real marks as source: `PhosraWordmark`, `OcssMark`, `Spark`, `CheckMark` (verbatim geometry, tinted by color — no asset bundle). * `Sources/PhosraLinkKit/Views/BrandTokens.swift` — the palette. The verified green is `BrandTokens.deep` (`#00794A`); it is applied **only** on the verified state. **Web — @phosra/connect** * `src/web/assets.tsx` — `PhosraWordmark`, `OcssMark`, `Spark` (inlined SVG, verbatim geometry). * `src/web/connect.css` — the branded stylesheet: `.phosra-connect__brand`, `.phosra-connect__wordmark`, `.phosra-connect__ocss`, and the `--pc-green600` token. Ship it as-is; do not override its rules. * `src/web/ConnectFlow.tsx` — the trust copy lives here: `data-phosra-connect="trust"` → *"Accredited on the OCSS Trust List."*, and `data-phosra-connect="verified"` → *"Verified on the OCSS Trust List."* (the only green). The honesty contract is load-bearing: your `bind` route must return a `grant_id` **only once** the consent is minted *and verified to the OCSS root*. The kit's green rests on that signal alone — returning an unverified `grant_id` makes the kit show a fake green, which is non-conformant. See [PhosraLinkKit → The honesty contract](/sdks/link-ios#the-honesty-contract). # @phosra/link Source: https://docs.phosra.com/sdks/link Writer-plane SDK for providers — signed consent ceremonies, rule directives, and the platform connect flow `@phosra/link` is the writer-plane SDK. It runs in a **provider's backend** (a parental-controls app's server), signs everything locally over RFC 9421, and posts to the hosted OCSS census. If you are integrating end-to-end, start with the [Provider quickstart](/integration/provider) — this page is the package reference for the pieces the quickstart doesn't cover in depth: platform resolution and parent sessions. ```bash theme={null} npm install @phosra/link ``` **`@phosra/link` is ESM-only** — its `package.json` exposes only an `import` condition (no `require`). Use it from an ESM module (`"type": "module"`, or a `.mjs` file, or `import` in TypeScript compiled to ESM). From a CommonJS module, load it with a dynamic import — `const link = await import('@phosra/link')` — not `require('@phosra/link')`. ## LinkConfig ```typescript theme={null} import type { LinkConfig } from '@phosra/link'; import pg from 'pg'; const config: LinkConfig = { censusBaseUrl: 'https://phosra-api-sandbox-production.up.railway.app', // production sandbox — canonical testing endpoint trustRootXB64Url: 'CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU', // sandbox trust-root public key X (see note below) parentKey: { seed: parentSeed, keyID: 'did:ocss:household-acme#parent-key-2026' }, writerKey: { seed: writerSeed, keyID: 'did:ocss:your-org#writer-key-2026' }, writerDid: 'did:ocss:your-org', routerDid: 'did:ocss:phosra-router', householdSecret: process.env.HOUSEHOLD_SECRET!, // never sent to the census parentSessionSecret: process.env.PARENT_SESSION_SECRET, // enables verified parent sessions pool: new pg.Pool({ connectionString: process.env.LINK_DB_URL }), developerOrgId: process.env.PHOSRA_DEV_ORG_ID, // billing attribution — optional, see note below }; ``` `trustRootXB64Url` above (`CMHWy3vUAiEcYDdE_bDvkRuEqwxkklS0tV-TYHJTlWU`) is a **PUBLIC** key, pinned here out-of-band — that's the point of root verification: you don't fetch the trust root from the same census you're about to verify. It's safe to hardcode or check into your own config. `developerOrgId` (a `phosra_live_...`-issued org) is **billing-attribution only** — the quickstart above runs correctly without one; omit it and the family lands in the anonymous/self-host bucket (no invoice, no SLA). The SDK is free and open (MIT). Signing and verification happen locally; billing is enforced server-side against your org — never inside this package, and never on the safety path. ## How platforms are resolved Before `initPlatformOAuth` builds an authorize URL, the SDK resolves the target platform in two layers: 1. **OCSS Trust List — the trust gate.** The signed trust list at `/.well-known/ocss/trust-list` is fetched and verified against the trust root. The platform's DID must be an `active`, unexpired entry. Absent, suspended, revoked, or expired all **fail closed** — you cannot connect a child to a non-accredited platform, and the next layer is never consulted. 2. **Phosra connect registry — the mechanics.** The platform's OAuth `authorize`/`token`/`profiles` endpoints come from [`GET /providers/{did}/connect`](/api-reference/providers/connect), a Phosra product endpoint. It is deliberately **not** part of the signed OCSS document: the standard stays vendor-neutral (trust, keys, accreditation); connection mechanics are Phosra's concern as the OCSS implementor. Both layers run inside `initPlatformOAuth`/`completePlatformOAuth`; you don't call them directly. A `404` from the connect registry surfaces as `link/provider: no Phosra connect config for `. **Which DID to test against.** Use **`did:ocss:loopline`** — against the production sandbox census (`https://phosra-api-sandbox-production.up.railway.app`) it is the DID with a live connect config, so the connect ceremony actually loads and completes. `GET /api/v1/providers/did:ocss:loopline/connect` returns its `authorize`/`token`/`profiles` endpoints, and `initPlatformOAuth` will build a valid authorize URL for it. Most other trust-listed DIDs are accredited but not yet connect-configured (that intentional 404 is the "accredited-but-unconfigured" case). To have a connect config published for a provider you operate, email [developers@phosra.com](mailto:developers@phosra.com) with the DID and its OAuth endpoints. ## Parent sessions The parent authenticates with **your app's own auth** — Phosra never sees parent credentials. What the SDK needs is a trustworthy binding between your authenticated parent session and the OAuth ceremony, so one parent's state can never be completed by another parent, and an expired login cannot finish a ceremony it started. **Verified mode (recommended).** Set `parentSessionSecret` in `LinkConfig`. After your login succeeds, issue a signed, expiring token server-side and pass it as `parentSessionRef` on all three ceremony legs (an `HttpOnly; Secure` cookie works well): ```typescript theme={null} import { issueParentSession } from '@phosra/link'; // at login time, server-side: const token = issueParentSession(process.env.PARENT_SESSION_SECRET!, { parentId: user.id, // your stable parent identifier ttlMs: 30 * 60 * 1000, // default 30 minutes }); ``` The SDK verifies the token (HMAC + expiry, fail closed) on `initPlatformOAuth`, `completePlatformOAuth`, **and** `bindProfile`, and binds the ceremony to the verified session id — a re-issued token for the same session still completes, an expired one does not, and another parent's valid token is rejected. **BYO mode.** Leave `parentSessionSecret` unset and pass your own server-side session identifier as `parentSessionRef` (as the [Provider quickstart](/integration/provider) shows with `req.session.id`). The contract: derive it server-side from your authenticated session; never accept it from the client. ## Reference BFF A complete, framework-free reference implementation of the three connect routes — including the login-cookie wiring — is reproduced **in full below**. It is the exact PCA-side server the iOS `PhosraLinkKit` and the web `@phosra/connect` transport expect. Copy it verbatim; the only production changes are called out under [Wiring it for production](#wiring-it-for-production). The three routes, and the `@phosra/link` call each wraps: | Route | Wraps | Returns | | ------------------------ | ------------------------------- | ------------------------------------ | | `POST /connect/init` | `initPlatformOAuth` | `{ authorizeUrl, state, sessionId }` | | `POST /connect/complete` | `completePlatformOAuth` | `{ sessionId, childProfiles }` | | `POST /connect/bind` | `bindProfile` → your `ceremony` | `{ grant_id }` | A fourth route, `POST /auth/login`, is a **demo stand-in** for the PCA's own login: a real PCA authenticates the parent first (password / passkey / SSO), then issues the `phosra_parent` cookie. What matters is that after login the BFF holds an HMAC-signed, expiring parent-session token and passes the **raw** cookie token to `@phosra/link` on every connect leg — the client never chooses its own `parentSessionRef`. ### server.ts ```typescript theme={null} // The PCA-side wiring @phosra/connect's transport (and PhosraLinkKit) expects. // Framework-free (node:http). The parent authenticates with the PCA's OWN auth // (Plaid model); after login the BFF issues an HMAC-signed, expiring parent-session // token as an HttpOnly cookie, and every connect route passes the RAW cookie token // to @phosra/link, which verifies it against cfg.parentSessionSecret and binds the // ceremony to the verified sid. import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http" import { issueParentSession, initPlatformOAuth, completePlatformOAuth, bindProfile, type PlatformSessionStore, type LinkConfig, type LinkSession, } from "@phosra/link" const COOKIE = "phosra_parent" function readCookie(req: IncomingMessage): string | null { const header = req.headers.cookie ?? "" for (const part of header.split(";")) { const [k, ...v] = part.trim().split("=") if (k === COOKIE) return v.join("=") } return null } function readBody(req: IncomingMessage): Promise { return new Promise((resolve, reject) => { let data = "" req.on("data", (c) => (data += c)) req.on("end", () => { try { resolve(data ? JSON.parse(data) : {}) } catch (e) { reject(e) } }) req.on("error", reject) }) } function sendJson(res: ServerResponse, status: number, body: unknown): void { res.writeHead(status, { "content-type": "application/json" }) res.end(JSON.stringify(body)) } export function createReferenceBff( cfg: LinkConfig, deps: { store: PlatformSessionStore fetchImpl?: typeof fetch // Runs the post-bind ceremony (completeLink + label delivery). Injected so the // example stays census-agnostic; production wires runConnectCeremony here. ceremony: (session: LinkSession) => Promise<{ grant_id: string }> }, ): Server { return createServer(async (req, res) => { try { if (req.method !== "POST") return sendJson(res, 404, { error: "not found" }) const body = await readBody(req) if (req.url === "/auth/login") { // DEMO stand-in for the PCA's real login. A real PCA authenticates the // parent first (password/passkey/SSO), THEN issues this token. if (!body.parentId) return sendJson(res, 400, { error: "parentId required" }) const token = issueParentSession(cfg.parentSessionSecret!, { parentId: body.parentId }) res.writeHead(200, { "content-type": "application/json", "set-cookie": `${COOKIE}=${token}; HttpOnly; Secure; Path=/; SameSite=Lax`, }) return res.end(JSON.stringify({ ok: true })) } const CONNECT_ROUTES = new Set(["/connect/init", "/connect/complete", "/connect/bind"]) if (!CONNECT_ROUTES.has(req.url ?? "")) return sendJson(res, 404, { error: "not found" }) // Every connect route requires the session cookie; @phosra/link verifies it. const token = readCookie(req) if (!token) return sendJson(res, 401, { error: "not logged in" }) if (req.url === "/connect/init") { const out = await initPlatformOAuth( cfg, { platformDid: body.platformDid, redirectUri: body.redirectUri, parentSessionRef: token, childHint: body.childHint }, { store: deps.store, fetchImpl: deps.fetchImpl }, ) return sendJson(res, 200, out) } if (req.url === "/connect/complete") { const out = await completePlatformOAuth( cfg, { code: body.code, state: body.state, parentSessionRef: token }, { store: deps.store, fetchImpl: deps.fetchImpl }, ) return sendJson(res, 200, out) } if (req.url === "/connect/bind") { const session = await bindProfile( cfg, { sessionId: body.sessionId, platformChildProfileId: body.platformChildProfileId, childId: body.childId, granted_scope: body.grantedScope, ageHint: body.ageHint, parentSessionRef: token, }, { store: deps.store }, ) const { grant_id } = await deps.ceremony(session) return sendJson(res, 200, { grant_id }) } return sendJson(res, 404, { error: "not found" }) } catch (e) { const msg = e instanceof Error ? e.message : String(e) // link/session: * = auth-plane failures → 401; everything else → 400. return sendJson(res, msg.startsWith("link/session:") ? 401 : 400, { error: msg }) } }) } ``` ### Database schema The BFF needs a Postgres database with **two tables** — the OAuth ceremony session store and the product-side grant index. Both are product-plane; the OCSS census never reads either (the §12.3 cut-test). Apply this DDL to the database you pass as `LinkConfig.pool`: ```sql theme={null} -- Product-side OAuth ceremony session store. Holds the BFF-only PKCE secret -- (code_verifier never leaves the server; only the S256 code_challenge goes into -- the authorize URL) and a single-use, session-bound state. CREATE TABLE IF NOT EXISTS phosra_link_platform_sessions ( session_id uuid PRIMARY KEY DEFAULT gen_random_uuid(), audience_did text NOT NULL, platform_did text NOT NULL, redirect_uri text NOT NULL, code_verifier text NOT NULL, code_challenge text NOT NULL, state text NOT NULL, parent_session_ref text NOT NULL, status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','authed','bound')), child_profiles jsonb, platform_child_profile_ref text, target_ref text, age_hint text CHECK (age_hint IN ('under_13','13_15','16_17')), granted_scope text[], created_at timestamptz NOT NULL DEFAULT now(), expires_at timestamptz NOT NULL ); CREATE UNIQUE INDEX IF NOT EXISTS uq_link_platform_session_state ON phosra_link_platform_sessions (state); -- Product-side grant index: maps a parent consent grant to its §8.3.2 attestation -- idem key once the ceremony completes. Single-active invariant: at most one active -- grant per (audience_did, subject_ref, target_ref). CREATE TABLE IF NOT EXISTS phosra_link_grants ( grant_id uuid PRIMARY KEY DEFAULT gen_random_uuid(), audience_did text NOT NULL, subject_ref text NOT NULL, target_ref text NOT NULL, granted_scope text[] NOT NULL, attestation_idem_key text, status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','revoked')), created_at timestamptz NOT NULL DEFAULT now(), revoked_at timestamptz ); CREATE UNIQUE INDEX IF NOT EXISTS uq_link_grant_active ON phosra_link_grants (audience_did, subject_ref, target_ref) WHERE status = 'active'; CREATE INDEX IF NOT EXISTS ix_link_grant_idem ON phosra_link_grants (attestation_idem_key); ``` `gen_random_uuid()` requires the `pgcrypto` extension (`CREATE EXTENSION IF NOT EXISTS pgcrypto;`) on older Postgres; it is built in from Postgres 13+. ### Wiring it for production `createReferenceBff` above is complete and framework-free. To take it to production: * **Real login.** Replace `/auth/login` with your app's actual authentication; issue the `phosra_parent` cookie only *after* the parent is authenticated. * **Postgres session store.** Build the store over your pool with the exported `makePlatformSessionStore(cfg.pool)` (against the `phosra_link_platform_sessions` table above) rather than an in-memory stub, so ceremonies survive restarts. * **Real ceremony.** Inject `runConnectCeremony` as `deps.ceremony` — it calls `completeLink` (which signs the §8.3.2 consent attestation with your OCSS key, posts it to the census, and writes the `phosra_link_grants` row) and delivers the rule labels to the platform. **It must return a `grant_id` only after the consent is minted *and* verified to the OCSS root** — the sheet's green "Verified" rests on that signal alone. * **Cookie hardening.** The `set-cookie` already sets `HttpOnly; Secure`; add a `Max-Age` matched to the parent-session token's TTL. **This page is the authoritative copy.** The `server.ts`, the DDL, and the production wiring above are complete and copy-pasteable on their own — you do **not** need repo or tarball access to build the BFF. As of `@phosra/link@0.1.2` the tarball also ships the same reference source under `node_modules/@phosra/link/examples/reference-bff/` (`server.ts` + `README.md`) as a convenience — but if your installed copy predates that or lacks the directory, copy the source from this page. ## Branding is mandatory The `@phosra/connect` / PhosraLinkKit drop-ins render the **Phosra Link branding** — the `phosra · OCSS` lockup, the "Accredited / Verified on the OCSS Trust List" trust signals, and the "never a fake green" honesty rule. This is **required, not optional**: any Phosra Link connect/consent surface MUST use the published kit, which renders it — a PCA may not hand-roll or restyle the branded consent, and the platform auth leg MUST co-brand `Phosra Link · `. Like Plaid Link, the parent recognizes the same surface everywhere — consistency is the trust mechanism, and it is **assessed** at accreditation. See the [Phosra Link Branding Requirement](/sdks/branding). ## Beyond connect The ceremony, rule writes (`directive`), and family-wide fan-out (`convergeFamily`) are covered step-by-step in the [Provider quickstart](/integration/provider). For the platform side of the handshake, see the [Platform quickstart](/integration/platform). The protocol primitives themselves are `@openchildsafety/ocss`, re-exported by the [Phosra Developer SDK](/sdks/phosra-developer-sdk) — Phosra implements the standard; it does not own it. # Phosra Link for iOS Source: https://docs.phosra.com/sdks/link-ios PhosraLinkKit — the native-iOS Connect sheet a parental-controls app presents in ~10 lines (the Plaid-Link analog). **PhosraLinkKit** is the native-iOS **Phosra Link** — a Phosra-branded *Connect* sheet a parental-controls app (PCA) presents in about ten lines, with `onSuccess` / `onExit` callbacks. It's the iOS peer of the web/React-Native [`@phosra/connect`](/sdks/link) drop-in, and it drives the same [`@phosra/link`](/sdks/link) ceremony — `init → complete → bind → grant` — through **your own backend (BFF)**. The parent's authentication and your OCSS signing key **never leave your app** (self-custody by default). The SDK holds no keys; your BFF signs the consent attestation. Same trust model as the rest of OCSS, same ergonomics as Plaid's iOS SDK. ## Install Swift Package Manager, iOS 16+: ```swift theme={null} .package(url: "https://github.com/Phosra-Inc/phosra-link-kit-ios.git", from: "0.1.0") ``` During monorepo development you can also depend on it by path (`.package(path: "packages/ios-connect")`). ## Quickstart ```swift theme={null} import PhosraLinkKit let config = PhosraLinkConfiguration( bffBaseURL: URL(string: "https://app.yourpca.com")!, // your backend sessionToken: myParentSessionToken, // the token YOUR login issued platform: ConnectPlatform(did: "did:ocss:loopline", name: "Loopline"), rules: [ ConnectRule(category: "addictive_pattern_block", label: "Turn off the infinite feed"), ConnectRule(category: "dm_restriction", label: "Limit who can message them"), ], grantedScope: ["addictive_pattern_block", "dm_restriction"], redirectUri: "yourapp://phosra-link", // a custom scheme your app registers childId: "child:uuid" // optional ) let handler = PhosraLink(configuration: config) handler.present( from: self, // any UIViewController onSuccess: { success in print("granted:", success.grantId) // the OCSS consent grant }, onExit: { exit in // .userCanceled or .error — no rules were changed } ) ``` That's the whole integration. Exactly one of `onSuccess` / `onExit` fires per presentation — including if the presenter is torn down out from under the sheet — and you do **not** need to retain the handler (it self-retains while presented). The platform's OAuth runs in a system `ASWebAuthenticationSession`, so your app never sees the parent's platform credentials. The Connect sheet renders **in your app** — there is no Phosra-hosted page or web view. The examples above use **`did:ocss:loopline`** — the DID with a live sandbox connect config, so the ceremony actually loads against the production sandbox census. Other DIDs return the "accredited-but-unconfigured" `404`; email [developers@phosra.com](mailto:developers@phosra.com) to have one published. See [How platforms are resolved](/sdks/link#how-platforms-are-resolved). ## End-to-end The whole integration on one page — client **and** backend: Add your custom scheme (e.g. `yourapp`) to the app target's `Info.plist` `CFBundleURLTypes`, so `ASWebAuthenticationSession` can receive the platform's OAuth callback at `yourapp://phosra-link`. The parent signs in with **your** app's auth — Phosra never sees their credentials. On success, mint a **server-side** session token (the reference BFF issues a signed `phosra_parent` cookie via `issueParentSession`). That token is the `sessionToken` you pass the SDK. Never accept it from the client. `POST /connect/init | /connect/complete | /connect/bind`, each wrapping `@phosra/link` server-side — see the [reference BFF](/sdks/link). Your `bind` route signs the consent with your OCSS key and returns `{ grant_id }`. Build `PhosraLinkConfiguration` (the Quickstart above) and call `PhosraLink(configuration:).present(from:onSuccess:onExit:)`. `onSuccess` delivers the `grantId` — persist it and drive rule directives with `@phosra/link`'s `directive(...)`. `onExit` means the parent canceled or errored; nothing changed. Metered census usage requires a Phosra API key — provision one in the developer console or via `POST /api/v1/developers/orgs/{orgID}/keys`. ## What your backend provides `PhosraLinkKit` never talks to Phosra directly. It calls **your** BFF — the three routes the [reference BFF](/sdks/link) exposes, each wrapping `@phosra/link` server-side: | Route | Wraps (`@phosra/link`) | | ------------------------ | ---------------------------------------------------------- | | `POST /connect/init` | `initPlatformOAuth` → `{ authorizeUrl, state, sessionId }` | | `POST /connect/complete` | `completePlatformOAuth` → `{ sessionId, childProfiles }` | | `POST /connect/bind` | `bindProfile` → `runConnectCeremony` → `{ grant_id }` | Your `bind` route signs the consent attestation with **your** OCSS key and posts it to the census. The SDK relays your parent-session token (a `phosra_parent` cookie by default; override `sessionHeaderName` / `sessionHeaderValue` for a bearer). This is Plaid's `link_token` pattern — a thin backend mints the session and finalizes the signature. ## Platform logos The sheet shows the platform's **verified** logo — the one from its accredited Trust-List entry, not a logo the app pastes in. `GET /api/v1/providers/{did}/connect` returns the provider's `name` and `icon_url` (from the registry); pass that URL into `ConnectPlatform`: ```swift theme={null} ConnectPlatform( did: "did:ocss:loopline", name: "Loopline", logoURL: URL(string: providerIconURL) // from /providers/{did}/connect → icon_url ) ``` The sheet renders it, falling back to a generic glyph while it loads or if it's missing. For a bundled asset instead of a URL, pass `platformLogo:` on the configuration. Because the logo comes from the accredited registry, *the logo you see is the one Phosra verified* — it's part of the provider's identity, not decoration. ## The honesty contract The sheet ports the approved design's honesty contract verbatim, and the SDK **cannot show a fake green**: * The header reads **phosra · OCSS** (a subordinate mark) and the trust claim is the precise **"Accredited on the OCSS Trust List"** — Phosra is an accredited router, not OCSS itself. * Success shows **"Verified on the OCSS Trust List"** — the only green — and only after the server binds the grant. * On failure: *"No rules were changed — you can try again."* **Load-bearing invariant:** your `bind` route must return a `grant_id` **only once** the consent is minted *and verified to the OCSS root*. The green "Verified" rests on that signal alone — return an unverified `grant_id` and you have made the SDK show a fake green. ## States The sheet walks: **Connect** (intro + accreditation) → **What gets applied** (the rules preview) → the platform's OAuth → **Choose account** → **Verified** success — or an honest **error** at any step. All rendered natively (SwiftUI); the marks (the Phosra spark, the wordmark, the OCSS glyph) ship as source and tint by color, so there is no asset bundle to manage. ## Advanced * **Custom transport.** The 4-argument `PhosraLinkConfiguration.init` takes your own `ConnectTransport` if you route the three calls differently. * **Fresh platform login.** `PhosraLinkConfiguration(prefersEphemeralSession: true)` forces a new platform sign-in (no shared cookies). * **Granular events.** Pass `onEvent` to observe each ceremony transition. ## Notes * iOS 16+. The core (controller, transport, sheet) is UI-framework-agnostic and unit-tested; the `ASWebAuthenticationSession` presenter is iOS-only. * **MIT-licensed.** All signing and verification are your backend's; metered census usage requires a Phosra API key, billed server-side — never in this package. # Phosra Developer SDK Source: https://docs.phosra.com/sdks/phosra-developer-sdk TypeScript SDK for the Phosra developer control plane — OCSS-conformant. Two halves: management (generated REST client) and protocol (@openchildsafety/ocss re-export). `@phosra/sdk-dev` is the TypeScript SDK for developers building on the Phosra platform. It has two distinct, clearly-named halves: a **management** client for the Phosra control-plane REST API, and an **OCSS protocol** surface that re-exports `@openchildsafety/ocss` — the open standard's reference implementation — verbatim. ## Phosra follows OCSS; it does not own it Phosra is an accredited provider on the Open Child Safety Specification (OCSS). The relationship is the same as Yubico shipping a FIDO2-conformant authentication key: Yubico implements FIDO2; the FIDO Alliance owns the standard. Phosra implements OCSS; the OCSS stewardship body owns the standard. The consequence for this SDK: the protocol signing and verification primitives (`@phosra/sdk-dev/protocol`) are not Phosra code — they are `@openchildsafety/ocss`, the vendor-neutral OCSS library, re-exported without modification. Phosra adds zero cryptographic logic to the protocol surface. If you later integrate a different OCSS-conformant provider, the `/protocol` subpath is identical across all of them. *** ## Installation **`@phosra/sdk-dev` is a planned *unified* wrapper — not yet built or published.** This page describes the eventual two-half shape. Today the surface ships as **individual published packages**, not a single `@phosra/sdk-dev`: | Need | Install | | -------------------------------------------------------- | ----------------------- | | OCSS protocol (sign/verify/seal, trust list, vocabulary) | `@openchildsafety/ocss` | | Phosra control-plane management client | `@phosra/sdk` | | Platform-side enforcement (`refreshProfile`/`isAllowed`) | `@phosra/gatekeeper` | | Provider-side rule writes (`directive`/`convergeFamily`) | `@phosra/link` | | CLI / quickstart | `@phosra/cli` | All five packages are published to npm today — install them directly using the block below. ```bash theme={null} # Published packages — install today: npm install @openchildsafety/ocss @phosra/sdk @phosra/gatekeeper @phosra/link ``` `@openchildsafety/ocss` is a **peer dependency** — the open OCSS library. Installing it separately keeps the OCSS version pinnable independently of the Phosra management client version. > **Naming note:** `@phosra/sdk-dev` is the *working name* for a future unified wrapper; the management client publishes today as `@phosra/sdk`. The unified package's existence and final name are a publish-time decision. The `@phosra/sdk-dev/...` import paths in the examples below illustrate the planned unified shape. *** ## Using `@phosra/sdk` today (published — the real 0.1.0 API) The "Two halves" / `client.setConfig` / `createOrg` examples further down describe the **planned unified `@phosra/sdk-dev` wrapper**, which is **not yet published**. The package you install today — **`@phosra/sdk@0.1.0`** — has a **different, class-based shape**: a single `PhosraClient` with resource namespaces (`families`, `children`, `policies`, `rules`, `setup`, …). Use the code in *this* section against the published package. `@phosra/sdk` exports one class, `PhosraClient`. Construct it with a credential and (for the sandbox) a `baseUrl`, then reach the data-plane through resource namespaces: ```typescript theme={null} import { PhosraClient } from "@phosra/sdk"; // Server-to-server: a phosra_ API key → sent as the X-Api-Key header. const phosra = new PhosraClient({ apiKey: process.env.PHOSRA_API_KEY, // phosra_test_… or phosra_live_… baseUrl: "https://phosra-api-sandbox-production.up.railway.app/api/v1", // sandbox }); // Parent-app / consumer: a WorkOS access token → sent as Authorization: Bearer. // const phosra = new PhosraClient({ accessToken: workosAccessToken }); ``` **Credential → header mapping** (precedence `deviceKey` > `apiKey` > `accessToken`): | Config field | Header sent | Use for | | -------------------------------------- | ------------------------------------ | --------------------- | | `apiKey` | `X-Api-Key: phosra_…` | Server / B2B / agent | | `accessToken` | `Authorization: Bearer ` | Parent app (consumer) | | `deviceKey` (`PhosraClient.forDevice`) | `X-Device-Key: phosra_dev_…` | On-device enforcement | `baseUrl` defaults to `https://prodapi.phosra.com/api/v1`; override it for the sandbox (above) or `http://localhost:8080/api/v1` for local dev. ### Family → child → policy → rule ```typescript theme={null} const family = await phosra.families.create({ name: "Acme Household" }); const child = await phosra.children.create(family.id, { name: "Mia", birth_date: "2013-01-15" }); const policy = await phosra.policies.create(child.id, { name: "Mia — default" }); // Apply a rule (one of the OCSS rule categories — see /ocss/rule-reference) const rule = await phosra.rules.create(policy.id, { category: "addictive_pattern_block", enabled: true, config: {}, }); const rules = await phosra.rules.list(policy.id); // read back ``` Or the one-step onboarding helper: ```typescript theme={null} const setup = await phosra.setup.quick({ /* QuickSetupRequest */ }); ``` `X-Api-Key` authenticates the **Phosra control-plane data plane** (families/children/policies/rules) — the platform's own store. It does **not** authenticate an **OCSS census verb** (a signed rule write, enforcement confirmation, harm-context, or consent attestation): those are RFC-9421 DID-signed only. If you need signed census writes, use `@phosra/link` (provider side) / `@phosra/gatekeeper` (platform side), not this management client. See [Authentication](/authentication). Resource namespaces on `PhosraClient`: `auth`, `families`, `children`, `members`, `policies`, `rules`, `enforcement`, `platforms`, `compliance`, `webhooks`, `ratings`, `standards`, `setup`, `devices`, `reports`, `sources`. All types (`Family`, `Child`, `ChildPolicy`, `PolicyRule`, `RuleCategory`, …) and error classes (`PhosraApiError`, `PhosraAuthError`, …) are re-exported from the package root. *** ## Two halves ### Half 1 — Management: `@phosra/sdk-dev` ```typescript theme={null} import { createOrg, createApiKey, registerAdvisor } from "@phosra/sdk-dev"; ``` The root import is the generated control-plane client, produced from the Phosra OpenAPI spec by [hey-api](https://heyapi.dev/). It covers: * **Organizations** — create, read, update, delete developer orgs; list members. * **API keys** — provision, revoke, and rotate `phosra_`-prefixed keys. * **Usage** — hourly request rollups per org. * **Advisor agents** — register, consult, and declare payload keys for OCSS federation. * **MCP tokens** — mint and revoke Model Context Protocol authentication tokens. The root import is intentionally **crypto-free**: no signing primitives are accessible here. Management operations use standard HTTP Bearer authentication with a `phosra_` API key. ### Half 2 — OCSS protocol: `@phosra/sdk-dev/protocol` ```typescript theme={null} import { signReceipt, verifyReceipt } from "@phosra/sdk-dev/protocol"; import { seal, open } from "@phosra/sdk-dev/protocol/envelope"; ``` The `/protocol` subpath is `@openchildsafety/ocss` re-exported verbatim — the open standard's reference TypeScript implementation. Phosra adds nothing here. This surface covers: * **Receipt signing and verification** — sign and verify OCSS write receipts against the Trust Framework. * **Sealed envelopes** — router-blind sealed payloads (the `harm_context` lane). * **Vocabulary, canon, and checksums** — OCSS rule vocabulary, canonical JSON serialization, family hash derivation. * **Trust list** — resolve and verify the OCSS Trust List. *** ## Quick start ### Configure the management client ```typescript theme={null} import { client } from "@phosra/sdk-dev"; client.setConfig({ baseUrl: "https://prodapi.phosra.com/api/v1", headers: { // Management calls (orgs, keys, usage) take your WorkOS session bearer // from signup/login — see /platform/create-account. The phosra_ key you // mint authenticates the /developer/* data-plane routes instead. Authorization: `Bearer ${process.env.WORKOS_ACCESS_TOKEN}`, }, }); ``` ### Create a developer organization ```typescript theme={null} import { createOrg } from "@phosra/sdk-dev"; const { data, error } = await createOrg({ body: { name: "Acme Corp", description: "Acme's OCSS integration", website_url: "https://acme.example.com", }, headers: { Authorization: `Bearer ${process.env.WORKOS_ACCESS_TOKEN}`, }, }); if (error) { // error.message, error.code, error.error (HTTP status text) throw new Error(`createOrg failed: ${error.message}`); } console.log("Created org:", data.id, data.slug); ``` `WORKOS_ACCESS_TOKEN` is the session bearer from your WorkOS signup/login session — no `phosra_` key is needed to create your org or mint your first key ([Create your account & get keys](/platform/create-account)). The `phosra_`-prefixed key that `createApiKey` returns authenticates the `/developer/*` data-plane routes. ### Sign an OCSS receipt (protocol half) ```typescript theme={null} import { signReceipt, verifyReceipt } from "@phosra/sdk-dev/protocol"; // Sign const receipt = await signReceipt(body, mySigningKey); // Verify against the published Trust List const result = await verifyReceipt(receipt, trustListResolver); ``` *** ## Management operations reference All operations are generated functions that accept an `Options<…Data>` object and return a `RequestResult` (a Promise resolving to `{ data, error }`). The generic `ThrowOnError` parameter controls whether errors throw (default: `false` — check `error` in the result). | Function | Method + path | Auth | | -------------------------- | ------------------------------------------------------- | ------------------ | | `listOrgs` | `GET /developers/orgs` | session bearer | | `createOrg` | `POST /developers/orgs` | session bearer | | `getOrg` | `GET /developers/orgs/{orgId}` | session bearer | | `updateOrg` | `PUT /developers/orgs/{orgId}` | session bearer | | `deleteOrg` | `DELETE /developers/orgs/{orgId}` | session bearer | | `listOrgMembers` | `GET /developers/orgs/{orgId}/members` | session bearer | | `listApiKeys` | `GET /developers/orgs/{orgId}/keys` | session bearer | | `createApiKey` | `POST /developers/orgs/{orgId}/keys` | session bearer | | `revokeApiKey` | `DELETE /developers/orgs/{orgId}/keys/{keyId}` | session bearer | | `regenerateApiKey` | `POST /developers/orgs/{orgId}/keys/{keyId}/regenerate` | session bearer | | `getOrgUsage` | `GET /developers/orgs/{orgId}/usage` | session bearer | | `registerAdvisor` | `POST /advisors/register` | session bearer | | `consultAdvisor` | `POST /advisors/consult` | session bearer | | `declareAdvisorPayloadKey` | `POST /advisors/{id}/payload-key` | Ed25519 possession | | `listMcpTokens` | `GET /mcp-tokens` | session bearer | | `createMcpToken` | `POST /mcp-tokens` | session bearer | | `revokeMcpToken` | `DELETE /mcp-tokens/{id}` | session bearer | *** ## TypeScript types All types are generated from the Phosra OpenAPI spec and re-exported from the root import: ```typescript theme={null} import type { DeveloperOrg, DeveloperApiKey, DeveloperApiKeyWithSecret, // includes raw `key` — returned once at create/regenerate AdvisorRegistration, McpToken, CreatedMcpToken, // includes raw `plain` token — returned once DeveloperUsage, } from "@phosra/sdk-dev"; ``` `DeveloperApiKeyWithSecret.key` and `CreatedMcpToken.plain` are returned only once (at create or regenerate time) and never stored server-side. Save them immediately. *** ## Base URLs | Environment | Base URL | | ----------- | ----------------------------------- | | Production | `https://prodapi.phosra.com/api/v1` | | Local dev | `http://localhost:8080/api/v1` | The generated client defaults to the production URL. Override with `client.setConfig({ baseUrl: "..." })`. *** ## Subpath exports | Import path | Contents | | ----------------------------------- | ---------------------------------------------------------------------------------------- | | `@phosra/sdk-dev` | Generated management client — all control-plane operations and types | | `@phosra/sdk-dev/protocol` | `@openchildsafety/ocss` re-exported — OCSS signing, verification, vocabulary, trust list | | `@phosra/sdk-dev/protocol/envelope` | `@openchildsafety/ocss` sealed-envelope subpath — `seal` / `open` | The management root (`@phosra/sdk-dev`) does **not** re-export the protocol surface. This is intentional: it keeps crypto off the management import and makes the OCSS/Phosra boundary legible to tree-shakers and to readers. *** ## Further reading * [OCSS specification](https://openchildsafety.com) — the vendor-neutral standard this SDK conforms to * [`@openchildsafety/ocss` package](https://www.npmjs.com/package/@openchildsafety/ocss) — the open OCSS TypeScript library (re-exported as `/protocol`) * [Phosra API reference](/developers/api-reference/overview) — full REST endpoint documentation * [MCP Server](/sdks/mcp-server) — Model Context Protocol integration for AI agents