Sign in

← All schemas

Schema · atlas/observation

Atlas observation

What an observer reported (RFC 0086, ADR 0006): either an observation, a measured or deterministically read fact (tbd_atlas_core::observation::Observation), or an artifact observation, an artefact's bytes named by their digest (ArtifactObservation). The two never mix: an object with an artifact is an artifact observation (id art_), one without is an observation (id obv_ with its subject, predicate, value and authority). An observation is always positive: absence is concluded from coverage, never reported. Ids are written as the API writes every Atlas id, prefix and UUID, never a bare UUID; the predicate is name@version as in the fact schema; a time carries its wall clock, its boot and its clock's uncertainty. Nothing here carries a document's text, a prompt or a credential, and every object is closed so a field outside these is refused. The observer is https://schemas.inorbit.hr/atlas/observer/v0.json. Draft line: it may still be corrected.

Canonical $id, draft
https://schemas.inorbit.hr/atlas/observation/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.

  • idstringrequired

    Its id: obv_ and a UUID for an observation, art_ and a UUID for an artifact observation.

    pattern ^(obv|art)_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$at most 40 characters

  • observeranyrequired

    The observer that made it: obs_ and a UUID.

  • classanyrequired

    The observer's class. An observation comes from a direct_sensor, a deterministic_extractor or an external_system; an artifact observation from a direct_sensor or a deterministic_extractor. A model's output is an extraction and a person's answer testimony, never these.

  • subjectanyoptional

    What it is about: an entity id, ent_ and a UUID. Observation only.

  • predicateanyoptional

    What it says of the subject: the predicate and its vocabulary version, name@version (net.connects_to@1). The name is dotted lowercase parts, at most 64 characters. Observation only.

  • valueanyoptional

    The object, typed. Observation only.

  • observedanyrequired

    When the observer saw it, as precisely as it can say.

  • authorityanyoptional

    What the observer was allowed to see when it observed. Observation only.

  • artifactanyoptional

    Where the artefact lives and the digest of the bytes read. Artifact observation only.

03Examples

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

{
  "id": "art_0192b8a4-5c3e-7d21-9f4a-2b3c4d5e6f70",
  "observer": "obs_0192b8a4-5c3e-7d21-9f4a-7c8d9e0f1a2b",
  "class": "deterministic_extractor",
  "artifact": {
    "location": "crates/events/src/service/sensors_api.rs@68e8093a65",
    "digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
  },
  "observed": {
    "wall": "2026-10-23T14:09:27Z",
    "boot_id": "build-17",
    "clock": {
      "source": "unknown",
      "uncertainty": {
        "secs": 1,
        "nanos": 0
      }
    }
  }
}

The repository's tests also check 33 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/atlas/observation/v0.json" }
]
check-jsonschema
check-jsonschema --schemafile https://schemas.inorbit.hr/atlas/observation/v0.json my-file.json
ajv-cli
ajv validate --spec=draft2020 -s <(curl -s https://schemas.inorbit.hr/atlas/observation/v0.json) -d my-file.json
curl
curl -s https://schemas.inorbit.hr/atlas/observation/v0.json
05The schema itself

The raw file of v0, as published.

schemas.inorbit.hr/atlas/observation/v0.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://schemas.inorbit.hr/atlas/observation/v0.json",
  "title": "Atlas observation",
  "description": "What an observer reported (RFC 0086, ADR 0006): either an observation, a measured or deterministically read fact (tbd_atlas_core::observation::Observation), or an artifact observation, an artefact's bytes named by their digest (ArtifactObservation). The two never mix: an object with an artifact is an artifact observation (id art_), one without is an observation (id obv_ with its subject, predicate, value and authority). An observation is always positive: absence is concluded from coverage, never reported. Ids are written as the API writes every Atlas id, prefix and UUID, never a bare UUID; the predicate is name@version as in the fact schema; a time carries its wall clock, its boot and its clock's uncertainty. Nothing here carries a document's text, a prompt or a credential, and every object is closed so a field outside these is refused. The observer is https://schemas.inorbit.hr/atlas/observer/v0.json. Draft line: it may still be corrected.",
  "type": "object",
  "additionalProperties": false,
  "required": ["id", "observer", "class", "observed"],
  "properties": {
    "id": {
      "description": "Its id: obv_ and a UUID for an observation, art_ and a UUID for an artifact observation.",
      "type": "string",
      "maxLength": 40,
      "pattern": "^(obv|art)_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
    },
    "observer": {
      "description": "The observer that made it: obs_ and a UUID.",
      "$ref": "https://schemas.inorbit.hr/atlas/observer/v0.json#/$defs/observer_id"
    },
    "class": {
      "description": "The observer's class. An observation comes from a direct_sensor, a deterministic_extractor or an external_system; an artifact observation from a direct_sensor or a deterministic_extractor. A model's output is an extraction and a person's answer testimony, never these.",
      "$ref": "https://schemas.inorbit.hr/atlas/observer/v0.json#/$defs/observer_class"
    },
    "subject": {
      "description": "What it is about: an entity id, ent_ and a UUID. Observation only.",
      "$ref": "#/$defs/entity_id"
    },
    "predicate": {
      "description": "What it says of the subject: the predicate and its vocabulary version, name@version (net.connects_to@1). The name is dotted lowercase parts, at most 64 characters. Observation only.",
      "$ref": "#/$defs/predicate"
    },
    "value": {
      "description": "The object, typed. Observation only.",
      "$ref": "#/$defs/value"
    },
    "observed": {
      "description": "When the observer saw it, as precisely as it can say.",
      "$ref": "#/$defs/observed_time"
    },
    "authority": {
      "description": "What the observer was allowed to see when it observed. Observation only.",
      "$ref": "#/$defs/authority"
    },
    "artifact": {
      "description": "Where the artefact lives and the digest of the bytes read. Artifact observation only.",
      "$ref": "#/$defs/artifact_ref"
    }
  },
  "allOf": [
    {
      "if": {
        "required": ["artifact"]
      },
      "then": {
        "properties": {
          "id": { "pattern": "^art_" },
          "class": { "enum": ["direct_sensor", "deterministic_extractor"] },
          "subject": false,
          "predicate": false,
          "value": false,
          "authority": false
        }
      },
      "else": {
        "required": ["subject", "predicate", "value", "authority"],
        "properties": {
          "id": { "pattern": "^obv_" },
          "class": { "enum": ["direct_sensor", "deterministic_extractor", "external_system"] }
        }
      }
    }
  ],
  "$defs": {
    "entity_id": {
      "description": "An entity's id: ent_ and a UUID.",
      "type": "string",
      "maxLength": 40,
      "pattern": "^ent_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
    },
    "predicate": {
      "description": "A predicate and its vocabulary version, name@version.",
      "type": "string",
      "minLength": 3,
      "maxLength": 75,
      "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*@[0-9]{1,10}$"
    },
    "timestamp": {
      "description": "RFC 3339 in UTC or with 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})$"
    },
    "digest": {
      "description": "sha256: and 64 lowercase hex characters. It names bytes; nothing of them travels (ADR 0063).",
      "type": "string",
      "pattern": "^sha256:[0-9a-f]{64}$"
    },
    "value": {
      "description": "The object of an observation, typed (tbd_atlas_core::vocabulary::Value): a truth value, a whole number, text compared exactly, another entity as its id, a digest, or a duration in milliseconds. There is no fractional number: a reading that has one is written as text or scaled by the writer. Short by design: a value names, it does not quote.",
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["type", "v"],
          "properties": {
            "type": { "const": "bool" },
            "v": { "type": "boolean" }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["type", "v"],
          "properties": {
            "type": { "const": "int" },
            "v": { "type": "integer", "minimum": -9223372036854775808, "maximum": 9223372036854775807 }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["type", "v"],
          "properties": {
            "type": { "const": "text" },
            "v": { "type": "string", "maxLength": 1024 }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["type", "v"],
          "properties": {
            "type": { "const": "entity" },
            "v": { "$ref": "#/$defs/entity_id" }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["type", "v"],
          "properties": {
            "type": { "const": "digest" },
            "v": { "$ref": "#/$defs/digest" }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["type", "v"],
          "properties": {
            "type": { "const": "millis" },
            "v": { "type": "integer", "minimum": 0, "maximum": 18446744073709551615 }
          }
        }
      ]
    },
    "observed_time": {
      "description": "When an observer saw something (tbd_atlas_core::time::ObservedTime): the wall clock, the boot it was read in, monotonic nanoseconds since that boot when the observer has them, and the clock with its worst-case error. Two events are ordered only when their uncertainty windows do not overlap.",
      "type": "object",
      "additionalProperties": false,
      "required": ["wall", "boot_id", "clock"],
      "properties": {
        "wall": {
          "description": "The wall-clock reading.",
          "$ref": "#/$defs/timestamp"
        },
        "boot_id": {
          "description": "The boot it was read in, 1 to 64 characters, no spaces or control characters.",
          "type": "string",
          "pattern": "^[^\\s\\u0000-\\u001f\\u007f-\\u009f]{1,64}$"
        },
        "monotonic_ns": {
          "description": "Nanoseconds since that boot; absent or null when the observer has no monotonic clock (the type writes null).",
          "type": ["integer", "null"],
          "minimum": 0,
          "maximum": 18446744073709551615
        },
        "clock": {
          "description": "The clock and its worst-case error, both ways.",
          "type": "object",
          "additionalProperties": false,
          "required": ["source", "uncertainty"],
          "properties": {
            "source": {
              "description": "The clock source: ntp, ptp, rtc, unknown.",
              "type": "string",
              "pattern": "^[a-z0-9_]{1,32}$"
            },
            "uncertainty": {
              "description": "The worst-case error as whole seconds and nanoseconds, never negative (chrono's TimeDelta, kept as the two numbers it is made of).",
              "type": "object",
              "additionalProperties": false,
              "required": ["secs", "nanos"],
              "properties": {
                "secs": { "type": "integer", "minimum": 0, "maximum": 9223372036854775 },
                "nanos": { "type": "integer", "minimum": 0, "maximum": 999999999 }
              }
            }
          }
        }
      }
    },
    "authority": {
      "description": "What an observer was allowed to see (tbd_atlas_core::observer::AuthorityContext): who it ran as, what it held, and whether that covered everything in scope. When it did not, absent and not visible to me cannot be told apart.",
      "type": "object",
      "additionalProperties": false,
      "required": ["principal", "capabilities", "sufficient"],
      "properties": {
        "principal": {
          "description": "The identity it ran as (a uid, a service account, an API token's subject): 1 to 128 characters, no spaces or control characters. Never the token itself.",
          "type": "string",
          "pattern": "^[^\\s\\u0000-\\u001f\\u007f-\\u009f]{1,128}$"
        },
        "capabilities": {
          "description": "The capabilities or scopes it held, sorted, each 1 to 64 of letters, digits and . _ : / * -.",
          "type": "array",
          "maxItems": 64,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[A-Za-z0-9._:/*-]{1,64}$"
          }
        },
        "sufficient": {
          "description": "Whether its authority covered everything in scope.",
          "type": "boolean"
        }
      }
    },
    "artifact_ref": {
      "description": "Where an artefact lives and what its bytes were (tbd_atlas_core::observation::ArtifactRef). The location names the artefact (a repository path at a commit, a URL, a document version); the bytes are never here.",
      "type": "object",
      "additionalProperties": false,
      "required": ["location", "digest"],
      "properties": {
        "location": {
          "description": "1 to 1024 characters, no control characters, and no credentials in a URL (user:password@host).",
          "type": "string",
          "pattern": "^[^\\u0000-\\u001f\\u007f-\\u009f]{1,1024}$",
          "not": {
            "pattern": "^[A-Za-z][A-Za-z0-9+.-]*://[^/?#]*@"
          }
        },
        "digest": {
          "description": "The digest of the bytes read, computed from the bytes.",
          "$ref": "#/$defs/digest"
        }
      }
    }
  }
}