Sign in

← All schemas

Schema · sensors/binding

Sensor binding

The tenant a sensor's readings are written into: the one account and the one Decisions space the Atlas writer scopes itself to when it turns that sensor's readings into observations and evidence. A sensor with no binding is not written to Atlas (readings stay in the sensors store); removing the binding stops the writing. The writer reads sensor_id as the key, requires account_id to equal the account the sensor itself belongs to (a binding never moves a sensor across accounts), converts space_id to the Atlas space and opens a store authority scoped to that account and space, so one sensor writes into exactly one tenant. bound_by and bound_at are the audit record of who made the binding and when; the writer does not act on them. A binding names its tenant and nothing else: no content, prompt or credential, and the object is closed so a field outside these is refused. Draft line: it may still be corrected.

Canonical $id, draft
https://schemas.inorbit.hr/sensors/binding/v0.json
01Versions

Each version is a file at its own address. The raw file is plain JSON, served without a sign-in.

02Fields

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_idanyrequired

    The sensor, as SensorsService names it. Defined here the same way as in https://schemas.inorbit.hr/sensors/reading/v0.json#/$defs/sensor_id so the two can be read together; this schema does not refer to it so that it stands alone.

  • account_idstringrequired

    The account the sensor belongs to and the writer scopes to: a UUID, as accounts are written everywhere (accounts have no prefix).

    pattern ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$

  • space_idstringrequired

    The Decisions space the observations are written into: lab_ and a ULID, as decisions_list_spaces names it. Never the bare ULID or the Atlas space UUID derived from it.

    pattern ^lab_[0-9A-HJKMNP-TV-Z]{26}$

  • bound_bystringrequired

    Who made the binding: the verified subject (a person's or an agent's), 1 to 128 characters, no spaces or control characters.

    pattern ^[^\s\u0000-\u001f\u007f-\u009f]{1,128}$

  • bound_atanyrequired

    When it was made.

03Examples

Valid documents with made-up values, as the repository's tests check them.

{
  "sensor_id": "builtin.incidents.open",
  "account_id": "2779ba0b-df69-4cfc-89fe-101441554413",
  "space_id": "lab_01M5FVHEWEWZZ1A66W0PCGZSWV",
  "bound_by": "user:legal@example.org",
  "bound_at": "2026-10-23T16:09:26+02:00"
}

The repository's tests also check 20 documents that this schema must refuse.

04Check a file against it

Replace the file name with yours.

VS Code, JetBrains: settings
"json.schemas": [
  { "fileMatch": ["my-file.json"], "url": "https://schemas.inorbit.hr/sensors/binding/v0.json" }
]
check-jsonschema
check-jsonschema --schemafile https://schemas.inorbit.hr/sensors/binding/v0.json my-file.json
ajv-cli
ajv validate --spec=draft2020 -s <(curl -s https://schemas.inorbit.hr/sensors/binding/v0.json) -d my-file.json
curl
curl -s https://schemas.inorbit.hr/sensors/binding/v0.json
05The schema itself

The raw file of v0, as published.

schemas.inorbit.hr/sensors/binding/v0.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://schemas.inorbit.hr/sensors/binding/v0.json",
  "title": "Sensor binding",
  "description": "The tenant a sensor's readings are written into: the one account and the one Decisions space the Atlas writer scopes itself to when it turns that sensor's readings into observations and evidence. A sensor with no binding is not written to Atlas (readings stay in the sensors store); removing the binding stops the writing. The writer reads sensor_id as the key, requires account_id to equal the account the sensor itself belongs to (a binding never moves a sensor across accounts), converts space_id to the Atlas space and opens a store authority scoped to that account and space, so one sensor writes into exactly one tenant. bound_by and bound_at are the audit record of who made the binding and when; the writer does not act on them. A binding names its tenant and nothing else: no content, prompt or credential, and the object is closed so a field outside these is refused. Draft line: it may still be corrected.",
  "type": "object",
  "additionalProperties": false,
  "required": ["sensor_id", "account_id", "space_id", "bound_by", "bound_at"],
  "properties": {
    "sensor_id": {
      "description": "The sensor, as SensorsService names it. Defined here the same way as in https://schemas.inorbit.hr/sensors/reading/v0.json#/$defs/sensor_id so the two can be read together; this schema does not refer to it so that it stands alone.",
      "$ref": "#/$defs/sensor_id"
    },
    "account_id": {
      "description": "The account the sensor belongs to and the writer scopes to: a UUID, as accounts are written everywhere (accounts have no prefix).",
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
    },
    "space_id": {
      "description": "The Decisions space the observations are written into: lab_ and a ULID, as decisions_list_spaces names it. Never the bare ULID or the Atlas space UUID derived from it.",
      "type": "string",
      "pattern": "^lab_[0-9A-HJKMNP-TV-Z]{26}$"
    },
    "bound_by": {
      "description": "Who made the binding: the verified subject (a person's or an agent's), 1 to 128 characters, no spaces or control characters.",
      "type": "string",
      "pattern": "^[^\\s\\u0000-\\u001f\\u007f-\\u009f]{1,128}$"
    },
    "bound_at": {
      "description": "When it was made.",
      "$ref": "#/$defs/timestamp"
    }
  },
  "$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})$"
    }
  }
}