Schema · sensors/reading
Sensor reading
One reading of a sensor as Readings (GET /v1/atlas/sensors/{sensor_id}/readings, raw) and the live signal stream return it (proto iohr.sensors.v1.Reading, 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. Draft line: it may still be corrected.
https://schemas.inorbit.hr/sensors/reading/v0.jsonEach version is a file at its own address. The raw file is plain JSON, served without a sign-in.
- v0draftMay still be corrected; kept for five minutes.schemas.inorbit.hr/sensors/reading/v0.json ↗
Fields of v0, the newest version, by path. A name ending in [] is each item of a list. Every field not listed is refused.
sensor_idanyrequiredThe sensor it belongs to.
produced_atstringrequiredThe producer's clock. Empty only for a built-in or monitor sensor that has not run yet.
pattern ^$|^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,9})?(Z|[+-][0-9]{2}:[0-9]{2})$received_atstringrequiredThe server's clock when the batch was stamped. Empty only for a monitor that has not run yet.
pattern ^$|^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,9})?(Z|[+-][0-9]{2}:[0-9]{2})$valuenumberoptionalA gauge's or counter's value. Absent for a state.
statestringoptionalA state sensor's word; empty or absent for a number.
pattern ^[a-z0-9_]{0,32}$labelsanyoptionalLabels the producer sent.
evidence_digeststringoptionalThe digest of the producer's evidence; empty or absent when none.
pattern ^$|^sha256:[0-9a-f]{64}$skewedbooleanoptionalTrue when the producer's time and the server's are more than 5 minutes apart (the reading is kept and flagged).
cursorstringoptionalThe reading's cursor in the account's order: digits. Empty for a built-in or monitor reading, which is derived and has none.
pattern ^$|^[0-9]{1,19}$
Valid documents with made-up values, as the repository's tests check them.
{
"sensor_id": "builtin.incidents.open",
"produced_at": "2026-10-23T14:09:26.123Z",
"received_at": "2026-10-23T14:09:26.123Z",
"value": 2,
"state": "",
"labels": {},
"evidence_digest": "",
"skewed": false,
"cursor": ""
}The repository's tests also check 23 documents that this schema must refuse.
Replace the file name with yours.
"json.schemas": [
{ "fileMatch": ["my-file.json"], "url": "https://schemas.inorbit.hr/sensors/reading/v0.json" }
]check-jsonschema --schemafile https://schemas.inorbit.hr/sensors/reading/v0.json my-file.jsonajv validate --spec=draft2020 -s <(curl -s https://schemas.inorbit.hr/sensors/reading/v0.json) -d my-file.jsoncurl -s https://schemas.inorbit.hr/sensors/reading/v0.jsonThe raw file of v0, as published.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://schemas.inorbit.hr/sensors/reading/v0.json",
"title": "Sensor reading",
"description": "One reading of a sensor as Readings (GET /v1/atlas/sensors/{sensor_id}/readings, raw) and the live signal stream return it (proto iohr.sensors.v1.Reading, 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. Draft line: it may still be corrected.",
"type": "object",
"additionalProperties": false,
"required": [
"sensor_id",
"produced_at",
"received_at"
],
"properties": {
"sensor_id": {
"description": "The sensor it belongs to.",
"$ref": "#/$defs/sensor_id"
},
"produced_at": {
"description": "The producer's clock. Empty only for a built-in or monitor sensor that has not run yet.",
"type": "string",
"pattern": "^$|^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]{1,9})?(Z|[+-][0-9]{2}:[0-9]{2})$"
},
"received_at": {
"description": "The server's clock when the batch was stamped. Empty only for a monitor that has not run yet.",
"type": "string",
"pattern": "^$|^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]{1,9})?(Z|[+-][0-9]{2}:[0-9]{2})$"
},
"value": {
"description": "A gauge's or counter's value. Absent for a state.",
"type": "number"
},
"state": {
"description": "A state sensor's word; empty or absent for a number.",
"type": "string",
"pattern": "^[a-z0-9_]{0,32}$"
},
"labels": {
"description": "Labels the producer sent.",
"$ref": "#/$defs/labels"
},
"evidence_digest": {
"description": "The digest of the producer's evidence; empty or absent when none.",
"type": "string",
"pattern": "^$|^sha256:[0-9a-f]{64}$"
},
"skewed": {
"description": "True when the producer's time and the server's are more than 5 minutes apart (the reading is kept and flagged).",
"type": "boolean"
},
"cursor": {
"description": "The reading's cursor in the account's order: digits. Empty for a built-in or monitor reading, which is derived and has none.",
"type": "string",
"pattern": "^$|^[0-9]{1,19}$"
}
},
"allOf": [
{
"if": {
"properties": {
"sensor_id": {
"pattern": "^sen_"
}
}
},
"then": {
"properties": {
"produced_at": {
"$ref": "#/$defs/timestamp"
},
"received_at": {
"$ref": "#/$defs/timestamp"
},
"cursor": {
"pattern": "^[0-9]{1,19}$"
}
},
"required": [
"cursor"
],
"oneOf": [
{
"required": [
"value"
],
"properties": {
"state": {
"maxLength": 0
}
}
},
{
"required": [
"state"
],
"properties": {
"state": {
"$ref": "#/$defs/state_word"
}
},
"not": {
"required": [
"value"
]
}
}
]
}
}
],
"$defs": {
"sensor_id": {
"description": "A sensor's id: sen_ and a ULID for a sensor a producer registered; builtin.<name> for a built-in count (builtin.incidents.open); mon_ and a ULID for a monitor, which is a built-in sensor too. Never a bare UUID: every id carries its prefix.",
"type": "string",
"maxLength": 64,
"oneOf": [
{
"pattern": "^sen_[0-9A-HJKMNP-TV-Z]{26}$"
},
{
"pattern": "^mon_[0-9A-HJKMNP-TV-Z]{26}$"
},
{
"pattern": "^builtin\\.[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$"
}
]
},
"timestamp": {
"description": "RFC 3339 with a T and a Z or an offset, at most nine fractional digits. The API writes UTC with milliseconds (2026-10-23T14:09:26.123Z).",
"type": "string",
"minLength": 20,
"maxLength": 40,
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]{1,9})?(Z|[+-][0-9]{2}:[0-9]{2})$"
},
"state_word": {
"description": "A state sensor's value: 1 to 32 of a-z, 0-9 and underscore (ok, warn, degraded). A word, never a sentence.",
"type": "string",
"pattern": "^[a-z0-9_]{1,32}$"
},
"label_key": {
"description": "A label key: 1 to 32 of a-z, 0-9 and underscore, one of the sensor's label_keys. A key that names a credential (token, secret, password, authorization, api_key, ...) is refused: the service refuses it at registration with the same rule.",
"type": "string",
"pattern": "^[a-z0-9_]{1,32}$",
"not": {
"pattern": "(^|_)(token|secret|password|passwd|pwd|credentials?|apikey|authorization|bearer|cookie|jwt|(api|private|access|signing|ssh)_key)(_|$)"
}
},
"label_value": {
"description": "A label's value: 1 to 64 characters, no control characters. The one place a producer's text reaches a reading; it is short and its key is on the sensor's allowlist. Put no secret or sentence here.",
"type": "string",
"pattern": "^[^\\u0000-\\u001f\\u007f-\\u009f]{1,64}$"
},
"labels": {
"description": "At most 8 labels, keys from the sensor's label_keys (the schema checks the form; the service checks the list).",
"type": "object",
"maxProperties": 8,
"propertyNames": {
"$ref": "#/$defs/label_key"
},
"additionalProperties": {
"$ref": "#/$defs/label_value"
}
},
"evidence_digest": {
"description": "sha256: and 64 lowercase hex characters, of evidence the producer keeps itself. The digest names the evidence; nothing of it travels (ADR 0063).",
"type": "string",
"pattern": "^sha256:[0-9a-f]{64}$"
}
}
}