Skip to main content
This is the lifecycle contract for the published SDK packages — how they are versioned, how their version relates to the wire API, when they can break, and how to move from one version to the next. For the wire API’s four version layers (the /api/v1 path, the OCSS spec version, Annex B editions), start with Versioning; this page is the package-level companion.
Pulled live. Every version, publish date, and status on this page was read from the public npm registry — the version history and per-package README — and cross-checked against the docs Changelog on 2026-07-06. Nothing here is hand-maintained — if a number looks stale, npm view it and open an issue.

Published packages, today

These are the packages actually on the public npm registry, newest release date first. Pin the ones you use.
PackageLatestFirst publishedVersion historyWhat it is
@openchildsafety/ocss0.1.32026-06-280.1.0 → 0.1.1 → 0.1.2 → 0.1.3OCSS protocol + RFC 9421 signing / verification, sealed envelopes, trust-list verify
@phosra/gatekeeper0.2.22026-06-280.1.0 → 0.2.0 → 0.2.1 → 0.2.2Platform-side decision engine — verify profiles, enforce, send §8.3.8 receipts
@phosra/link0.1.22026-06-280.1.0 → 0.1.1 → 0.1.2Writer-plane SDK — the consent ceremony + signed rule-write directives
@phosra/mcp0.4.02026-05-150.2.0 → 0.4.0MCP server exposing Phosra tools to AI agents (npx @phosra/mcp)
@phosra/cli0.2.02026-06-280.1.0 → 0.1.1 → 0.2.0Partner CLI — scaffold, verify OCSS setup, sandbox round-trips
@phosra/sdk0.1.02026-06-280.1.0Typed control-plane client (PhosraClient)
@phosra/connect0.1.12026-07-010.1.0 → 0.1.1Embeddable Connect component (web + React Native)
@phosra/classify0.1.12026-06-290.1.0 → 0.1.1Rule-category classification helpers
@phosra/provider0.1.02026-06-290.1.0Provider-side helpers over @openchildsafety/ocss
There is no @ocss/* npm scope. The open standard’s reference library ships as @openchildsafety/ocss; Phosra’s own SDKs ship under @phosra/*. If a guide anywhere tells you to npm install @ocss/…, it is wrong — that install 404s. The native iOS / Android enforcement SDKs are distributed privately (not on npm/Maven Central) — see iOS SDK and Android SDK.
Verify any row yourself — this prints the live registry state, no auth:
npm
npm view @openchildsafety/ocss version   # → 0.1.3
npm view @phosra/gatekeeper version      # → 0.2.2
npm view @phosra/mcp version             # → 0.4.0
npm view @phosra/sdk versions            # → [ '0.1.0' ]

The semver commitment

The packages follow Semantic Versioning 2.0.0. Every package above is currently pre-1.0 (0.x), and that changes what a version bump promises.
Bump1.x and later (future)0.x (today)
Patch (0.1.1 → 0.1.2)Bug fixes only, no API changeBug fixes only, no API change
Minor (0.1.x → 0.2.0)New features, backward-compatibleMay include breaking changes — this is what 0.x means under SemVer §4
Major (0.x → 1.0, later 1.x → 2.0)Breaking changes, with a migration guideThe 1.0.0 line stabilizes the public API and starts the standard guarantees
Pin exact while we are 0.x. Because a 0.x minor bump can break, do not float on a caret range (^0.1.0) in production — an npm install could silently pull a breaking 0.2.0. Pin the exact version you tested and bump on purpose after reading the changelog.
npm
# Reproducible — pin exact, no surprise breaks
npm install @phosra/sdk@0.1.0 @openchildsafety/ocss@0.1.3 @phosra/gatekeeper@0.2.2
What every version does promise, even at 0.x:

Patches never break

A patch bump (0.2.1 → 0.2.2) is bug-fix-only. Safe to take without a code change.

Breaks are documented

A breaking minor lands with a changelog entry and a before/after migration — like the @phosra/gatekeeper 0.1 → 0.2 example worked below.

Published bytes are immutable

A published version is never re-published with different bytes. 0.1.3 is 0.1.3 forever — new code gets a new number.

Deprecated exports are flagged

Anything on the way out carries a @deprecated JSDoc tag your editor and tsc surface before it is removed.

How an SDK version maps to the API version

There are three independent version axes, and the SDK’s semver is decoupled from both wire axes. The SDK talks to the stable /api/v1 path and stamps the current OCSS spec version into the signed envelopes it builds; bumping the SDK never changes which API version or spec version you hit, and either wire axis evolving additively never forces an SDK bump.
SDK version (@phosra/sdk@0.1.0)API version (/api/v1)OCSS spec version (OCSS-Spec-Version)
Whereyour package.jsonthe request URL patha request header on signed census calls, and the ocss_version field inside every OCSS envelope
Today’s valueper-package semver (e.g. 0.1.0)v1OCSS-v1.0-pre
Cadencenormal library releasesonly a genuinely incompatible contract cuts /api/v2the protocol itself revs; the census negotiates the highest common version
Who moves ityou, by upgrading the packagePhosra, and v1 keeps serving in parallelthe OCSS steward; the SDK sets it for you
Breaking?a 0.x minor may break your builda new path, never a mutation of v1negotiated fail-closed — an unsupported version is rejected, never silently downgraded
Both the pinned-0.1.0 SDK and a hand-rolled curl client hit the exact same v1 surface. Proof — the SDK’s default baseUrl is /api/v1, and there is no v2 yet:
curl
BASE=https://phosra-api-sandbox-production.up.railway.app
curl -s -o /dev/null -w "v1 → %{http_code}\n" "$BASE/api/v1/platforms"   # → v1 → 200
curl -s -o /dev/null -w "v2 → %{http_code}\n" "$BASE/api/v2/platforms"   # → v2 → 404 (no v2 yet)
The spec-version header is not the SDK version. The OCSS-Spec-Version request header (and the matching ocss_version field the writer-plane SDKs stamp into each signed envelope) identifies the protocol edition — OCSS-v1.0-pre today — and is covered by your RFC 9421 signature. It moves on the OCSS steward’s cadence, not on any @phosra/* release. So upgrading @phosra/gatekeeper from 0.1 to 0.2 does not change the spec version you speak, and a future OCSS-v1.0 does not force an SDK bump — the census negotiates the highest version you both support and fails closed if there is no overlap. The three axes rev on three clocks. See Versioning → the four layers for the full picture.
The forward-compatibility rule the SDKs are built for. Inside v1, Phosra adds new endpoints, new optional request fields, and new response fields / enum values without a major bump. The typed clients deserialize leniently and ignore unknown fields, so an additive API change reaches you as data — no reinstall required. See Forward compatibility.

Deprecation & end-of-life

SDK deprecations ride the same Deprecation & Sunset policy as the wire API, expressed in package terms:
  • A deprecated export keeps working. It gains a @deprecated JSDoc tag naming the replacement; tsc and your editor flag it, but the runtime behavior is unchanged.
  • Removal follows semver. At 0.x, a deprecated export can be removed in the next minor (0.2 → 0.3). Once a package reaches 1.0, a deprecated export survives until the next major and its removal is called out in the changelog.
  • The wire windows still protect you. The ≥ 90-day deprecation window and ≥ 12-month major-version overlap in the Deprecation policy govern the API surface the SDK calls — so even if you pin an old SDK, the endpoints it depends on do not vanish under it without notice.
A floating SDK caret range does not inherit the wire API’s notice windows. The ≥ 90-day guarantee is about the endpoint; a 0.x minor SDK bump is the one lane that can break with no separate window. That is the trade you make by not pinning exact. See Deprecation → pre-1.0 SDKs.

Native iOS & Android SDK lifecycle

The npm packages above are only the control- and writer-plane. The on-device enforcement SDKs — PhosraSDK (iOS, Swift) and com.phosra:sdk (Android, Kotlin) — are distributed privately (not npm, not Maven Central, not Swift Package Index), so their lifecycle is worth stating explicitly. Full API guides: iOS SDK and Android SDK.
The private SDKs are in preview. During private preview a new tag can carry a breaking change with no separate notice window — exactly like the 0.x npm minors. Pin the exact version Phosra gives you and read the release note emailed to the preview list before bumping. Email developers@phosra.com for repo/AAR access.

Version scheme & how you pin

iOS — PhosraSDKAndroid — com.phosra:sdk
Distributionprivate Git repo, consumed via Swift Package Managerprivate Maven repo (URL + credentials from Phosra)
Version identifiera semver Git tag on the private repoa semver Maven coordinate — today com.phosra:sdk:1.0.0
How to pin exactSPM .exact("x.y.z") in Package.swift (or the pinned tag in Xcode)the fully-qualified coordinate, e.g. implementation("com.phosra:sdk:1.0.0")never a + dynamic version
Platform floor (the real EOL lever)iOS 16.0+, Swift 5.9+, Xcode 15+, FamilyControls entitlementAndroid 8.0+ (API 26), Kotlin 1.9+, Gradle 8+, compileSdk/targetSdk 35
Pin the exact tag/coordinate — the same discipline as pinning an npm 0.x:
// URL is the private repo Phosra provides; the version is a semver Git tag.
.package(url: "<private PhosraSDK repo URL>", exact: "1.0.0"),
// …then add "PhosraSDK" to your target's dependencies.

Upgrade delivery — no npm outdated

Because these SDKs are not on a public registry, you cannot npm outdated them. Upgrade delivery works differently:
  • You are notified by email (the private-preview list) when a new tag/coordinate is cut, with its release note. There is no public dist-tag to poll.
  • iOS: bump the exact: version in Package.swift (or the pinned tag in Xcode) and re-resolve the package graph.
  • Android: bump the com.phosra:sdk:x.y.z coordinate and re-sync Gradle.
  • Re-test on-device, not just build — an enforcement behavior change (like the gatekeeper’s staleness floor) compiles clean but changes runtime verdicts.
The on-device policy version is a different axis from the SDK version. The policy.version integer you ackPolicyVersion(…) on-device increments every time a parent changes a rule and is delivered over the wire at runtime (the device sends sinceVersion and gets a 304 when nothing changed) — see iOS SDK → policy sync and Android SDK → policy sync. That is not a library upgrade: it moves on the parent’s cadence, needs no rebuild, and is unrelated to the SPM tag / Maven coordinate. Two separate clocks — don’t conflate them.

Deprecation, EOL & support matrix

Lifecycle questionPrivate iOS / Android SDKs (today)At public GA (future)
Can a version break me?Yes — a preview tag may break, like a 0.x npm minor. Pin exact; read the emailed release note.No: semver applies — breaking changes only on a major, with a migration guide.
What sets end-of-life?The OS floor above. When Apple/Google drop an OS release, or the FamilyControls entitlement policy changes, support follows the platform.OS floor plus the standard ≥ 12-month major-version overlap.
How is a removal announced?By email to the preview list, ahead of the tag that removes it.Changelog entry + a @deprecated-equivalent symbol annotation before removal.
Support channeldevelopers@phosra.com (private preview — no public LTS yet).Public support + the wire-API deprecation windows that protect the endpoints the SDK calls.
The ≥ 90-day / ≥ 12-month wire windows in the Deprecation policy protect the census endpoints these native SDKs call (policy fetch, receipt POST) regardless of SDK distribution — so even a pinned old on-device build keeps talking to a stable /api/v1.

Per-package changelogs

The canonical, human record of every release is the Changelog. Package detail lives with the package:

Changelog (all packages)

Dated release notes for the API, platform conventions, and every SDK line — newest first.

@phosra/gatekeeper README

The gatekeeper README documents the behavior contract — the §8.3.6 revoked-tombstone / endpointState() handling and the 0.x stability policy. The dated version-by-version record lives in the Changelog.

npm version history

Every published version and its release date, per package, straight from npm.

SDK overview

What each package is, which are published vs. private, and the language coverage matrix.

Worked example — @phosra/gatekeeper 0.1.x → 0.2.x

This is a real breaking upgrade (recorded in the Changelog; the behavior contract it introduced is documented in the @phosra/gatekeeper README), and the best illustration of why 0.x minors demand attention. The gatekeeper is the platform-side enforcement engine, so its 0.2.0 “safety hardening” release deliberately changed a runtime behavior. What changed (0.1.0 → 0.2.0, published 2026-06-28):

Bounded-staleness floor (behavior change)

A new profileMaxAgeMs config field (default 60 000 ms) makes isAllowed return a block verdict for any cached enforcement profile older than the floor, independent of the census-signed window. Partners relying on long census windows see more block verdicts unless they refreshProfile() within it.

Two new lifecycle surfaces

endpointState() and the onRevoked config callback (below) let you distinguish a positive disconnect from a transient outage — a distinction 0.1.0 could not express.
Then 0.2.2 (published 2026-07-04) added revoked-state handling — a router-signed §8.3.6 tombstone (top-level status: "revoked") is recognized as a terminal disconnect, via two new, additive surfaces on the 0.2.x line:
  • gk.endpointState()"active" | "revoked" | "stale" | "absent", so you can tell a disconnect ("revoked" → release-to-native, stop polling) from an outage ("stale" → keep enforcing last-known-good, fail-closed).
  • an onRevoked({ childRef, endpointLabel }) config callback, fired once on the transition to revoked.
isAllowed returns synchronously; it does not throw for a block. In both 0.1.0 and 0.2.2 the signature is gk.isAllowed({ category, signal? }): Verdict, and the Verdict carries decision: "allow" | "warn" | "block". A block is a return value, never an exception — so branch on verdict.decision, never a try/catch. (Verified against the published @phosra/gatekeeper@0.2.2 type declarations.)

Migrating your code

The upgrade is behavioral, not a signature break — the same isAllowed call site compiles on both versions. The work is adopting the new floor and the new states. Both snippets below are self-contained (declared placeholders stand in for the platform key you mint at endpoint-bind time — see Link):
import { createGatekeeper } from "@phosra/gatekeeper";
import type { GatekeeperConfig } from "@phosra/gatekeeper";
import type { SenderKey } from "@openchildsafety/ocss";

// Your platform's Ed25519 signing key, minted at endpoint-bind time. Never inline it —
// load from a secret store. See /sdks/link for how to obtain one.
declare const platformSigningKey: SenderKey;

// 0.1.0 config: note there is NO profileMaxAgeMs and NO onRevoked in this version.
const config: GatekeeperConfig = {
  platformDid:          "did:ocss:your-platform",
  platformKeyId:        "did:ocss:your-platform#key-2026",
  gatekeeperSigningKey: platformSigningKey,
  censusBaseUrl:        "https://phosra-api-sandbox-production.up.railway.app",
  trustRootXB64Url:     process.env.PHOSRA_TRUST_ROOT_X!,   // public root pubkey X
  endpointId:           "endpoint-mia-01",                  // the §9.3(b) bound label
  ratingMappings:       [{ ocssCategory: "content_rating", myField: "maturity", vocabulary: "mpaa" }],
};

const gk = createGatekeeper(config);

// isAllowed is synchronous and returns a Verdict; branch on verdict.decision.
function guard(maturity: string): boolean {
  const verdict = gk.isAllowed({ category: "content_rating", signal: { maturity } });
  return verdict.decision !== "block";
  // 0.1.0 has no endpointState()/onRevoked, so a census outage and a real
  // disconnect are indistinguishable here — the gap 0.2.x closes.
}
Do not release enforcement on "stale". "stale" means the census is unreachable — keep enforcing last-known-good (fail-closed). Only "revoked" (a positive, signed, terminal disconnect) releases to native controls. Releasing on "stale" is a fail-open bug.
Upgrade command:
npm
npm install @phosra/gatekeeper@0.2.2

Worked example — @phosra/mcp 0.2.0 → 0.4.0

The MCP server is run, not imported (npx @phosra/mcp), so “upgrading” means bumping the version your MCP client pins. Between 0.2.0 (2026-05-15) and 0.4.0 (2026-06-28) the tool surface grew — the published 0.4.0 exposes 79 tools (federation, streaming, reversibility) — but the invocation and auth are unchanged: same phosra-mcp binary, same Authorization: Bearer phomcp_… token.
{
  "mcpServers": {
    "phosra": {
      "command": "npx",
      "args": ["-y", "@phosra/mcp@0.2.0", "--api-key", "phomcp_…"]
    }
  }
}
Confirm the tool count after the bump the same way the MCP server page does — a live tools/list is authoritative (ignore the cosmetic Registering 42 tools… stderr banner):
Verify tool count
npx -y @phosra/mcp@0.4.0 --api-key "$PHOSRA_MCP_TOKEN" <<'EOF' | \
  python3 -c "import sys,json; print(len(json.loads(sys.stdin.readlines()[-1])['result']['tools']), 'tools')"
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"verify","version":"1"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
EOF
# → 79 tools
New tools are additive — every tool your agent already called in 0.2.0 still exists in 0.4.0 with the same name and arguments. There is nothing to rewrite; the bump only adds capability. See the full tool catalog on the MCP server page.

Worked example — @openchildsafety/ocss 0.1.0 → 0.1.3

The OCSS reference library is pinned byte-for-byte to the spec’s frozen §8.5 golden vectors, so its 0.1.x line is additive within a stable public barrel: 0.1.1, 0.1.2, and 0.1.3 add and fix subpath exports (./envelope, ./trustlist/verify, ./profile, …) without changing what 0.1.0 already exported. A straight patch/minor bump inside 0.1.x needs no code change:
npm
# Additive within 0.1.x — existing imports keep resolving
npm install @openchildsafety/ocss@0.1.3
Unchanged across 0.1.0 → 0.1.3
import { verifyTrustList } from "@openchildsafety/ocss/trustlist/verify";
import { sealEnvelope }    from "@openchildsafety/ocss/envelope";
// These imports resolve identically on 0.1.0 and 0.1.3 — the barrel is frozen
// against the production-deployed OCSS verbs; new verbs arrive as new subpaths.
The additive guarantee here holds within the 0.1.x line. Per SemVer §4, a future 0.1 → 0.2 minor is the point where a breaking change to the OCSS barrel could land — it would ship with its own migration entry in the changelog. Pin exact.

@phosra/sdk — one line so far

@phosra/sdk has exactly one published version, 0.1.0 — there is no vX → vY migration to write yet. Pin it exactly; when 0.2.0 ships it will arrive here with a before/after example, and (because it is a 0.x minor) you should read that entry before bumping.
npm
npm install @phosra/sdk@0.1.0   # the only published version today

Upgrade checklist

Whenever you bump any @phosra/* or @openchildsafety/* package:
  1. Read the changelog entry for the target version — /changelog is the dated record; the package’s own README on npm (npm view <pkg> readme, or the package page’s Readme tab) documents its behavior contract.
  2. Check the bump class. Patch (0.2.1 → 0.2.2) is safe; a 0.x minor (0.1 → 0.2) may break — treat it like a major.
  3. Pin exact, install, and let tsc surface any @deprecated flags or removed exports.
  4. Re-run against the sandbox (https://phosra-api-sandbox-production.up.railway.app) before touching production — the Quickstart path proves your setup end-to-end with no API key.
  5. Verify runtime behavior, not just that it compiles — the gatekeeper example above is a compile-clean bump that changes what isAllowed returns.

Next steps

Versioning (the four layers)

How the API path, OCSS spec version, Annex B editions, and SDK semver pin together.

Deprecation & Sunset policy

The advance-notice windows, the Deprecation / Sunset headers, and how a deprecation reaches you.

Changelog

The canonical dated record of every API change, deprecation, and SDK release.

SDK overview

Every package, its status (published vs. private), and the language-coverage matrix.