Skip to main content

Observation Schema — obs.v0.1

Schema ID: urn:stave:schema:observation:v0.1

An observation is a point-in-time snapshot of infrastructure assets. Each JSON file represents one capture timestamp. Stave requires at least two snapshots (two points in time) for duration-based controls to calculate unsafe periods.

Top-Level Structure

{
"schema_version": "obs.v0.1",
"generated_by": { ... },
"captured_at": "2026-01-15T00:00:00Z",
"assets": [ ... ],
"identities": [ ... ]
}
FieldTypeRequiredDescription
schema_versionstringYesMust be "obs.v0.1"
generated_byobjectNoTool that generated this observation
captured_atstring (date-time)YesRFC 3339 timestamp of capture
assetsarray of assetYesInfrastructure assets
identitiesarray of identityNoIAM identities

The schema uses additionalProperties: false at every level. Extra fields cause validation failure.

generated_by

FieldTypeRequiredDescription
source_typestringNoSource type identifier (e.g., aws.config_api, custom.script)
toolstringNoTool name
tool_versionstringNoTool version
providerstringNoProvider name
provider_versionstringNoProvider version

The source_type field is optional. Any value is accepted, including custom source types from third-party collectors.

Asset

Each asset represents a single infrastructure component.

{
"id": "res:aws:s3:bucket:my-bucket",
"type": "storage_bucket",
"vendor": "aws",
"properties": {
"storage": {
"access": { "public_read": true }
}
},
"source": { "file": "infra/main.tf", "line": 42 }
}
FieldTypeRequiredDescription
idstringYesUnique asset identifier (min 1 char)
typestringYesAsset type (e.g., storage_bucket, iam_role)
vendorstringYesCloud vendor (e.g., aws, gcp, azure)
propertiesobjectYesAsset properties for predicate evaluation
sourcesource_refNoSource file reference

The properties object is free-form — its structure depends on the asset type. Control predicates reference fields within properties using dot-notation paths (e.g., properties.storage.access.public_read).

Identity

Each identity represents an IAM principal.

{
"id": "arn:aws:iam::123456789012:role/app-signer",
"type": "iam_role",
"vendor": "aws",
"grants": { "has_wildcard": false },
"scope": { "distinct_systems": 1, "distinct_resource_groups": 2 }
}
FieldTypeRequiredDescription
idstringYesUnique identity identifier
typestringYesIdentity type (e.g., iam_role, service_account)
vendorstringYesCloud vendor
ownerstringNoOwner of the identity
purposestringNoPurpose of the identity
grantsobjectYesGrant properties
grants.has_wildcardbooleanYesWhether identity has wildcard permissions
scopeobjectYesAccess scope
scope.distinct_systemsintegerYesNumber of distinct systems accessed (min 0)
scope.distinct_resource_groupsintegerYesNumber of distinct resource groups accessed (min 0)
sourcesource_refNoSource file reference

Source Reference

FieldTypeRequiredDescription
filestringNoSource file path
lineintegerNoLine number in source file (min 1)

File Layout

Observations are stored as flat JSON files — one file per timestamp. Do not wrap multiple snapshots in a "snapshots" array; the schema rejects this.

observations/
├── 2026-01-01T000000Z.json
├── 2026-01-02T000000Z.json
└── 2026-01-03T000000Z.json

File naming is not enforced by schema, but using the captured_at timestamp as the filename is the convention used in examples and tests.

Validation

stave validate performs full JSON Schema Draft 2020-12 validation using the santhosh-tekuri/jsonschema library. The schema is embedded in the binary — no external files or network access needed.

Validation also runs automatically at the start of stave apply, so inputs are always checked before evaluation begins.

What the schema enforces

ConstraintDetail
additionalProperties: falseExtra fields are rejected at every level when the schema is applied. The schema applies to per-timestamp files; the directory loader detects bundle-shaped files ({"snapshots":[…]}) and routes them through ParseBundle instead of this schema — so the bundle wrapper is accepted at the loader level, not because the schema permits it.
Required fieldsschema_version, captured_at, assets
const versionschema_version must be exactly "obs.v0.1"
Timestamp formatcaptured_at must be RFC 3339 (date-time)
Asset required fieldsEach asset must have id, type, vendor, properties
String minimumsid, type, vendor must be non-empty (minLength: 1)
Identity required fieldsid, type, vendor, grants (with has_wildcard), scope (with distinct_systems, distinct_resource_groups)
Integer minimumsscope.distinct_systems and scope.distinct_resource_groups must be >= 0; source.line must be >= 1

Commands

# Validate a single observation file
stave validate --in observations/2026-01-01T000000Z.json

# Validate all observations in a directory
stave validate --observations observations/

# Validate from stdin
cat snapshot.json | stave validate --in -

# JSON output for programmatic use
stave validate --observations observations/ --format json

Exit codes: 0 = valid, 2 = validation errors found.

Schema Source

The canonical schema file is schemas/obs.v0.1.schema.json.