Contract-First Schemas
Stave uses contract-first JSON schemas so validation is deterministic, offline, and portable across toolchains. All schemas use JSON Schema Draft 2020-12.
Schema Versions
| Contract | Version | Schema ID |
|---|---|---|
| Control | ctrl.v1 | urn:stave:schema:control:v1 |
| Observation | obs.v0.1 | urn:stave:schema:observation:v1 |
| Snapshot (external producer) | n/a | urn:stave:schema:snapshot:external |
| Output | out.v0.1 | urn:stave:schema:output:v1 |
| Finding | v1 | urn:stave:schema:finding:v1 |
| Diagnose | v1 | urn:stave:schema:diagnose:v1 |
Two observation schemas — one engine-internal, one for external producers.
urn:stave:schema:observation:v1is the strict wire-format the engine validates inbound snapshots against (schemas/observation/v1/observation.schema.json).urn:stave:schema:snapshot:externalatdocs/snapshot.schema.jsonis a documented producer-side contract for external tools (Terraform plan renderers, CloudFormation renderers, test-fixture generators) — the same shape, with looser language about field meaning, intended for reading by humans writing extractors. Use the strict schema for validation; reference the external schema for documentation.Version strings vs schema URNs. The Version column carries the user-facing wire-format identifier emitted in document
schema_versionfields (e.g.out.v0.1lands in the JSON output under"schema_version": "out.v0.1"). The Schema ID column carries the internal URN the schema registry compiles each contract under. The two evolve independently: a wire-format revision (e.g.out.v0.2) does not require a new URN if the schema document is backward-compatible, and a URN bump (urn:…:v2) does not require all consumers to switch wire versions in lockstep. Forward-compatibility note: bothobs.v0.1and a futureobs.v1are accepted by the loader so producers can ship the new wire string ahead of consumer rollout.
Control Contract (ctrl.v1)
A control defines a safety check. Required fields:
| Field | Type | Required | Description |
|---|---|---|---|
dsl_version | string | yes | "ctrl.v1" |
id | string | yes | Unique ID (e.g., CTL.S3.PUBLIC.001) |
name | string | yes | Short human-readable name |
description | string | yes | What unsafe condition this detects |
type | string | yes | Check category (e.g., unsafe_state, authorization_boundary) |
One of:
| Field | Description |
|---|---|
unsafe_predicate | Inline predicate logic (any/all + field operators) |
unsafe_predicate_alias | Named alias expanded at load time (e.g., s3.is_public_readable) |
Optional fields:
| Field | Type | Description |
|---|---|---|
version | string | Control document version |
domain | string | Grouping label (exposure, governance, access) |
scope_tags | array | Applicability tags (aws, s3, prod) |
severity | string | critical, high, medium, low, info |
compliance | object | Framework mappings (cis_aws_v1.4.0: "2.1.1") |
params | object | Values for value_from_param in predicates |
exposure | object | Exposure classification (type + principal_scope) |
remediation | object | Fix guidance (description, action, example) |
See Evaluation Semantics for predicate operators and matching rules.
Observation Contract (obs.v0.1)
A point-in-time snapshot of asset state. The directory loader accepts
two file shapes: a flat per-timestamp JSON object (one snapshot per
file, fields below) or a bundle file with
{"schema_version":"obs.v0.1","snapshots":[{…},{…}]} that expands
inline. Per-timestamp files run the strict obs.v0.1 schema validator;
bundle files route through ParseBundle and skip per-file validation.
The single-snapshot stdin/composition path requires per-timestamp
shape and errors explicitly on bundles.
| Field | Type | Required | Description |
|---|---|---|---|
schema_version | string | yes | "obs.v0.1" |
captured_at | string | yes | RFC 3339 capture time |
assets | array | yes | Asset state objects |
generated_by | object | no | Extraction metadata |
identities | array | no | IAM identity objects |
Each asset: id, type, vendor, properties (required), source
(optional).
See Observation Contract for the full field dictionary.
Output Contract (out.v0.1)
Evaluation output. Two kinds distinguished by the kind field:
Evaluation output (kind: "ASSESSMENT")
| Field | Type | Required | Description |
|---|---|---|---|
schema_version | string | yes | "out.v0.1" |
kind | string | yes | "ASSESSMENT" |
run | object | yes | Run metadata (tool version, timing, parameters) |
summary | object | yes | Aggregate counts (violations, assets, controls) |
findings | array | yes | Individual violation findings |
Verification output (kind: "ATTESTATION")
| Field | Type | Required | Description |
|---|---|---|---|
schema_version | string | yes | "out.v0.1" |
kind | string | yes | "ATTESTATION" |
run | object | yes | Run metadata |
summary | object | yes | Before/after counts |
resolved | array | yes | Findings resolved between runs |
remaining | array | yes | Findings still present |
introduced | array | yes | New findings since baseline |
Finding Contract (v1)
Each finding represents a single control violation for a specific asset.
Required fields:
| Field | Type | Description |
|---|---|---|
control_id | string | Control that was violated |
control_name | string | Human-readable control name |
control_description | string | What was detected |
asset_id | string | Asset that violated the control |
asset_type | string | Asset type (storage_bucket) |
asset_vendor | string | Cloud vendor (aws) |
evidence | object | Temporal proof of violation |
remediation | object | Fix guidance |
Optional fields:
| Field | Type | Description |
|---|---|---|
source | object | Source file + line reference |
fix_plan | object | Machine-readable fix actions |
exposure | object | Exposure classification (type + principal_scope) |
posture_drift | object | Temporal pattern (persistent, degraded, intermittent) + exposure window count |
Evidence structure
| Field | Type | Description |
|---|---|---|
first_unsafe_at | string | RFC 3339 timestamp of first unsafe observation |
unsafe_duration_hours | number | Hours continuously unsafe |
threshold_hours | number | Max-unsafe threshold that was exceeded |
reason | string | Why the asset is unsafe |
value | any | Matched field value (optional) |
source_evidence | array | Snapshot source references (optional) |
Remediation structure
| Field | Type | Required | Description |
|---|---|---|---|
description | string | yes | How to fix the condition |
action | string | yes | Specific action to take |
example | string | no | Example command or config |
Schema Layout
schemas/
control/v1/control.schema.json
observation/v1/observation.schema.json
output/v1/output.schema.json
finding/v1/finding.schema.json
diagnose/v1/diagnose.schema.json
Embedded Runtime Schemas
The CLI embeds schemas in internal/contracts/schema/embedded/ for
offline use. Programmatic access:
schema.LoadSchema(kind, version)
Supported kinds: control, observation, finding, output, diagnose.
Validate vs Lint
| Command | Purpose |
|---|---|
stave validate | Schema conformance (structural contract validity) |
stave lint | Authoring quality (design conventions, determinism) |
Deterministic Guarantees
- Offline-only schema loading (embedded files).
- Deterministic diagnostic ordering.
- Stable lint output format:
path:line:col RULE_ID message.
Polyglot Validation
Schemas are published as versioned JSON files. Non-Go tools can validate the same contracts without Stave runtime dependencies.