From 06a4ee6d56945fd0d48bb78288624fb826bfa842 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 23:48:41 -0400 Subject: [PATCH 1/8] feat(spec): add HostCapabilityPlacement schema --- schemas/HostCapabilityPlacement.json | 100 +++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 schemas/HostCapabilityPlacement.json diff --git a/schemas/HostCapabilityPlacement.json b/schemas/HostCapabilityPlacement.json new file mode 100644 index 0000000..d9cb79c --- /dev/null +++ b/schemas/HostCapabilityPlacement.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/HostCapabilityPlacement.json", + "title": "HostCapabilityPlacement", + "description": "A SourceOS immutable-node placement declaration for one host capability, service workload, extension, or state root.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "name", + "placementClass", + "authority" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:host-capability-placement:", + "description": "Stable URN identifier. Pattern: urn:srcos:host-capability-placement:." + }, + "type": { + "const": "HostCapabilityPlacement", + "description": "Discriminator constant — always HostCapabilityPlacement." + }, + "specVersion": { + "type": "string", + "description": "Spec version of this document, e.g. 2.2.0." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Human-readable capability name." + }, + "placementClass": { + "type": "string", + "enum": [ + "image-baked-host-capability", + "sysext-host-capability", + "confext-policy-config-capability", + "quadlet-bound-service-workload", + "quadlet-floating-service-workload", + "var-state-object" + ], + "description": "Canonical placement class inherited from the immutable-node host capability model." + }, + "authority": { + "type": "string", + "enum": [ + "sourceos-base", + "sourceos-boot", + "agent-machine", + "sourceos-devtools", + "agentplane", + "prophet-platform", + "socios-optional-pack" + ], + "description": "Repo or plane class that owns the implementation boundary for this capability." + }, + "mandatoryForBaseNode": { + "type": "boolean", + "default": false, + "description": "Whether the capability is required for a base SourceOS immutable node without Socios enrollment." + }, + "requiresEnrollment": { + "type": "boolean", + "default": false, + "description": "Whether activation requires opt-in enrollment, Proof-of-Life, or signed intent." + }, + "lifecycleCoupling": { + "type": "string", + "enum": [ + "host-release", + "extension-release", + "service-release", + "state-schema", + "operator-local" + ], + "description": "Primary lifecycle to which this capability is coupled." + }, + "pathHints": { + "type": "array", + "items": { "type": "string" }, + "description": "Expected implementation paths such as /usr/libexec/sourceos, /usr/share/containers/systemd, /var/lib/sourceos, or /var/lib/socios." + }, + "evidenceRefs": { + "type": "array", + "items": { "type": "string" }, + "description": "Optional references to evidence records emitted by the capability." + }, + "policyRef": { + "type": ["string", "null"], + "description": "Optional policy identifier or URN governing activation or mutation." + }, + "notes": { + "type": ["string", "null"], + "description": "Human-readable review notes." + } + } +} From 01a88b1d1fc109c8bd88be77e17b06d4f0bd45bb Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Tue, 5 May 2026 00:57:24 -0400 Subject: [PATCH 2/8] feat(spec): add NodeStateSchema schema --- schemas/NodeStateSchema.json | 91 ++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 schemas/NodeStateSchema.json diff --git a/schemas/NodeStateSchema.json b/schemas/NodeStateSchema.json new file mode 100644 index 0000000..aca0ab2 --- /dev/null +++ b/schemas/NodeStateSchema.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/NodeStateSchema.json", + "title": "NodeStateSchema", + "description": "A SourceOS immutable-node durable state schema declaration for rollback-aware host and service state roots.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "name", + "rootPath", + "stateClass", + "rollbackCompatibility" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:node-state-schema:", + "description": "Stable URN identifier. Pattern: urn:srcos:node-state-schema:." + }, + "type": { + "const": "NodeStateSchema", + "description": "Discriminator constant — always NodeStateSchema." + }, + "specVersion": { + "type": "string", + "description": "Spec version of this document, e.g. 2.2.0." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Human-readable state root name." + }, + "rootPath": { + "type": "string", + "pattern": "^/var/(lib|cache)/", + "description": "Durable or rebuildable state root. Authoritative mutable truth must not live under /usr or /etc." + }, + "stateClass": { + "type": "string", + "enum": [ + "durable-truth", + "append-only-evidence", + "replay-checkpoint", + "model-cache", + "rebuildable-cache", + "operator-local" + ], + "description": "Semantic class of the state root." + }, + "schemaRef": { + "type": ["string", "null"], + "description": "Optional schema, contract, or URN reference for records stored in this root." + }, + "rollbackCompatibility": { + "type": "string", + "enum": [ + "required-n-and-n-minus-1", + "best-effort", + "not-required-rebuildable", + "operator-managed" + ], + "description": "Rollback compatibility expectation for readers and writers of this state root." + }, + "mutability": { + "type": "string", + "enum": [ + "append-only", + "controlled-write", + "rebuildable", + "ephemeral-cache" + ], + "description": "Expected write posture for this root." + }, + "owner": { + "type": ["string", "null"], + "description": "Owning plane or repository class for this state root." + }, + "evidenceRequired": { + "type": "boolean", + "default": false, + "description": "Whether writes to this state root require evidence emission." + }, + "notes": { + "type": ["string", "null"], + "description": "Human-readable review notes." + } + } +} From 24214908a1496a2f4f0e96a446f239afe8cf726f Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Tue, 5 May 2026 01:15:09 -0400 Subject: [PATCH 3/8] feat(spec): add ImmutableNodeProfile schema --- schemas/ImmutableNodeProfile.json | 118 ++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 schemas/ImmutableNodeProfile.json diff --git a/schemas/ImmutableNodeProfile.json b/schemas/ImmutableNodeProfile.json new file mode 100644 index 0000000..fa28d35 --- /dev/null +++ b/schemas/ImmutableNodeProfile.json @@ -0,0 +1,118 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/ImmutableNodeProfile.json", + "title": "ImmutableNodeProfile", + "description": "A SourceOS immutable-node profile tying together boot/release references, host capability placements, durable state schemas, validation entrypoints, and optional Socios capability-pack posture.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "name", + "channel", + "substrate" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:immutable-node-profile:", + "description": "Stable URN identifier. Pattern: urn:srcos:immutable-node-profile:." + }, + "type": { + "const": "ImmutableNodeProfile", + "description": "Discriminator constant — always ImmutableNodeProfile." + }, + "specVersion": { + "type": "string", + "description": "Spec version of this document, e.g. 2.2.0." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Human-readable immutable node profile name." + }, + "channel": { + "type": "string", + "minLength": 1, + "description": "Realization channel such as linux-dev, linux-stable, m2-asahi-dev, or fog-node." + }, + "substrate": { + "type": "object", + "additionalProperties": false, + "required": ["strategy", "hostMutationPosture", "sociosRequired"], + "properties": { + "strategy": { + "type": "string", + "enum": ["bootc-first", "ostree-compatible", "rpm-ostree", "coreos-assembler", "nixos-adapter", "other"], + "description": "Primary immutable-host realization strategy." + }, + "hostMutationPosture": { + "type": "string", + "enum": ["immutable-runtime", "staged-updates-only", "lab-live-mutation-allowed", "unknown"], + "description": "Allowed host mutation posture for this profile." + }, + "sociosRequired": { + "const": false, + "description": "Base SourceOS immutable nodes must not require Socios enrollment. Optional Socios packs are modeled separately." + } + } + }, + "bootReleaseSetRef": { + "type": ["string", "null"], + "description": "Optional BootReleaseSet or boot/recovery handoff reference." + }, + "releaseSetRef": { + "type": ["string", "null"], + "description": "Optional ReleaseSet assignment reference." + }, + "workstationProfileRef": { + "type": ["string", "null"], + "description": "Optional WorkstationProfile reference for desktop/workstation realization." + }, + "agentMachineProfileRef": { + "type": ["string", "null"], + "description": "Optional Agent Machine or AgentPod profile reference for local runtime realization." + }, + "hostCapabilityPlacementRefs": { + "type": "array", + "items": { "type": "string", "pattern": "^urn:srcos:host-capability-placement:" }, + "description": "References to HostCapabilityPlacement objects used by this profile." + }, + "nodeStateSchemaRefs": { + "type": "array", + "items": { "type": "string", "pattern": "^urn:srcos:node-state-schema:" }, + "description": "References to NodeStateSchema objects used by this profile." + }, + "optionalSociosCapabilityPackRefs": { + "type": "array", + "items": { "type": "string" }, + "description": "Optional Socios capability pack references. Presence does not imply base node dependency or enrollment." + }, + "validation": { + "type": "object", + "additionalProperties": false, + "properties": { + "planCommand": { "type": ["string", "null"] }, + "validateCommand": { "type": ["string", "null"] }, + "inspectCommand": { "type": ["string", "null"] }, + "evidenceCommand": { "type": ["string", "null"] } + }, + "description": "Operator-facing dry-run, validation, inspection, and evidence entrypoints." + }, + "policyRefs": { + "type": "array", + "items": { "type": "string" }, + "description": "Policy references that govern activation, mutation, enrollment, or rollback." + }, + "evidenceRefs": { + "type": "array", + "items": { "type": "string" }, + "description": "Evidence or receipt references expected from this node profile." + }, + "notes": { + "type": ["string", "null"], + "description": "Human-readable review notes." + } + } +} From 13a616a18c15bef5b7a80b2ca75d1d668431b3be Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Tue, 5 May 2026 01:24:22 -0400 Subject: [PATCH 4/8] feat(spec): add HostCapabilityPlacement example --- examples/hostcapabilityplacement.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 examples/hostcapabilityplacement.json diff --git a/examples/hostcapabilityplacement.json b/examples/hostcapabilityplacement.json new file mode 100644 index 0000000..3fb0208 --- /dev/null +++ b/examples/hostcapabilityplacement.json @@ -0,0 +1,20 @@ +{ + "id": "urn:srcos:host-capability-placement:sourceos-supervisor", + "type": "HostCapabilityPlacement", + "specVersion": "2.2.0", + "name": "SourceOS host supervisor", + "placementClass": "image-baked-host-capability", + "authority": "sourceos-base", + "mandatoryForBaseNode": true, + "requiresEnrollment": false, + "lifecycleCoupling": "host-release", + "pathHints": [ + "/usr/libexec/sourceos/supervisor", + "/usr/lib/systemd/system/sourceos-supervisor.service" + ], + "evidenceRefs": [ + "urn:srcos:release-receipt:sourceos-supervisor-bootstrap" + ], + "policyRef": "urn:srcos:policy:immutable-node-host-supervisor", + "notes": "Base SourceOS capability. This is not a Socios enrollment dependency." +} From 36452cf40f5778201814d50c33b566778ff55757 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Tue, 5 May 2026 02:07:43 -0400 Subject: [PATCH 5/8] feat(spec): add NodeStateSchema example --- examples/nodestateschema.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 examples/nodestateschema.json diff --git a/examples/nodestateschema.json b/examples/nodestateschema.json new file mode 100644 index 0000000..367b2a8 --- /dev/null +++ b/examples/nodestateschema.json @@ -0,0 +1,14 @@ +{ + "id": "urn:srcos:node-state-schema:sourceos-evidence-root", + "type": "NodeStateSchema", + "specVersion": "2.2.0", + "name": "SourceOS evidence root", + "rootPath": "/var/lib/sourceos/evidence", + "stateClass": "append-only-evidence", + "schemaRef": "urn:srcos:release-receipt:sourceos-evidence", + "rollbackCompatibility": "required-n-and-n-minus-1", + "mutability": "append-only", + "owner": "agent-machine", + "evidenceRequired": true, + "notes": "Durable evidence root survives host rollback and must preserve reader compatibility." +} From f8b57c58dbe433a5801d38eb8bbaa4d75ec2427b Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Tue, 5 May 2026 02:24:49 -0400 Subject: [PATCH 6/8] feat(spec): add ImmutableNodeProfile example --- examples/immutablenodeprofile.json | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 examples/immutablenodeprofile.json diff --git a/examples/immutablenodeprofile.json b/examples/immutablenodeprofile.json new file mode 100644 index 0000000..7776739 --- /dev/null +++ b/examples/immutablenodeprofile.json @@ -0,0 +1,39 @@ +{ + "id": "urn:srcos:immutable-node-profile:m2-asahi-dev", + "type": "ImmutableNodeProfile", + "specVersion": "2.2.0", + "name": "M2 Asahi SourceOS dev immutable node", + "channel": "m2-asahi-dev", + "substrate": { + "strategy": "bootc-first", + "hostMutationPosture": "staged-updates-only", + "sociosRequired": false + }, + "bootReleaseSetRef": "urn:srcos:boot-release-set:m2-asahi-dev", + "releaseSetRef": "urn:srcos:release-set:m2-asahi-dev", + "workstationProfileRef": "urn:srcos:workstation-profile:workstation-v0", + "agentMachineProfileRef": "urn:srcos:agent-machine-profile:m2-asahi-dev", + "hostCapabilityPlacementRefs": [ + "urn:srcos:host-capability-placement:sourceos-supervisor" + ], + "nodeStateSchemaRefs": [ + "urn:srcos:node-state-schema:sourceos-evidence-root" + ], + "optionalSociosCapabilityPackRefs": [ + "urn:socios:capability-pack:automation-commons-opt-in" + ], + "validation": { + "planCommand": "sourceosctl immutable-node plan --profile m2-asahi-dev --dry-run", + "validateCommand": "sourceosctl immutable-node validate --profile m2-asahi-dev", + "inspectCommand": "sourceosctl immutable-node inspect --profile m2-asahi-dev", + "evidenceCommand": "agent-machine release evidence inspect --profile m2-asahi-dev" + }, + "policyRefs": [ + "urn:srcos:policy:immutable-node-base", + "urn:srcos:policy:socios-opt-in-required" + ], + "evidenceRefs": [ + "urn:srcos:release-receipt:m2-asahi-dev" + ], + "notes": "SourceOS base node profile. Socios pack reference is optional and does not imply enrollment." +} From 3b6088bf9738fe21e7c184bf0743c3c8c41e57a4 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Tue, 5 May 2026 02:29:32 -0400 Subject: [PATCH 7/8] feat(spec): add immutable node example validator --- tools/validate_immutable_node_examples.py | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tools/validate_immutable_node_examples.py diff --git a/tools/validate_immutable_node_examples.py b/tools/validate_immutable_node_examples.py new file mode 100644 index 0000000..ea37d87 --- /dev/null +++ b/tools/validate_immutable_node_examples.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Validate immutable-node contract examples against their schemas.""" + +from __future__ import annotations + +import json +from pathlib import Path + +import jsonschema + +ROOT = Path(__file__).resolve().parents[1] + +PAIRS = [ + ("schemas/HostCapabilityPlacement.json", "examples/hostcapabilityplacement.json"), + ("schemas/NodeStateSchema.json", "examples/nodestateschema.json"), + ("schemas/ImmutableNodeProfile.json", "examples/immutablenodeprofile.json"), +] + + +def load_json(path: str) -> object: + with (ROOT / path).open("r", encoding="utf-8") as fh: + return json.load(fh) + + +def main() -> None: + for schema_path, example_path in PAIRS: + schema = load_json(schema_path) + example = load_json(example_path) + jsonschema.Draft202012Validator.check_schema(schema) + jsonschema.validate(instance=example, schema=schema) + print(f"OK: {example_path} validates against {schema_path}") + + +if __name__ == "__main__": + main() From 2f23c4ebec01d3e50ec59f36cc8a31e3704c0531 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Tue, 5 May 2026 04:20:08 -0400 Subject: [PATCH 8/8] docs(spec): add immutable node contract addition note --- .../immutable-node-profiles.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 docs/contract-additions/immutable-node-profiles.md diff --git a/docs/contract-additions/immutable-node-profiles.md b/docs/contract-additions/immutable-node-profiles.md new file mode 100644 index 0000000..5f32e09 --- /dev/null +++ b/docs/contract-additions/immutable-node-profiles.md @@ -0,0 +1,61 @@ +# Immutable Node Profile Contracts + +This contract addition defines the first SourceOS machine-readable surface for immutable Linux node profiles. + +## Why this exists + +The platform standards layer already defines the general immutable host capability placement grammar. SourceOS needs a contract-native form that downstream implementation repos can validate, render, and emit evidence for. + +The key boundary is: + +- SourceOS immutable nodes are base substrate profiles. +- Socios capability packs are optional automation commons and must not be required for a base SourceOS node. + +## Added schemas + +| Schema | Purpose | URN prefix | +|---|---|---| +| `ImmutableNodeProfile.json` | Top-level immutable node profile tying together substrate posture, release refs, host placements, state schemas, validation commands, and optional Socios pack refs. | `urn:srcos:immutable-node-profile:` | +| `HostCapabilityPlacement.json` | Placement declaration for one host capability, service workload, extension, or state root. | `urn:srcos:host-capability-placement:` | +| `NodeStateSchema.json` | Durable state root contract with rollback compatibility and mutability posture. | `urn:srcos:node-state-schema:` | + +## Added examples + +| Example | Schema | +|---|---| +| `examples/immutablenodeprofile.json` | `schemas/ImmutableNodeProfile.json` | +| `examples/hostcapabilityplacement.json` | `schemas/HostCapabilityPlacement.json` | +| `examples/nodestateschema.json` | `schemas/NodeStateSchema.json` | + +## Validation + +The focused validator is: + +```bash +python3 tools/validate_immutable_node_examples.py +``` + +A future hygiene pass should wire this into the repository `Makefile` and `schemas/README.md` once the contract slice is accepted. + +## Downstream implementation map + +| Downstream repo | Expected role | +|---|---| +| `SourceOS-Linux/sourceos-boot` | Consumes boot/release references and coordinates boot/recovery/install/rollback handoff. | +| `SourceOS-Linux/agent-machine` | Renders and validates systemd, Quadlet, state roots, activation decisions, storage receipts, and runtime evidence. | +| `SourceOS-Linux/sourceos-devtools` | Exposes `sourceosctl immutable-node plan|validate|inspect` operator flows. | +| `SocioProphet/agentplane` | Consumes immutable-node activation/runtime evidence and replayable receipts. | +| `SocioProphet/prophet-platform` | Consumes profiles as deployment/FogStack substrate evidence. | +| `SocioProphet/sociosphere` | Registers topology and cross-repo governance edges. | +| `SociOS-Linux/socios` | Optional automation/personalization commons only after explicit enrollment. | + +## Non-goals + +This tranche does not: + +- implement boot execution; +- mutate host state; +- make Socios mandatory; +- replace BootReleaseSet; +- add a production node renderer; +- add CI or Makefile wiring yet.