Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: validate validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples
.PHONY: validate validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples validate-lifecycle-boundary-examples

validate: validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples
validate: validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples validate-lifecycle-boundary-examples
@echo "OK: validate"

validate-control-plane-examples:
Expand All @@ -22,3 +22,7 @@ validate-ops-history-examples:
validate-runtime-observability-examples:
python3 -m pip install --user jsonschema >/dev/null
python3 tools/validate_runtime_observability_examples.py

validate-lifecycle-boundary-examples:
python3 -m pip install --user jsonschema >/dev/null
python3 tools/validate_lifecycle_boundary_examples.py
26 changes: 26 additions & 0 deletions examples/grant-state-decision.missing-authorization.invalid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"schema_version": "1.1.0",
"decision_kind": "grant-state-decision",
"decision_id": "grant-state-decision:missing-authorization-invalid",
"ts_decided": "2026-05-27T00:54:00Z",
"grant_ref": "agent-grant:agent-alpha-001",
"subject_ref": "agent://agent-alpha",
"authority_decision": "revoked",
"decision_actor_ref": "service://sourceos-policy-adjudicator",
"authorization_policy_ref": "",
"authorization_evidence_refs": [],
"effective_at": "2026-05-27T00:55:00Z",
"authority_effects": {
"tool_access": "revoked",
"memory_access": "revoked",
"event_write": "revoked",
"bridge_export": "revoked",
"runtime_dispatch": "revoked"
},
"source_policy_decision_ref": "policy-decision:agent-alpha-revoke-001",
"source_runtime_effect_decision_ref": null,
"restoration_allowed": false,
"restoration_conditions": [],
"evidence_refs": ["evidence://sourceos/grant-state/missing-authorization-invalid"],
"explanation": "Invalid fixture: grant state changes require authorization policy and evidence refs."
}
26 changes: 26 additions & 0 deletions examples/grant-state-decision.valid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"schema_version": "1.1.0",
"decision_kind": "grant-state-decision",
"decision_id": "grant-state-decision:agent-alpha-review-001",
"ts_decided": "2026-05-27T00:52:00Z",
"grant_ref": "agent-grant:agent-alpha-001",
"subject_ref": "agent://agent-alpha",
"authority_decision": "reduced",
"decision_actor_ref": "service://sourceos-policy-adjudicator",
"authorization_policy_ref": "policy://sourceos/grant-state-v1.1",
"authorization_evidence_refs": ["policy-decision:agent-alpha-review-001", "evidence://sourceos/grant-state/agent-alpha-review-001"],
"effective_at": "2026-05-27T00:53:00Z",
"authority_effects": {
"tool_access": "reduced",
"memory_access": "unchanged",
"event_write": "reduced",
"bridge_export": "suspended",
"runtime_dispatch": "reduced"
},
"source_policy_decision_ref": "policy-decision:agent-alpha-review-001",
"source_runtime_effect_decision_ref": null,
"restoration_allowed": true,
"restoration_conditions": ["fresh policy pass", "grant not expired", "no revocation ref present"],
"evidence_refs": ["evidence://sourceos/grant-state/agent-alpha-review-001"],
"explanation": "Policy review requires reduced authority; grant state change is recorded separately from policy and runtime effect decisions."
}
25 changes: 25 additions & 0 deletions examples/runtime-effect-decision.authority-mutated.invalid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"schema_version": "1.1.0",
"decision_kind": "runtime-effect-decision",
"decision_id": "runtime-effect-decision:invalid-authority-mutation",
"ts_decided": "2026-05-27T00:51:00Z",
"policy_decision_ref": "policy-decision:demo-export-001",
"event_ref": {
"event_id": "sourceos-event:demo-001",
"event_content_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
},
"runtime_effect": "allow_dispatch",
"effect_status": "admitted",
"effect_scope": {
"component_ref": "agent-term://dispatch",
"operation": "dispatch_agent",
"side_effecting": true
},
"authority_mutation_performed": true,
"ledger_write_performed": false,
"grant_state_decision_ref": "grant-state-decision:invalid-inline-mutation",
"ledger_record_ref": null,
"downstream_refs": [],
"evidence_refs": ["evidence://sourceos/runtime-effect/invalid-authority-mutation"],
"explanation": "Invalid fixture: runtime effect decisions must not mutate authority inline."
}
25 changes: 25 additions & 0 deletions examples/runtime-effect-decision.valid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"schema_version": "1.1.0",
"decision_kind": "runtime-effect-decision",
"decision_id": "runtime-effect-decision:demo-dispatch-001",
"ts_decided": "2026-05-27T00:50:00Z",
"policy_decision_ref": "policy-decision:demo-export-001",
"event_ref": {
"event_id": "sourceos-event:demo-001",
"event_content_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
},
"runtime_effect": "export_ref_only",
"effect_status": "admitted",
"effect_scope": {
"component_ref": "sourceos-shell://receipt-export",
"operation": "export_receipt_ref",
"side_effecting": false
},
"authority_mutation_performed": false,
"ledger_write_performed": false,
"grant_state_decision_ref": null,
"ledger_record_ref": null,
"downstream_refs": ["grant-state-decision:optional-followup"],
"evidence_refs": ["evidence://sourceos/runtime-effect/demo-001"],
"explanation": "Policy decision allows ref-only export; runtime effect admits only the ref export and performs no authority mutation."
}
54 changes: 54 additions & 0 deletions schemas/grant-state-decision.v1.1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://sourceos.dev/schemas/grant-state-decision.v1.1.json",
"title": "SourceOS Grant State Decision v1.1",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"decision_kind",
"decision_id",
"ts_decided",
"grant_ref",
"subject_ref",
"authority_decision",
"decision_actor_ref",
"authorization_policy_ref",
"authorization_evidence_refs",
"effective_at",
"authority_effects",
"source_policy_decision_ref",
"restoration_allowed"
],
"properties": {
"schema_version": {"const": "1.1.0"},
"decision_kind": {"const": "grant-state-decision"},
"decision_id": {"type": "string", "minLength": 1},
"ts_decided": {"type": "string", "format": "date-time"},
"grant_ref": {"type": "string", "minLength": 1},
"subject_ref": {"type": "string", "minLength": 1},
"authority_decision": {"type": "string", "enum": ["unchanged", "reduced", "suspended", "revoked", "restored"]},
"decision_actor_ref": {"type": "string", "minLength": 1},
"authorization_policy_ref": {"type": "string", "minLength": 1},
"authorization_evidence_refs": {"type": "array", "minItems": 1, "items": {"type": "string"}, "uniqueItems": true},
"effective_at": {"type": "string", "format": "date-time"},
"authority_effects": {
"type": "object",
"additionalProperties": false,
"required": ["tool_access", "memory_access", "event_write", "bridge_export", "runtime_dispatch"],
"properties": {
"tool_access": {"type": "string", "enum": ["unchanged", "reduced", "suspended", "revoked", "restored"]},
"memory_access": {"type": "string", "enum": ["unchanged", "reduced", "suspended", "revoked", "restored"]},
"event_write": {"type": "string", "enum": ["unchanged", "reduced", "suspended", "revoked", "restored"]},
"bridge_export": {"type": "string", "enum": ["unchanged", "reduced", "suspended", "revoked", "restored"]},
"runtime_dispatch": {"type": "string", "enum": ["unchanged", "reduced", "suspended", "revoked", "restored"]}
}
},
"source_policy_decision_ref": {"type": ["string", "null"]},
"source_runtime_effect_decision_ref": {"type": ["string", "null"]},
"restoration_allowed": {"type": "boolean"},
"restoration_conditions": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"evidence_refs": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"explanation": {"type": "string"}
}
}
60 changes: 60 additions & 0 deletions schemas/runtime-effect-decision.v1.1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://sourceos.dev/schemas/runtime-effect-decision.v1.1.json",
"title": "SourceOS Runtime Effect Decision v1.1",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"decision_kind",
"decision_id",
"ts_decided",
"policy_decision_ref",
"event_ref",
"runtime_effect",
"effect_status",
"effect_scope",
"authority_mutation_performed",
"ledger_write_performed",
"evidence_refs"
],
"properties": {
"schema_version": {"const": "1.1.0"},
"decision_kind": {"const": "runtime-effect-decision"},
"decision_id": {"type": "string", "minLength": 1},
"ts_decided": {"type": "string", "format": "date-time"},
"policy_decision_ref": {"type": "string", "minLength": 1},
"event_ref": {
"type": "object",
"additionalProperties": false,
"required": ["event_id", "event_content_sha256"],
"properties": {
"event_id": {"type": "string", "minLength": 1},
"event_content_sha256": {"$ref": "#/$defs/sha256"}
}
},
"runtime_effect": {
"type": "string",
"enum": ["allow_dispatch", "deny_dispatch", "require_review", "redact_payload", "metadata_only", "export_ref_only", "quarantine", "block", "noop"]
},
"effect_status": {"type": "string", "enum": ["admitted", "partial", "rejected", "requires_review", "failed_closed"]},
"effect_scope": {
"type": "object",
"additionalProperties": false,
"required": ["component_ref", "operation", "side_effecting"],
"properties": {
"component_ref": {"type": "string", "minLength": 1},
"operation": {"type": "string", "minLength": 1},
"side_effecting": {"type": "boolean"}
}
},
"authority_mutation_performed": {"const": false},
"ledger_write_performed": {"const": false},
"grant_state_decision_ref": {"type": ["string", "null"]},
"ledger_record_ref": {"type": ["string", "null"]},
"downstream_refs": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"evidence_refs": {"type": "array", "minItems": 1, "items": {"type": "string"}, "uniqueItems": true},
"explanation": {"type": "string"}
},
"$defs": {"sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"}}
}
104 changes: 104 additions & 0 deletions tools/validate_lifecycle_boundary_examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env python3
from __future__ import annotations

import json
from pathlib import Path
from typing import Any

import jsonschema

ROOT = Path(__file__).resolve().parents[1]
RUNTIME_SCHEMA = ROOT / "schemas" / "runtime-effect-decision.v1.1.json"
GRANT_SCHEMA = ROOT / "schemas" / "grant-state-decision.v1.1.json"
RUNTIME_VALID = ROOT / "examples" / "runtime-effect-decision.valid.json"
RUNTIME_INVALID_AUTHORITY = ROOT / "examples" / "runtime-effect-decision.authority-mutated.invalid.json"
GRANT_VALID = ROOT / "examples" / "grant-state-decision.valid.json"
GRANT_INVALID_MISSING_AUTH = ROOT / "examples" / "grant-state-decision.missing-authorization.invalid.json"


class ValidationError(Exception):
pass


def load(path: Path) -> dict[str, Any]:
payload = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(payload, dict):
raise ValidationError(f"{path}: expected JSON object")
return payload


def validate_json_schema(schema_path: Path, instance_path: Path) -> dict[str, Any]:
schema = load(schema_path)
jsonschema.validators.validator_for(schema).check_schema(schema)
instance = load(instance_path)
jsonschema.validate(instance, schema)
return instance


def validate_runtime_effect(instance: dict[str, Any]) -> None:
if instance.get("decision_kind") != "runtime-effect-decision":
raise ValidationError("runtime effect decision_kind mismatch")
if instance.get("authority_mutation_performed") is not False:
raise ValidationError("runtime effect decisions must not mutate authority")
if instance.get("ledger_write_performed") is not False:
raise ValidationError("runtime effect decisions must not write ledger records")
effect = instance.get("runtime_effect")
status = instance.get("effect_status")
scope = instance.get("effect_scope", {})
if effect in {"allow_dispatch", "export_ref_only"} and status not in {"admitted", "partial"}:
raise ValidationError(f"{effect} requires admitted or partial status")
if effect in {"block", "quarantine", "deny_dispatch"} and status == "admitted":
raise ValidationError(f"{effect} cannot report admitted status")
if scope.get("side_effecting") is True and effect in {"metadata_only", "export_ref_only", "noop"}:
raise ValidationError("metadata/ref/noop runtime effects cannot be side-effecting")
if instance.get("grant_state_decision_ref") and instance.get("authority_mutation_performed") is not False:
raise ValidationError("grant_state_decision_ref is a reference, not inline authority mutation")


def validate_grant_state(instance: dict[str, Any]) -> None:
if instance.get("decision_kind") != "grant-state-decision":
raise ValidationError("grant state decision_kind mismatch")
if not instance.get("authorization_policy_ref"):
raise ValidationError("grant state decisions require authorization_policy_ref")
if not instance.get("authorization_evidence_refs"):
raise ValidationError("grant state decisions require authorization_evidence_refs")
decision = instance.get("authority_decision")
effects = instance.get("authority_effects", {})
changed = any(value != "unchanged" for value in effects.values())
if decision == "unchanged" and changed:
raise ValidationError("unchanged grant state decision requires unchanged authority_effects")
if decision != "unchanged" and not changed:
raise ValidationError("changed grant state decision requires changed authority_effects")
if decision == "revoked" and any(value != "revoked" for value in effects.values()):
raise ValidationError("revoked grant state decision requires all authority_effects revoked")
if decision == "restored" and instance.get("restoration_allowed") is not True:
raise ValidationError("restored grant state decision requires restoration_allowed=true")


def expect_invalid(schema_path: Path, instance_path: Path, semantic_validator) -> None:
try:
instance = validate_json_schema(schema_path, instance_path)
semantic_validator(instance)
except Exception:
return
raise ValidationError(f"invalid fixture unexpectedly validated: {instance_path.relative_to(ROOT)}")


def main() -> int:
runtime = validate_json_schema(RUNTIME_SCHEMA, RUNTIME_VALID)
validate_runtime_effect(runtime)
grant = validate_json_schema(GRANT_SCHEMA, GRANT_VALID)
validate_grant_state(grant)
expect_invalid(RUNTIME_SCHEMA, RUNTIME_INVALID_AUTHORITY, validate_runtime_effect)
expect_invalid(GRANT_SCHEMA, GRANT_INVALID_MISSING_AUTH, validate_grant_state)
print(json.dumps({"ok": True, "checks": [
str(RUNTIME_VALID.relative_to(ROOT)),
str(GRANT_VALID.relative_to(ROOT)),
str(RUNTIME_INVALID_AUTHORITY.relative_to(ROOT)),
str(GRANT_INVALID_MISSING_AUTH.relative_to(ROOT)),
]}, indent=2, sort_keys=True))
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading