> ## Documentation Index
> Fetch the complete documentation index at: https://docs.phosra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Versioning

> How the API path, the OCSS spec version, dated Annex B editions, and the SDK semver all pin together — and how to stay forward-compatible.

Phosra pins versions at four layers — the **API path**, the **OCSS spec version**,
dated **Annex B editions**, and the **SDK semver**. Each is stable and additive, so
code you ship today keeps working.

<Info>
  **Runnable.** Every version string and status below was captured live from
  `https://phosra-api-sandbox-production.up.railway.app` on **2026-07-06**.
</Info>

## The four layers

| Layer                 | Where you see it                                                     | Today's value                    | Changes when                                                       |
| --------------------- | -------------------------------------------------------------------- | -------------------------------- | ------------------------------------------------------------------ |
| **API version**       | URL path `/api/v1/…`                                                 | `v1`                             | A breaking API change → a new path (`/api/v2`); `v1` keeps serving |
| **OCSS spec version** | `ocss_version` in the trust list; `OCSS-Spec-Version` request header | `OCSS-v1.0-pre`                  | The protocol itself revs                                           |
| **Annex B edition**   | `/api/v1/annex-b/editions/{n}`                                       | *(pre-Edition-1)*                | The steward freezes a new numbered edition                         |
| **SDK**               | your `package.json`                                                  | semver, e.g. `@phosra/sdk@0.1.0` | Normal library releases; pin exact                                 |

## API version — it is in the path

The major version is the first path segment. Everything under `/api/v1` shares one
contract, and additions inside `v1` are backward-compatible. A future breaking
change gets a **new** path — `v1` is never mutated under you.

```bash curl theme={null}
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)
```

<Note>
  **What "backward-compatible additions" means inside `v1`:** we may add new
  endpoints, new **optional** request fields, and new **response** fields or enum
  values. Write clients that **ignore unknown fields** and do not break on a new
  enum value — that is the whole forward-compatibility contract.
</Note>

## OCSS spec version

The protocol carried over the API has its own version, `OCSS-v1.0-pre`. You can read
the census's binding of it directly from the (unmetered, unauthenticated) trust list:

```bash curl theme={null}
BASE=https://phosra-api-sandbox-production.up.railway.app
curl -s "$BASE/.well-known/ocss/trust-list" \
  | python3 -c "import sys,json; d=json.loads(json.load(sys.stdin)['document']); \
print('ocss_version =', d['ocss_version']); print('schema_version =', d['schema_version'])"
```

```text Output theme={null}
ocss_version = OCSS-v1.0-pre
schema_version = 1
```

On **signed census writes**, your client declares which spec it built against by
setting the `OCSS-Spec-Version` request header (covered by your RFC 9421 signature).
Send `OCSS-v1.0-pre`; the census pins its own side via `ocss_version`, so a mismatch
is detectable rather than silent.

<Warning>
  `OCSS-Spec-Version` is a **request** header you set on signed census calls — it is
  not echoed on ordinary product-plane responses. Do not poll for it on a `GET`;
  read `ocss_version` from the trust list instead.
</Warning>

## Dated artifacts — Annex B editions

Governed content (the rule registry, statute mappings) is published as **frozen,
numbered Annex B editions**. An edition is content-addressed and immutable: once
edition `N` is cut, its bytes never change, and its `ETag` is the artifact hash. Pin
to an edition number and you have pinned to exact bytes forever.

```bash curl theme={null}
BASE=https://phosra-api-sandbox-production.up.railway.app
curl -s -o /dev/null -w "edition 1 → %{http_code}\n" "$BASE/api/v1/annex-b/editions/1"
# → edition 1 → 404   (the sandbox census is pre-Edition-1; no editions frozen yet)
```

<Note>
  A `404` on an edition number means that edition is **not yet frozen** — every
  number returns `404` before Edition 1 is cut. It is a clean not-found, never a
  substitution to a nearby edition. Rule slugs reference their registry edition
  inline as `ocss-rules/<edition>#<slug>`, so a stored rule always names the exact
  edition it was written against.
</Note>

## Conditional reads and caching

Discovery artifacts (the trust list, profiles, editions) return a strong `ETag` and
`Cache-Control: public, max-age=300`. Send the `ETag` back as `If-None-Match` to get
an empty `304` when nothing moved — free, and it does not count against your
[rate limit](/rate-limits).

```bash curl theme={null}
BASE=https://phosra-api-sandbox-production.up.railway.app
# 1. First read — capture the ETag
ETAG=$(curl -s -D - -o /dev/null "$BASE/.well-known/ocss/trust-list" \
  | tr -d '\r' | awk 'tolower($1)=="etag:"{print $2}')

# 2. Conditional read — 304 while unchanged, 200 + body when it moves
curl -s -o /dev/null -w "%{http_code}\n" \
  -H "If-None-Match: $ETAG" "$BASE/.well-known/ocss/trust-list"
# → 304
```

The trust list also carries a **monotonic issue serial** so you can detect a change
without diffing the body — a higher `issue.number` means the census re-issued:

```bash curl theme={null}
curl -s "$BASE/.well-known/ocss/trust-list" \
  | python3 -c "import sys,json; i=json.loads(json.load(sys.stdin)['document'])['issue']; \
print('issue #', i['number'], 'at', i['issued_at'])"
# e.g. → issue # 136 at 2026-07-06T06:44:43Z
```

## SDK versioning — pin exact

The published SDKs follow semver. Pin the **exact** version you tested against and
bump deliberately; do not float on a caret range in production.

| Package                                    | Latest  | Pin                                |
| ------------------------------------------ | ------- | ---------------------------------- |
| `@phosra/sdk`                              | `0.1.0` | `"@phosra/sdk": "0.1.0"`           |
| `@phosra/link`                             | `0.1.2` | `"@phosra/link": "0.1.2"`          |
| `@phosra/mcp`                              | `0.4.0` | `"@phosra/mcp": "0.4.0"`           |
| `@openchildsafety/ocss` (protocol/signing) | `0.1.3` | `"@openchildsafety/ocss": "0.1.3"` |

<Warning>
  These packages are pre-1.0 (`0.x`). Under semver, a `0.x` **minor** bump may carry
  breaking changes, so an exact pin — not `^0.1.0` — is the safe default until a
  `1.0.0` line ships. Read the [changelog](/changelog) before you bump.
</Warning>

```bash npm theme={null}
# Pin exact — reproducible installs, no surprise breaks
npm install @phosra/sdk@0.1.0 @openchildsafety/ocss@0.1.3
```

## Staying forward-compatible

<CardGroup cols={2}>
  <Card title="Ignore unknown fields" icon="filter">
    New response fields and enum values arrive inside `v1`. Deserialize leniently;
    never fail on a field you did not expect.
  </Card>

  <Card title="Pin dated artifacts by number" icon="lock">
    Reference Annex B editions by number and verify the `ETag`. Frozen bytes never
    move under you.
  </Card>

  <Card title="Pin the SDK exact" icon="box">
    Test against a specific version, pin it, and bump on purpose after reading the
    changelog.
  </Card>

  <Card title="Watch the issue serial" icon="hashtag">
    Poll the trust list with `If-None-Match`; act only when `issue.number` climbs.
  </Card>
</CardGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Deprecation & sunset policy" icon="calendar-xmark" href="/deprecation">
    The lifecycle before anything can change — advance-notice windows, the
    `Deprecation`/`Sunset` headers, and how to migrate.
  </Card>

  <Card title="Changelog" icon="clock-rotate-left" href="/changelog">
    What shipped, when — including SDK releases and platform conventions.
  </Card>

  <Card title="Forward compatibility (OCSS)" icon="arrows-rotate" href="/integration/forward-compatibility">
    How the protocol itself stays additive across editions.
  </Card>
</CardGroup>
