InOrbit schemas
Versioned JSON Schemas for InOrbit's wire formats.
Each schema is a JSON Schema (draft 2020-12) document for a frame or a request or response body that InOrbit's agents, sensors and API exchange. Its $id is the URL it is served from, so a $ref resolves over HTTP. There is one immutable file per major version, v0 is a draft, and index.json lists everything for a program to read. Open, no sign-in.
Published schemas: 7. Versions: 7. · What a schema is · index.json
Published schemas and their versions.
Search by name, title, field path or $id. Built from the repository's schemas folder; index.json lists the same entries.
7 schemas
agent · 1
approvals · 1
runner · 1
sensors · 4
- 04sensors/consentSensor consentA record of what one client agreed to share with InOrbit as sensor readings, who agreed, for which account (and optionally one Decisions space), and whether and when that agreement ended. A client is a Mac app, a phone, a browser extension, a Claude plugin, an agent or a runner. It is a record of an agreement, not of data: it holds ids, words from closed lists and timestamps, no reading value, no content and no credential, and the object is closed so a field outside these is refused. A consent is off until a person grants it; a revoke is a fact added to the record (revoked_at and revoked_by), never an edit that erases the grant. A consent is in force when it is not revoked and has not expired. This is not the OAuth consent of a sign-in: that one lets a client call the API; this one lets a client send readings. The sensor id and timestamp are those of sensors/reading v0, by reference. The sources and kinds are the words of sensors/sensor v0 and the sources the Sensors API registers, which are the source of truth; they are listed here.draftv0
- 05sensors/ingestSensor ingest requestThe body a producer posts to Ingest (POST /v1/atlas/signals): readings from one sensor, at most 500, each accepted or refused on its own (send an Idempotency-Key header to make a retry safe). A reading is a produced_at, exactly one of a number (value) or a state word (state), labels from the sensor's label_keys, and optionally the digest of evidence the producer keeps. It carries no content: no text, prompt, document or credential, and every object is closed so a field outside these is refused. Only the sensor's producer may write, a paused or revoked sensor refuses, and a built-in sensor is read only: the service decides those, the schema cannot.draftv0
- 06sensors/readingSensor readingOne reading of a sensor as Readings (GET /v1/atlas/sensors/{sensor_id}/readings, raw) and the live signal stream return it (JSON field names as the API writes them): when it was produced and received, a number or a state word, labels, and the digest of evidence the producer keeps. A reading carries no content: no text, prompt, document or credential, and the object is closed so a field outside these is refused. A registered sensor's reading holds a value or a state, never both; a built-in sensor's reading (builtin. or mon_) is derived: a monitor's holds its latest latency as the value and its verdict as the state, either may be absent before its first run, and there is no cursor. Fields the API leaves at their default (an empty string, false, an empty object) may be absent. The shared definitions below (state_word, label_key, label_value, labels, evidence_digest, timestamp, sensor_id) are the ones the ingest schema refers to, so a reading sent and a reading read cannot drift.draftv0
- 07sensors/sensorSensorOne sensor as Get (GET /v1/atlas/sensors/{sensor_id}, under "sensor"), List (GET /v1/atlas/sensors, inside each of "groups"[]."sensors"), Register and the pause, resume and revoke calls return it (JSON field names as the API writes them). A sensor is a named source of readings: what it is, who produces it, how often it reports and whether it is healthy. It carries no content: no text, prompt, document or credential, and the object is closed so a field outside these is refused. Every id carries its prefix (sen_ for a registered sensor, mon_ for a monitor, builtin. for a derived count); a bare UUID is never a sensor id. Fields the API leaves at their default (an empty string, 0, false) are present.draftv0
A JSON Schema is a declarative validator for a JSON document.
JSON has no types or required fields of its own, so a misspelt property, a missing one or a string where a number belongs is accepted until a program fails on it. A JSON Schema is a second JSON document that constrains the first: the properties it may have, the type and limits of each and which are required. A validator evaluates the document against the schema and returns whether it conforms, or the failing keyword and its path.
Below: a minimal schema, a document that validates and one that does not. The published schemas use the same keywords at greater size.
{
"type": "object",
"required": ["id", "kind"],
"properties": {
"id": { "type": "string" },
"kind": { "enum": ["gate", "check", "score"] }
},
"additionalProperties": false
}{ "id": "nudge-weight", "kind": "score" }{ "id": "nudge-weight", "kind": "banana" }Refused: kind is not in the enum (gate, check, score).
The schema is the machine-readable contract of a wire format.
The schema is the exact definition of the body a service accepts or returns, or of the frame an agent sends. Because it is data and not prose, a program can use it: validate a payload before sending it or after receiving it, drive an editor's completion, generate types or a client model with a tool that reads JSON Schema (check its support for draft 2020-12), diff two versions field by field, and fail CI when a stored file stops conforming.
Schemas refer to each other by $id. The sensors/ingest body takes its timestamp, state word, labels and evidence digest from sensors/reading with a $ref to that file's URL, so a reading that is sent and one that is read share one definition and cannot drift.
A schema holds no customer data: it describes properties and limits, never values. The files are open: no sign-in, no token.
One immutable file per major version, addressed by its $id.
Every schema lives at one address per major version: schemas.inorbit.hr/<area>/<name>/v<major>.json. The file names that same address in its $id, so a schema that refers to another resolves it from the web and a copied file cannot pass for another one; the build checks this for every file. Each schema also has a page per version, and .../latest/ always opens the newest one.
A stable major (v1 and up) never changes once it is published, so the server tells every cache to keep it for a year. A fix that would change what a valid document is becomes the next major, v2, at its own address, and v1 stays exactly as it was. Each page compares a version with the one before it, field by field.
v0 is the draft line. It may still be corrected, so it is kept for five minutes and then fetched again. A schema at v0 is a draft design; its description says what, if anything, uses it today.
Every schema carries one of three statuses. stable has a published major that will not change. draft may still be corrected. deprecated stays at its address so existing documents keep validating, and its page says which schema replaces it.
We call a change breaking when a document that was valid could stop being valid, or a program written against the old shape could misread a new one: removing or renaming a field, changing a field's type, making an optional field required, or tightening a limit. A breaking change is a new major. A schema that refuses unknown fields also needs a new major for a new optional field, because a validator that has the old file would refuse a document that uses it.
Validate in an editor, in CI or in code.
In an editor that understands JSON Schema, map your files to the schema in the editor's settings, and it will mark a wrong field as you type and offer the allowed values. Do not put a $schema line in a document whose schema refuses fields it does not list: the line itself would be rejected. Each schema's page says whether it does.
In CI, run a validator on the files you keep in a repository. Any validator that supports JSON Schema draft 2020-12 works; the two below are only examples; we neither ship nor support them. Each schema's page also checks a pasted document in your browser.
To read one, or to vendor a copy, fetch it. It is plain JSON, with CORS open for browsers. A program that wants the whole list reads index.json: every schema with its page, versions, status and field names.
The coordinator compiles its published schemas into the binary and validates against them. The services behind the agent and sensor schemas enforce the same rules in their own code, and the repository's tests run every example and refused document through the published files, and the sensor service's real answers through the sensor schemas, so the two cannot drift. The iohr command line does not validate files against a schema yet.
"json.schemas": [
{ "fileMatch": ["my-file.json"], "url": "https://schemas.inorbit.hr/agent/signal/v0.json" }
]check-jsonschema --schemafile https://schemas.inorbit.hr/agent/signal/v0.json my-file.jsonajv validate --spec=draft2020 -s <(curl -s https://schemas.inorbit.hr/agent/signal/v0.json) -d my-file.jsoncurl -s https://schemas.inorbit.hr/agent/signal/v0.jsonimport Ajv2020 from "ajv/dist/2020.js";
const { schemas } = await (await fetch("https://schemas.inorbit.hr/index.json")).json();
const reading = schemas.find((s) => s.path === "sensors/reading");
const schema = await (await fetch(reading.versions[0].id)).json(); // newest first
const validate = new Ajv2020({ strict: false }).compile(schema);
validate(payload); // true, or false with validate.errorsA schema checks structure. The service checks semantics.
A schema can say an evidence digest is sha256: and 64 lowercase hex characters. It cannot say the digest matches the evidence the producer keeps, that the sender is the sensor's producer, or that the sensor is not paused or revoked. The service that accepts the document checks those, and it can still answer no to one that validates.
The reverse is tested for ingest: the refused documents are posted to the running service, and whatever the schema refuses the service must not accept. The example documents on each page have made-up values (their digests are a repeated pattern) and are the ones those tests use.