Skip to content
Open
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
65 changes: 65 additions & 0 deletions docs/contract-additions/immutable-node-profiles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Immutable Node Profile Contracts

This contract addition defines the first SourceOS machine-readable surface for immutable Linux node and agent-runtime substrate profiles.

## Why this exists

The platform standards layer 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.
- Agent Machine and AgentPlane are the primary runtime/evidence consumers for node and agent-runtime substrate posture.
- Desktop, shell, and workstation surfaces may display summarized posture and expose operator controls, but they do not own the node substrate.
- 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 node/agent-runtime substrate profile tying together substrate posture, release refs, host placements, state schemas, validation commands, desktop consumer refs, and optional Socios pack refs. | `urn:srcos:immutable-node-profile:` |
| `HostCapabilityPlacement.json` | Placement declaration for one node substrate capability, agent runtime workload, host extension, or state root. | `urn:srcos:host-capability-placement:` |
| `NodeStateSchema.json` | Durable state root contract with rollback compatibility, mutability posture, and desktop visibility metadata. | `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. |
| `SourceOS-Linux/sourceos-shell` | Desktop/shell consumer of summarized node posture only. |
| `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;
- make desktop or shell surfaces the owner of node substrate contracts;
- replace BootReleaseSet;
- add a production node renderer;
- add CI or Makefile wiring yet.
21 changes: 21 additions & 0 deletions examples/hostcapabilityplacement.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"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",
"primaryPlane": "node-substrate",
"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 node substrate capability. This is not a desktop-owned feature and not a Socios enrollment dependency."
}
45 changes: 45 additions & 0 deletions examples/immutablenodeprofile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"id": "urn:srcos:immutable-node-profile:m2-asahi-agent-node-dev",
"type": "ImmutableNodeProfile",
"specVersion": "2.2.0",
"name": "M2 Asahi SourceOS agent node dev profile",
"channel": "m2-asahi-dev",
"primaryPlane": "agent-runtime-substrate",
"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",
"agentMachineProfileRef": "urn:srcos:agent-machine-profile:m2-asahi-dev",
"agentPlaneRuntimeRef": "urn:srcos:agentplane-runtime:m2-asahi-dev",
"workstationProfileRef": "urn:srcos:workstation-profile:workstation-v0",
"desktopConsumerRefs": [
"urn:srcos:desktop-consumer:sourceos-settings-node-status",
"urn:srcos:desktop-consumer:sourceos-shell-node-posture"
],
"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-agent-node-dev --dry-run",
"validateCommand": "sourceosctl immutable-node validate --profile m2-asahi-agent-node-dev",
"inspectCommand": "sourceosctl immutable-node inspect --profile m2-asahi-agent-node-dev",
"evidenceCommand": "agent-machine release evidence inspect --profile m2-asahi-agent-node-dev"
},
"policyRefs": [
"urn:srcos:policy:immutable-node-base",
"urn:srcos:policy:socios-opt-in-required"
],
"evidenceRefs": [
"urn:srcos:release-receipt:m2-asahi-dev"
],
"notes": "Node/agent-runtime substrate profile. Workstation and shell references are consumers only. Optional Socios pack reference does not imply enrollment."
}
16 changes: 16 additions & 0 deletions examples/nodestateschema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"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",
"primaryPlane": "execution-evidence",
"schemaRef": "urn:srcos:release-receipt:sourceos-evidence",
"rollbackCompatibility": "required-n-and-n-minus-1",
"mutability": "append-only",
"owner": "agent-machine",
"evidenceRequired": true,
"desktopVisible": true,
"notes": "Durable evidence root survives host rollback. Desktop surfaces may display summarized posture, but Agent Machine and evidence consumers own the state contract."
}
37 changes: 37 additions & 0 deletions schemas/HostCapabilityPlacement.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$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 node substrate capability, agent runtime workload, host extension, or durable 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:<local-id>." },
"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 immutable-node placement class."
},
"authority": {
"type": "string",
"enum": ["sourceos-base", "sourceos-boot", "agent-machine", "sourceos-devtools", "agentplane", "prophet-platform", "sourceos-desktop-consumer", "socios-optional-pack"],
"description": "Repo or plane class that owns the implementation boundary for this capability."
},
"primaryPlane": {
"type": "string",
"enum": ["node-substrate", "agent-runtime-substrate", "boot-recovery", "operator-tooling", "execution-evidence", "platform-runtime", "desktop-consumer", "optional-commons"],
"description": "Primary architectural plane for this capability. Desktop should normally be consumer, not owner."
},
"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." }
}
}
50 changes: 50 additions & 0 deletions schemas/ImmutableNodeProfile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"$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 for node and agent-runtime substrate contracts. Desktop and workstation surfaces may consume this profile, but they do not own the node substrate.",
"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:<local-id>." },
"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." },
"primaryPlane": { "type": "string", "enum": ["node-substrate", "agent-runtime-substrate"], "description": "Primary plane for this profile. ImmutableNodeProfile is not a desktop-owned contract." },
"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." },
"agentMachineProfileRef": { "type": ["string", "null"], "description": "Optional Agent Machine or AgentPod profile reference for local runtime realization." },
"agentPlaneRuntimeRef": { "type": ["string", "null"], "description": "Optional AgentPlane runtime/evidence contract reference that consumes node activation or runtime receipts." },
"workstationProfileRef": { "type": ["string", "null"], "description": "Optional WorkstationProfile reference for desktop/workstation consumption. This must not make the desktop the owner of the node substrate." },
"desktopConsumerRefs": { "type": "array", "items": { "type": "string" }, "description": "Optional desktop, shell, settings, or workstation surfaces that may display summarized node posture." },
"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. These are usually implemented by sourceosctl and Agent Machine, not desktop code."
},
"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." }
}
}
25 changes: 25 additions & 0 deletions schemas/NodeStateSchema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$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 node substrate and agent runtime 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:<local-id>." },
"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." },
"primaryPlane": { "type": "string", "enum": ["node-substrate", "agent-runtime-substrate", "execution-evidence", "platform-runtime", "desktop-consumer", "optional-commons"], "description": "Primary architectural plane for the state root. Desktop surfaces should usually consume state summaries, not own durable node state." },
"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." },
"desktopVisible": { "type": "boolean", "default": false, "description": "Whether desktop/workstation surfaces may display this state root's summarized posture." },
"notes": { "type": ["string", "null"], "description": "Human-readable review notes." }
}
}
35 changes: 35 additions & 0 deletions tools/validate_immutable_node_examples.py
Original file line number Diff line number Diff line change
@@ -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()