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-20. 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.
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.
The Golden generation of @phosra/link, @phosra/gatekeeper, and @openchildsafety/ocss is now published on public npm — install unpinned to get it. Production manifest, trust, and client-directory discovery are live. Self-service credential issuance is not yet released, so a Golden integration still requires an operator-issued credential; see what you cannot self-serve yet.
Verify any row yourself — this prints the live registry state, no auth:
npm

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.
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
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. 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
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

Pin the exact tag/coordinate — the same discipline as pinning an npm 0.x:

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

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.3.0 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):
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

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.
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
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
Unchanged across 0.1.0 → 0.1.3
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

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.