# The open schema

The canonical risk object is Craton's core primitive, and its JSON Schema is
open. You can fetch it, read it, validate against it and build on it **with no
Craton account** — nothing to authenticate, nothing to install, nothing that
expires. The engine is paid; the vocabulary is not.

This page shows the whole of that: resolve which file to use and which version
this deployment emits, fetch it, and validate an object of your own against it
in one run.

---

## What you need

* `curl` 7.76 or newer (this page uses `--fail-with-body`) and `python3` 3.8
  or newer. Nothing else — the validator below is standard library only.
* `CEDE_DOCS_URL` — the base URL of a Craton documentation site. That is the
  only address on this page: **the API is not involved**, and no credential is
  sent anywhere.

```sh
export CEDE_DOCS_URL="https://docs.oncraton.com"   # no trailing slash
```

Craton is pre-release, and the site above is reachable to members of the
operator's tailnet rather than to the open internet — a public address is
waiting on a domain. Point `CEDE_DOCS_URL` at any Craton documentation site
someone runs for you and nothing else on this page changes.

## What is published

`$CEDE_DOCS_URL/schema` lists every published file, with the version this
deployment stamps on the documents it emits. The files are:

| File | What it is |
|---|---|
| `risk-object.v0.schema.json` | The canonical risk object: exposure, peril, financial structure, trigger or wording, period, jurisdiction, counterparties, provenance. |
| `price-response.v0.schema.json` | What Price answers with — the technical price *and* its complete assumption set. |
| `backtest-response.v0.schema.json` | What Backtest answers with, year by year, with its data-coverage disclosure. |
| `structure.v0.schema.json` | A candidate structure produced by Structure. |
| `package.v0.schema.json` | The submission pack Package assembles: the exhibits it carries, and the watermark, draft status and disclosure that are constants in the file itself. |
| `model-object.v0.schema.json`, `model-primitives.v0.schema.json`, `model-run.v0.schema.json` | A registry model, the vocabulary it is composed from, and one run of it. |
| `job.v0.schema.json` | The one asynchronous job resource every verb uses. |
| `error.v0.schema.json` | Every refusal, from every route, in one shape. |
| `account.v0.schema.json`, `usage.v0.schema.json`, `service-info.v0.schema.json` | An account and its key, metered usage, and the health document. |
| `api-contract.v0.json` | The map: which schema governs which response, and which version this deployment emits. Generated from the server's own route registrations. |
| `validate.py` | A standard-library validator for the risk object, plus the two rules JSON Schema cannot express. |
| `README.md` | Field by field, where every required/optional decision comes from. |

These are the same files Craton validates its own responses against — not a
copy published for readers. A response that fails one of them is a product
bug, and the gate that says so reads the file you just downloaded.

## Versions, and what a version line means

A file name carries the version **line**, not the version:
`risk-object.v0.schema.json` accepts every `0.x.y` document. That is
deliberate — a minor version that adds optional members must never
retroactively invalidate an object you already hold — and it leaves one
question the files alone cannot answer: *which* version is this deployment
emitting? That is what `versions.emitted` in the contract is for, and the
block below resolves it rather than trusting a number typed on a page.

Breaking changes bump the major version and arrive as a **new file**
(`risk-object.v1.schema.json`), never as an edit to this one. So a URL you
link, cite or pin in your build is stable: the bytes behind
`$CEDE_DOCS_URL/schema/risk-object.v0.schema.json` are only ever extended
compatibly.

## Fetch and validate, end to end

Copy the block. It fetches the contract, resolves the file and the version,
downloads the schema and the validator, validates an object it writes, and
then proves the validator bites by feeding it a broken one.

<!-- cede:runnable -->

```bash
set -euo pipefail

: "${CEDE_DOCS_URL:?export CEDE_DOCS_URL first — see 'What you need'}"

read_json() {
  python3 -c 'import json, sys
document = json.load(open(sys.argv[1]))
for step in sys.argv[2].split("."):
    document = document[step]
print(document)' "$1" "$2"
}

# 1 — The contract: which file governs the risk object, and which version of
#     it this deployment emits. No key is sent, and none is asked for.
curl -sS --fail-with-body "$CEDE_DOCS_URL/schema/api-contract.v0.json" \
  -o api-contract.v0.json

schema_file="$(read_json api-contract.v0.json schemas.risk-object)"
version="$(read_json api-contract.v0.json versions.emitted.risk-object)"
echo "risk object: $schema_file, emitted version $version"

# 2 — The schema itself, and the validator that ships beside it. Standard
#     library only: nothing to install, nothing to trust but python3.
curl -sS --fail-with-body "$CEDE_DOCS_URL/schema/$schema_file" -o "$schema_file"
curl -sS --fail-with-body "$CEDE_DOCS_URL/schema/validate.py" -o validate.py

# 3 — An object of your own, stamped with the version resolved above rather
#     than one copied off a page. A draft needs exposure and nothing else:
#     source_fidelity is mandatory and its three lists are mandatory empty,
#     so "nothing was guessed" has to be stated, never left to silence.
python3 - "$version" > my-object.json <<'PY'
import json, sys
print(json.dumps({
    "schema_version": sys.argv[1],
    "status": "draft",
    "exposure": {
        "kind": "location_schedule",
        "currency": "USD",
        "locations": [{
            "address_as_given": "1 Harbour Road, Miami FL",
            "geocode": {"resolution": "unknown"},
        }],
        "source_fidelity": {
            "unmapped_columns": [],
            "guessed_units": [],
            "ambiguous_rows": [],
        },
    },
}, indent=2))
PY

# 4 — Validate. Exit 0 means the object conforms to the published schema.
python3 validate.py --schema "$schema_file" my-object.json

# 5 — And prove the validator bites, because a validator that accepts
#     everything would have printed the same green line above. This object
#     names a peril outside the closed taxonomy.
python3 - "$version" > broken-object.json <<'PY'
import json, sys
print(json.dumps({
    "schema_version": sys.argv[1],
    "status": "analysed",
    "exposure": {
        "kind": "index",
        "source_fidelity": {
            "unmapped_columns": [],
            "guessed_units": [],
            "ambiguous_rows": [],
        },
    },
    "peril": {"code": "meteor_strike"},
}, indent=2))
PY

if python3 validate.py --schema "$schema_file" broken-object.json; then
  echo "the validator accepted an object it should have refused" >&2
  exit 1
fi
echo "refused, as it should be — the schema is doing work"
```

What you get from step 4 is a line naming the schema and the verdict, and an
exit status you can put in your own build. Nothing on this page contacted the
API, so nothing here depends on having an account, a key, or a plan.

## What the validator adds

Two rules the specification states and JSON Schema has no keyword for, applied
by `validate.py` on top of the schema:

* **S1** — `period.expiry` must be strictly after `period.inception`. No
  keyword compares two sibling values.
* **S2** — `period.timezone` must name a real IANA zone, checked against your
  machine's tz database. Where there is no tz database, it says so out loud
  instead of passing over it.

It also refuses to load a schema containing a keyword it does not implement:
a validator that ignores what it does not understand under-validates without
telling you. If you would rather use your own implementation, the files are
plain JSON Schema draft 2020-12 and every published draft 2020-12 validator
reads them — `validate.py` is a convenience, not a dependency.

## Validating what the API sends you

Once you do hold a key, the same files validate every response. Read
`api-contract.v0.json` as four rules:

* `routes` — for a 2xx, take `answers[status]` and validate the body against
  `schemas[that id]`.
* `errors` — any status of 400 or above, from any route, validates against
  the error schema. One refusal shape for the whole surface.
* `embedded` — a published document carried inside another at a JSON pointer:
  a technical price travels inside the job that produced it, and a backtest
  and a model run do the same. Follow the pointer and validate it in full.
* `versions.emitted` — the version each of those documents is stamped with,
  which is the number step 1 above resolves.

The contract is generated from the server's own route registrations, and the
contract oracle fails the build if the published map and the running surface
disagree in either direction. A route the map does not document is a red gate,
not an undocumented feature.

The [quickstart](quickstart.md) takes it from there: signup, a key, an ingest
call, and a first object of your own to run through the validator above.

## What is open, and what is not

The schema is open source. The engine behind the verbs — normalisation,
hazard data, pricing, backtesting, the registry — is the paid product, billed
by usage with a free sandbox tier. Building your own tooling against these
files, publishing objects that conform to them, or writing your own validator
needs nothing from Craton and asks nothing of you.

Craton outputs technical prices with their assumptions attached and draft terms.
It holds no money and never touches paper; nothing produced by anything on
this page is a transaction or a position anyone stands ready to take.
