diff --git a/examples/README.md b/examples/README.md index b38d79b..20faa83 100644 --- a/examples/README.md +++ b/examples/README.md @@ -6,7 +6,7 @@ This directory contains one conforming JSON example payload for each top-level s ## What the examples show -The examples are designed to tell coherent end-to-end stories. The original example set catalogs, governs, transforms, and releases a personal health dataset within an agent session. Newer SourceOS examples show a SourceOS Workstation artifact flowing from content intent through overlays, build request, release manifest, evidence bundle, catalog entry, and access profile. The control-plane examples add the local-first lifecycle proof path: a `ReleaseSet` assigned to an M2 demo device and a `Fingerprint` reporting the realized post-apply state. +The examples are designed to tell coherent end-to-end stories. The original example set catalogs, governs, transforms, and releases a personal health dataset within an agent session. Newer SourceOS examples show a SourceOS Workstation artifact flowing from content intent through overlays, build request, release manifest, evidence bundle, catalog entry, and access profile. The control-plane examples add the local-first lifecycle proof path: a `ReleaseSet` assigned to an M2 demo device, a `BootReleaseSet` for the recovery/provisioning lane, an `EnrollmentToken` authorizing one-time recovery access, and a `Fingerprint` reporting the realized post-apply state. ```text connector.json ──► asset.json @@ -49,7 +49,7 @@ content_spec.json ──► overlay_bundle.json ──► build_request.json ─ ▼ access_profile.json -release_set.json ──► fingerprint.json +release_set.json ──► boot_release_set.json ──► enrollment_token.json ──► fingerprint.json ``` --- @@ -65,13 +65,15 @@ These examples illustrate the Truth Plane contract additions: --- -## Recent additions — Control-plane lifecycle examples +## Recent additions — Control-plane lifecycle and boot provisioning examples -These examples illustrate the local-first control-plane lifecycle family: +These examples illustrate the local-first control-plane lifecycle and secure boot/recovery family: | File | Schema type | Description | |------|------------|-------------| | `release_set.json` | ReleaseSet | Assigned M2 demo release set with source Git ref, target, profile refs, and boot artifact refs | +| `boot_release_set.json` | BootReleaseSet | SourceOS Recovery Environment boot artifact set linked to the assigned ReleaseSet | +| `enrollment_token.json` | EnrollmentToken | One-time recovery authorization token scoped to the M2 demo device and BootReleaseSet | | `fingerprint.json` | Fingerprint | Post-apply device observation proving the realized state matches the assigned release set | --- @@ -118,6 +120,7 @@ These examples illustrate the shared object family used by SourceOS artifact bui | `agreement.json` | Agreement | Default personal-data agreement | | `agent_session.json` | AgentSession | An executor session running the obfuscation workflow | | `asset.json` | PhysicalAsset | Lakehouse asset for curated health observations | +| `boot_release_set.json` | BootReleaseSet | SourceOS Recovery Environment boot artifact set for secure recovery/provisioning | | `build_request.json` | BuildRequest | SourceOS Workstation build request with agentplane and Katello refs | | `capability_token.json` | CapabilityToken | Access token scoped to the health dataset export operation | | `catalog_entry.json` | CatalogEntry | Catalog entry for the SourceOS Workstation dev release | @@ -130,6 +133,7 @@ These examples illustrate the shared object family used by SourceOS artifact bui | `data_sphere.json` | DataSphere | The personal-curated execution environment | | `delta_surface.json` | DeltaSurface | Truth Plane delta surface example | | `enrollment_profile.json` | EnrollmentProfile | Katello/Foreman enrollment profile for dev workstation installs | +| `enrollment_token.json` | EnrollmentToken | One-time recovery authorization token for the M2 local-first demo | | `evidence_bundle.json` | EvidenceBundle | Evidence bundle pointing at agentplane validation/run/replay artifacts | | `execution_decision.json` | ExecutionDecision | Agent allow-decision for a tool invocation | | `event_envelope.json` | EventEnvelope | Event published when the run completes | @@ -176,7 +180,7 @@ npm install -g ajv-cli # Validate a single example ajv validate -s ../schemas/Dataset.json -d dataset.json -# Validate control-plane lifecycle examples +# Validate control-plane lifecycle and boot provisioning examples make validate-control-plane-examples ``` diff --git a/examples/boot_release_set.json b/examples/boot_release_set.json new file mode 100644 index 0000000..d53b0d0 --- /dev/null +++ b/examples/boot_release_set.json @@ -0,0 +1,13 @@ +{ + "boot_release_set_id": "urn:srcos:boot-release-set:m2-demo-recovery-2026-04-26", + "base_release_set_ref": "urn:srcos:release-set:m2-demo-2026-04-26", + "boot_mode": "recovery", + "status": "ready", + "artifacts": { + "kernel_ref": "urn:srcos:artifact:m2-demo-recovery-kernel-sha256-0f3b6d7f", + "initrd_ref": "urn:srcos:artifact:m2-demo-recovery-initrd-sha256-08f4c82e", + "rootfs_ref": "urn:srcos:artifact:m2-demo-recovery-rootfs-sha256-5f4dcc3b" + }, + "created_at": "2026-04-26T14:30:00Z", + "notes": "SourceOS Recovery Environment for the M2 local-first demo. This models the nlboot-evolved recovery path: minimal boot artifacts, signed content-addressed refs, and linkage back to the assigned ReleaseSet." +} diff --git a/examples/enrollment_token.json b/examples/enrollment_token.json new file mode 100644 index 0000000..c8d1e21 --- /dev/null +++ b/examples/enrollment_token.json @@ -0,0 +1,15 @@ +{ + "token_id": "urn:srcos:enrollment-token:m2-demo-recovery-onetime-2026-04-26", + "purpose": "recovery", + "audience": { + "subject_kind": "device", + "subject_id": "urn:srcos:device:m2-local-demo" + }, + "release_set_ref": "urn:srcos:release-set:m2-demo-2026-04-26", + "boot_release_set_ref": "urn:srcos:boot-release-set:m2-demo-recovery-2026-04-26", + "one_time_use": true, + "issued_at": "2026-04-26T14:31:00Z", + "expires_at": "2026-04-26T14:46:00Z", + "status": "issued", + "notes": "Single-use recovery authorization token for the M2 local-first demo. The live/recovery environment redeems this token to authorize the assigned BootReleaseSet and ReleaseSet." +} diff --git a/tools/validate_control_plane_examples.py b/tools/validate_control_plane_examples.py index bbdc05c..df7f2d8 100644 --- a/tools/validate_control_plane_examples.py +++ b/tools/validate_control_plane_examples.py @@ -2,7 +2,6 @@ from __future__ import annotations import json -import sys from pathlib import Path import jsonschema @@ -11,22 +10,25 @@ PAIRS = [ (ROOT / "schemas" / "control-plane" / "ReleaseSet.json", ROOT / "examples" / "release_set.json"), (ROOT / "schemas" / "control-plane" / "Fingerprint.json", ROOT / "examples" / "fingerprint.json"), + (ROOT / "schemas" / "control-plane" / "BootReleaseSet.json", ROOT / "examples" / "boot_release_set.json"), + (ROOT / "schemas" / "control-plane" / "EnrollmentToken.json", ROOT / "examples" / "enrollment_token.json"), ] +def validate_pair(schema_path: Path, example_path: Path) -> None: + schema = json.loads(schema_path.read_text(encoding="utf-8")) + jsonschema.validators.validator_for(schema).check_schema(schema) + legacy_ref = schema.get("allOf", [{}])[0].get("$ref") + validation_schema_path = schema_path.with_name(legacy_ref) if legacy_ref else schema_path + validation_schema = json.loads(validation_schema_path.read_text(encoding="utf-8")) + example = json.loads(example_path.read_text(encoding="utf-8")) + jsonschema.validate(example, validation_schema) + + def main() -> int: checks: dict[str, bool] = {} for schema_path, example_path in PAIRS: - schema = json.loads(schema_path.read_text(encoding="utf-8")) - example = json.loads(example_path.read_text(encoding="utf-8")) - resolver = jsonschema.validators.validator_for(schema) - resolver.check_schema(schema) - validator = resolver(schema, registry=jsonschema.validators.SPECIFICATIONS) - # Resolve local legacy refs by changing cwd-like base through a RefResolver fallback for older jsonschema behavior. - # jsonschema 4.x resolves relative refs against the schema id; these wrapper schemas reference local legacy files. - legacy_schema_path = schema_path.with_name(schema["allOf"][0]["$ref"]) - legacy_schema = json.loads(legacy_schema_path.read_text(encoding="utf-8")) - jsonschema.validate(example, legacy_schema) + validate_pair(schema_path, example_path) checks[example_path.name] = True print(json.dumps({"ok": all(checks.values()), "checks": checks}, indent=2, sort_keys=True)) return 0