Provider Quickstart
The private @phosra/link 0.7.10 candidate can be evaluated with synthetic parent,
child, and platform fixtures. The Golden path explores replacing manual
OAuth, trust, signing, endpoint minting, and retry wiring with one credential
and three application adapters.
Private evaluation candidate. Public npm currently serves 0.6.1; the
0.7.10 code below is not a generally available production integration.
Hosted routing, production credentials, and a production receipt rail remain
roadmap. Contact the team about a bounded design-partner pilot.
What you build
- A server-only Link singleton imported by one same-origin catch-all route and one worker entry point.
- Adapters for your existing parent auth, child ownership, and policy facts.
- One durable worker process.
- The SDK-owned
PhosraLinkcomponent in your parent UI.
Link owns the rest: environment and Trust List verification, platform discovery, signed Intent V2, public consent copy, OAuth handoff, durable delivery, retry, and evidence-based status.
1. Create the Golden server
// lib/phosra-link-server.ts — server-only shared singleton
// Node-only module; never import from client components.
import { Pool } from "pg"
import { createGoldenLinkServer } from "@phosra/link/server"
export const phosraLinkServer = await createGoldenLinkServer({
credential: process.env.PHOSRA_CREDENTIAL!,
database: new Pool({ connectionString: process.env.DATABASE_URL }),
authenticate: async (request) => {
const session = await requireYourParentSession(request)
return session === null ? null : {
sessionID: session.id,
parentID: session.parentId,
}
},
children: {
resolve: async ({ principal, childRef }) => {
const child = await children.findOwned(principal.parentID, childRef)
return child === null ? null : {
id: child.id,
displayName: child.name,
ageBand: child.ageBand,
householdRef: child.familyId,
}
},
},
policy: {
resolve: ({ child, platformDid }) =>
policies.rulesFor(child.id, platformDid),
},
})Then mount that same singleton in one catch-all route:
// app/api/phosra/link/[...phosra]/route.ts
import { phosraLinkServer } from "@/lib/phosra-link-server"
export const GET = phosraLinkServer.handler
export const POST = phosraLinkServer.handler
export const DELETE = phosraLinkServer.handlerUse a v3 PHOSRA_CREDENTIAL. It carries the pinned environment authority; do
not copy service URLs, trust roots, signing keys, or platform endpoints into
browser code.
authenticate and children.resolve are authorization boundaries. A child
reference from the request is only a lookup key. Return a child only after
proving it belongs to the authenticated parent.
Household authority and Intent V2
Return a stable provider-local family identifier as householdRef. Link commits
that value into a receiver-specific digest in the signed Intent V2. This enables
safe one-to-many linking—for example, two children in one parental-controls
household can share one Notflix Kids profile—without exposing the raw family ID
to the browser or platform.
The platform still receives a separate, parent-authorized member for each child. Gatekeeper combines those members on the shared target and computes the effective policy. Your provider does not send a platform account ID or choose the merge algorithm.
No provider-owned sharing modal
If the parent selects an occupied profile from the same verified family, Gatekeeper's authorize handler owns the recommendation and explicit consent. It recommends a separate profile, explains strictest-rule aggregation and shared activity attribution, and offers Choose another profile, Confirm sharing, and Cancel and return in that order.
Keep the existing PhosraLink createSession, onEvent, and onExit contract.
Do not pass family, child, platform-profile, target, or policy authority through
browser props, and do not recreate the decision in your application. The initial
screen identifies the authenticated platform catalog label recovered from the
platform's server-side sealed catalog and the same-family member count. The
signed selected-profile presentation follows token exchange; it is not the
source of the confirmation label. Member names and numeric ages remain deferred
until a future provider-signed member-display resolver is available.
2. Run the worker
// phosra-link-worker.ts
import { phosraLinkServer } from "@/lib/phosra-link-server"
const shutdown = new AbortController()
process.once("SIGTERM", () => shutdown.abort())
process.once("SIGINT", () => shutdown.abort())
await phosraLinkServer.runWorker({ signal: shutdown.signal })Run one loop. It serializes durable delivery and signed retry processing. Do not
also schedule runWorkerOnce() in the same process while the loop is active.
3. Open Link from the parent UI
import { PhosraLink } from "@phosra/link/react"
{open && <PhosraLink
createSession={async ({ signal }) => {
const response = await fetch("/api/phosra/link/sessions", {
method: "POST",
signal,
headers: { "content-type": "application/json" },
body: JSON.stringify({
child_ref: selectedChildId,
platform_did: selectedPlatformDid,
return_to: window.location.pathname,
}),
})
if (!response.ok) throw new Error("Unable to start Phosra Link")
return response.json()
}}
onEvent={analytics.record}
onExit={() => setOpen(false)}
/>}Forward the AbortSignal exactly. The component owns the branded review,
handoff, bounded polling, cancellation, retry, and restart-safe recovery.
4. Read status honestly
The session snapshot is the authority for parent-facing status:
| Stage | Safe parent meaning |
|---|---|
bound | The connection is durable; platform confirmation is pending. |
rule_recorded | Controls were recorded; application is not yet confirmed. |
profile_ready | The platform has the verified profile and results are being checked. |
applied + E4 | Every requested control has current concrete platform evidence. |
degraded | Some controls are limited; a bounded retry may be offered. |
refused | The platform declined at least one required control. |
stale | Prior evidence is no longer current. |
revoked | Provider authority is revoked; release completion is tracked separately. |
Never translate an HTTP 2xx, delivery acknowledgement, or adapter return into “controls active.”
5. Disconnect
Authorize the exact grant in your own tenant boundary, then call:
import { phosraLinkServer } from "@/lib/phosra-link-server"
const authorizedGrant = await grants.requireForParent({ parentId, grantId })
const result = await phosraLinkServer.disconnectGrant({
grantId: authorizedGrant.id,
platformDid: authorizedGrant.platformDid,
targetRef: authorizedGrant.targetRef,
})Reuse the exact targetRef persisted with the issued grant. Never synthesize it
from a raw child ID or householdRef: householdRef establishes authenticated
provider-local family authority during issuance, while the saved targetRef
binds this exact grant operation.
The returned snapshot proves provider-side revocation. Platform removal is
complete only when releaseStatus becomes complete through the signed release
lifecycle. See Disconnect & reconnect.
Design-partner pilot checklist
- Keep
PHOSRA_CREDENTIALand Postgres server-only. - Mount GET, POST, and DELETE on the complete catch-all path.
- Derive parent and child authority from your authenticated server session.
- Use a stable, non-display
householdRef. - Run one worker with graceful abort.
- Render
PhosraLink; do not replace its evidence language with optimistic copy. - Let the platform SDK own shared-profile confirmation; add no host modal or browser authority.
- Log only your correlation IDs—never credentials, authorization URLs, or raw household identifiers.
For the complete API surface, see @phosra/link. Platform teams
should use the Platform quickstart.