diff --git a/docs/adr/0001-os-build-cybernetic-boundary.md b/docs/adr/0001-os-build-cybernetic-boundary.md new file mode 100644 index 0000000..664dc22 --- /dev/null +++ b/docs/adr/0001-os-build-cybernetic-boundary.md @@ -0,0 +1,58 @@ +# ADR-0001: OS build / cybernetic boundary contracts + +**Date:** 2026-04-14 +**Status:** Proposed + +--- + +## Context + +The typed contract canon currently models metadata, governance, execution, provenance, agent sessions, release receipts, and related domain objects, but it does not yet expose a first-class boundary between: + +1. immutable host image identity, +2. mutable install-time or enrollment-time node assignment, and +3. runtime cybernetic identity and control semantics. + +Without this separation, downstream repos risk mixing environment, topology, runtime service identity, and control-loop meaning into image identity or installer state. + +## Decision + +Introduce three additive schema types: + +- `OSImage` +- `NodeBinding` +- `CyberneticAssignment` + +These contracts formalize the plane boundary as follows: + +- `OSImage` owns immutable substrate identity, host ABI, boot/update posture, os-release identity, OCI metadata, and provenance references. +- `NodeBinding` owns install-time and enrollment-time mutable assignment such as topology, fleet, update ring, installer profile, registry mirrors, and bootstrap trust roots. +- `CyberneticAssignment` owns runtime service identity, deployment environment projection, policy refs, graph relations, and control objectives. + +## Alternatives considered + +| Alternative | Reason not chosen | +|-------------|------------------| +| Encode environment and runtime role directly in image naming | Pollutes immutable image identity and makes shared-image reuse brittle. | +| Put binding and cybernetic semantics into installer-only contracts | Loses a shared cross-repo canonical object model and makes runtime enforcement harder. | +| Keep the seam purely documentary with no schema types | Fails to make the boundary machine-checkable for downstream validation and release gating. | + +## Consequences + +Positive: + +- gives downstream build, policy, runtime, and deployment repos a single canonical seam; +- enables validation rules that reject cybernetic leakage into immutable image metadata; +- supports additive rollout through existing SemVer and ADR discipline. + +Negative: + +- requires follow-on updates to schema catalog documentation, OpenAPI/AsyncAPI surfaces, semantic overlays, and changelog entries; +- introduces new URN namespaces that must be carried consistently in examples and downstream imports. + +## References + +- `docs/specs/canon.os-build.v1.md` +- `schemas/OSImage.json` +- `schemas/NodeBinding.json` +- `schemas/CyberneticAssignment.json` diff --git a/docs/specs/canon.os-build.v1.md b/docs/specs/canon.os-build.v1.md new file mode 100644 index 0000000..921d75c --- /dev/null +++ b/docs/specs/canon.os-build.v1.md @@ -0,0 +1,175 @@ +# canon.os-build.v1 + +**Status:** Draft +**Intended repository:** `SourceOS-Linux/sourceos-spec` +**Suggested first release:** `v2.1.0` + +## Context + +SourceOS and SociOS need a normative contract boundary between immutable host-image identity, mutable install-time binding, and runtime cybernetic meaning. Existing platform discussions and downstream repos already distinguish build substrate, install/enrollment flow, and runtime policy/evidence lanes, but the typed contract canon does not yet expose first-class objects for these layers. + +## Decision + +This specification defines three first-class contract objects: + +- `OSImage` — immutable substrate identity and host contract +- `NodeBinding` — mutable install-time and enrollment-time node assignment +- `CyberneticAssignment` — runtime identity, policy, telemetry, relations, and control semantics + +The governing rule is: + +- a field belongs in `OSImage` if it changes boot, measured identity, host ABI, update behavior, extension compatibility, or image provenance; +- a field belongs in `NodeBinding` if two nodes can boot the same image but must receive different install-time or enrollment-time values; +- a field belongs in `CyberneticAssignment` if it changes runtime service identity, deployment environment projection, policy, relations, objectives, or control-loop behavior. + +## Naming rules + +### Immutable short IDs + +`OSImage.shortId` uses the pattern: + +`so-` + +Examples: + +- `so1-edge-appliance` +- `so1-workstation` +- `so1-vm-base` +- `so1-installer` + +### Artifact projection + +Rendered artifact names may append artifact and architecture: + +`--` + +Examples: + +- `so1-edge-appliance-oci-aarch64` +- `so1-vm-base-qcow2-x86_64` +- `so1-installer-iso-x86_64` + +### Forbidden in immutable short IDs + +Do not encode: + +- environment (`dev`, `stage`, `prod`) +- topology (`na-use1`, `eu-de1`) +- customer or site names +- cybernetic role words (`sensor`, `planner`, `governor`) +- runtime service names + +## Required object shape + +### `OSImage` + +Must contain: + +- URN identity +- `shortId` +- `family`, `epoch`, `hostProfile`, `artifact`, `architecture` +- `osRelease` with at least `ID`, `VERSION_ID`, `IMAGE_ID`, `IMAGE_VERSION` +- OCI annotations +- substrate capability list +- provenance references + +### `NodeBinding` + +Must contain: + +- URN identity +- `imageRef` +- `nodeProfile` +- topology +- fleet +- update ring + +May contain: + +- registry mirrors +- hostname template +- installer profile +- bootstrap trust roots +- install-time target image reference + +### `CyberneticAssignment` + +Must contain: + +- URN identity +- `nodeRef` +- runtime service identity +- deployment environment name +- policy refs +- graph relations + +May contain: + +- principal reference +- control profile reference +- objective set + +## Allowlist and denylist + +### `OSImage` allowlist categories + +- distro lineage and ABI epoch +- boot and update contract +- secure boot / TPM / measured boot posture +- generic hardware and virtualization enablement +- `os-release` identity +- OCI image metadata +- build provenance and attestations +- substrate agents tightly coupled to the host + +### `OSImage` denylist categories + +- runtime service identity +- deployment environment name +- runtime graph relations +- control objectives +- workload selection intent +- live enrollment tokens +- long-lived secrets + +## Versioning + +This addition is additive if introduced as new schema types and optional API/event surfaces. Recommended versioning policy: + +- patch — clarifications, descriptions, examples +- minor — additive fields and additive schema types +- major — boundary changes or incompatible rename/removal + +## Reference example + +```json +{ + "id": "urn:srcos:osimage:so1-edge-appliance", + "type": "OSImage", + "specVersion": "2.1.0", + "shortId": "so1-edge-appliance", + "family": "sourceos", + "epoch": 1, + "hostProfile": "edge-appliance", + "artifact": "oci", + "architecture": "aarch64", + "osRelease": { + "ID": "sourceos", + "ID_LIKE": ["fedora"], + "VERSION_ID": "1", + "IMAGE_ID": "so1-edge-appliance", + "IMAGE_VERSION": "2026.04.14.1" + }, + "ociAnnotations": { + "org.opencontainers.image.version": "2026.04.14.1", + "org.opencontainers.image.revision": "fcea8a4bf4cb4f06b63e090fd9cf89fcdb24194c", + "org.opencontainers.image.source": "https://github.com/SourceOS-Linux/sourceos-spec", + "com.socioprophet.os.channel": "stable" + }, + "substrateCapabilities": ["bootc", "secureboot", "tpm2", "measured-boot"], + "provenance": { + "statementRef": "urn:srcos:attestation:osimage:so1-edge-appliance:2026.04.14.1", + "slsaPredicateRef": "urn:srcos:slsa:osimage:so1-edge-appliance:2026.04.14.1" + } +} +``` diff --git a/examples/cyberneticassignment.json b/examples/cyberneticassignment.json new file mode 100644 index 0000000..2505645 --- /dev/null +++ b/examples/cyberneticassignment.json @@ -0,0 +1,37 @@ +{ + "id": "urn:srcos:cyberassign:edge-gateway-na-use1-001", + "type": "CyberneticAssignment", + "specVersion": "2.1.0", + "nodeRef": "urn:srcos:node:edge-gateway-na-use1-001", + "principalRef": "urn:srcos:principal:edge-gateway-na-use1-001", + "controlProfileRef": "urn:srcos:control-profile:regional-sensor-hub", + "serviceIdentity": { + "namespace": "socioprophet.edge", + "name": "gateway-control", + "instanceId": "edge-gateway-na-use1-001" + }, + "deploymentEnvironmentName": "production", + "policyRefs": [ + "urn:srcos:policy:edge-guardrails-v1" + ], + "relations": { + "reports_to": [ + "urn:srcos:council:regional-ops" + ], + "observes": [ + "urn:srcos:signal:market-edge-demand" + ], + "acts_on": [ + "urn:srcos:workflow:route-shift" + ], + "governed_by": [ + "urn:srcos:policy:edge-guardrails-v1" + ] + }, + "objectives": [ + { + "name": "routing-latency-p95", + "target": "<=200ms" + } + ] +} diff --git a/examples/nodebinding.json b/examples/nodebinding.json new file mode 100644 index 0000000..0b7851e --- /dev/null +++ b/examples/nodebinding.json @@ -0,0 +1,19 @@ +{ + "id": "urn:srcos:nodebinding:edge-gateway-na-use1-001", + "type": "NodeBinding", + "specVersion": "2.1.0", + "imageRef": "urn:srcos:osimage:so1-edge-appliance", + "nodeProfile": "field-gateway", + "topology": "na-use1", + "fleet": "edge-prod", + "updateRing": "stable", + "targetImgref": "quay.io/sourceos/edge-appliance:2026.04.14.1", + "hostnameTemplate": "edge-gateway-%03d", + "registries": [ + "registry-use1.internal" + ], + "bootstrapTrustRootRefs": [ + "urn:srcos:trustroot:global-platform-root" + ], + "installerProfile": "edge-default" +} diff --git a/examples/osimage.json b/examples/osimage.json new file mode 100644 index 0000000..ef5d3c2 --- /dev/null +++ b/examples/osimage.json @@ -0,0 +1,42 @@ +{ + "id": "urn:srcos:osimage:so1-edge-appliance", + "type": "OSImage", + "specVersion": "2.1.0", + "shortId": "so1-edge-appliance", + "family": "sourceos", + "epoch": 1, + "hostProfile": "edge-appliance", + "artifact": "oci", + "architecture": "aarch64", + "osRelease": { + "ID": "sourceos", + "ID_LIKE": [ + "fedora" + ], + "VERSION_ID": "1", + "IMAGE_ID": "so1-edge-appliance", + "IMAGE_VERSION": "2026.04.14.1" + }, + "ociAnnotations": { + "org.opencontainers.image.version": "2026.04.14.1", + "org.opencontainers.image.revision": "fcea8a4bf4cb4f06b63e090fd9cf89fcdb24194c", + "org.opencontainers.image.source": "https://github.com/SourceOS-Linux/sourceos-spec", + "com.socioprophet.os.channel": "stable", + "com.socioprophet.os.capabilities": [ + "bootc", + "secureboot", + "tpm2" + ] + }, + "substrateCapabilities": [ + "bootc", + "secureboot", + "tpm2", + "measured-boot" + ], + "provenance": { + "statementRef": "urn:srcos:attestation:osimage:so1-edge-appliance:2026.04.14.1", + "slsaPredicateRef": "urn:srcos:slsa:osimage:so1-edge-appliance:2026.04.14.1", + "sbomRef": "urn:srcos:sbom:osimage:so1-edge-appliance:2026.04.14.1" + } +} diff --git a/schemas/CyberneticAssignment.json b/schemas/CyberneticAssignment.json new file mode 100644 index 0000000..5094f85 --- /dev/null +++ b/schemas/CyberneticAssignment.json @@ -0,0 +1,141 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/CyberneticAssignment.json", + "title": "CyberneticAssignment", + "description": "Runtime identity, policy, telemetry, relation, and control semantics assigned after image selection and node binding.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "nodeRef", + "serviceIdentity", + "deploymentEnvironmentName", + "policyRefs", + "relations" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:cyberassign:[a-z0-9][a-z0-9._-]*$", + "description": "Stable URN identifier. Pattern: urn:srcos:cyberassign:." + }, + "type": { + "const": "CyberneticAssignment", + "description": "Discriminator constant — always \"CyberneticAssignment\"." + }, + "specVersion": { + "type": "string", + "description": "Spec version of this document, e.g. \"2.1.0\"." + }, + "nodeRef": { + "type": "string", + "description": "URN reference to the node receiving this runtime assignment." + }, + "principalRef": { + "type": "string", + "description": "Optional URN reference to the active runtime principal or DID-backed identity." + }, + "controlProfileRef": { + "type": "string", + "description": "Optional URN reference to the higher-order control profile applied to the node." + }, + "serviceIdentity": { + "type": "object", + "description": "OpenTelemetry-style service identity assigned at runtime.", + "additionalProperties": false, + "required": [ + "namespace", + "name" + ], + "properties": { + "namespace": { + "type": "string", + "description": "Logical service namespace." + }, + "name": { + "type": "string", + "description": "Logical service name." + }, + "instanceId": { + "type": "string", + "description": "Optional runtime instance identifier." + } + } + }, + "deploymentEnvironmentName": { + "type": "string", + "description": "Deployment environment name for runtime identity projection, such as production or staging." + }, + "policyRefs": { + "type": "array", + "items": { + "type": "string", + "description": "Policy reference string." + }, + "description": "Policy references constraining runtime behavior and authorization." + }, + "relations": { + "type": "object", + "description": "Graph edges binding the node into the runtime control system.", + "additionalProperties": true, + "properties": { + "reports_to": { + "type": "array", + "items": { + "type": "string", + "description": "Relation target reference." + }, + "description": "Targets to which this node or runtime reports." + }, + "observes": { + "type": "array", + "items": { + "type": "string", + "description": "Relation target reference." + }, + "description": "Signals or entities observed by this runtime." + }, + "acts_on": { + "type": "array", + "items": { + "type": "string", + "description": "Relation target reference." + }, + "description": "Targets or workflows acted on by this runtime." + }, + "governed_by": { + "type": "array", + "items": { + "type": "string", + "description": "Relation target reference." + }, + "description": "Governance or policy targets controlling this runtime." + } + } + }, + "objectives": { + "type": "array", + "description": "Optional objective set for the runtime control loop.", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "target" + ], + "properties": { + "name": { + "type": "string", + "description": "Objective name." + }, + "target": { + "type": "string", + "description": "Human-readable target expression for the objective." + } + } + } + } + } +} diff --git a/schemas/NodeBinding.json b/schemas/NodeBinding.json new file mode 100644 index 0000000..232fe4d --- /dev/null +++ b/schemas/NodeBinding.json @@ -0,0 +1,92 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/NodeBinding.json", + "title": "NodeBinding", + "description": "Install-time or enrollment-time mutable assignment for a node booting a shared OS image.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "imageRef", + "nodeProfile", + "topology", + "fleet", + "updateRing" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:nodebinding:[a-z0-9][a-z0-9._-]*$", + "description": "Stable URN identifier. Pattern: urn:srcos:nodebinding:." + }, + "type": { + "const": "NodeBinding", + "description": "Discriminator constant — always \"NodeBinding\"." + }, + "specVersion": { + "type": "string", + "description": "Spec version of this document, e.g. \"2.1.0\"." + }, + "imageRef": { + "type": "string", + "pattern": "^urn:srcos:osimage:[a-z0-9][a-z0-9._-]*$", + "description": "URN reference to the immutable OSImage applied to the node." + }, + "nodeProfile": { + "type": "string", + "description": "Install-time node persona or profile selected for this node." + }, + "topology": { + "type": "string", + "description": "Topology identifier such as a region, site, or cell code." + }, + "fleet": { + "type": "string", + "description": "Fleet or rollout group name to which the node belongs." + }, + "updateRing": { + "type": "string", + "enum": [ + "canary", + "preview", + "stable", + "lts" + ], + "description": "Update ring used for image selection and promotion." + }, + "targetImgref": { + "type": "string", + "description": "Optional explicit runtime image reference overriding the source install image reference." + }, + "hostnameTemplate": { + "type": "string", + "description": "Optional hostname template used during install or first boot." + }, + "registries": { + "type": "array", + "items": { + "type": "string", + "description": "Registry or mirror endpoint string." + }, + "description": "Optional list of registry mirrors or pull sources." + }, + "bootstrapTrustRootRefs": { + "type": "array", + "items": { + "type": "string", + "description": "Trust root reference string." + }, + "description": "Optional list of trust-root references used during bootstrap or enrollment." + }, + "installerProfile": { + "type": "string", + "description": "Optional installer profile or preset selected by the operator." + }, + "notes": { + "type": "string", + "description": "Free-form operational notes for the binding." + } + } +} diff --git a/schemas/OSImage.json b/schemas/OSImage.json new file mode 100644 index 0000000..fdbdc50 --- /dev/null +++ b/schemas/OSImage.json @@ -0,0 +1,207 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/OSImage.json", + "title": "OSImage", + "description": "Immutable host image identity and substrate contract for a SourceOS operating-system artifact.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "shortId", + "family", + "epoch", + "hostProfile", + "artifact", + "architecture", + "osRelease", + "ociAnnotations", + "substrateCapabilities", + "provenance" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:osimage:[a-z0-9][a-z0-9._-]*$", + "description": "Stable URN identifier. Pattern: urn:srcos:osimage:." + }, + "type": { + "const": "OSImage", + "description": "Discriminator constant — always \"OSImage\"." + }, + "specVersion": { + "type": "string", + "description": "Spec version of this document, e.g. \"2.1.0\"." + }, + "shortId": { + "type": "string", + "pattern": "^so[0-9]+-[a-z0-9]+(?:-[a-z0-9]+)*$", + "description": "Immutable short substrate identifier. Pattern: so-." + }, + "family": { + "const": "sourceos", + "description": "OS family constant for SourceOS image contracts." + }, + "epoch": { + "type": "integer", + "minimum": 1, + "description": "Compatibility epoch for the host image contract." + }, + "hostProfile": { + "type": "string", + "enum": [ + "workstation", + "edge-appliance", + "vm-base", + "installer", + "recovery", + "builder" + ], + "description": "Coarse substrate persona for the image. This is not a cybernetic runtime role." + }, + "artifact": { + "type": "string", + "enum": [ + "oci", + "qcow2", + "raw", + "iso", + "ami", + "vmdk" + ], + "description": "Rendered artifact form for the image." + }, + "architecture": { + "type": "string", + "enum": [ + "x86_64", + "aarch64" + ], + "description": "Target machine architecture for the rendered image." + }, + "osRelease": { + "type": "object", + "description": "Rendered os-release identity fields exported by the image.", + "additionalProperties": false, + "required": [ + "ID", + "VERSION_ID", + "IMAGE_ID", + "IMAGE_VERSION" + ], + "properties": { + "ID": { + "type": "string", + "description": "Primary os-release ID value." + }, + "ID_LIKE": { + "type": "array", + "items": { + "type": "string", + "description": "Related upstream distribution identifier." + }, + "description": "Optional list of upstream-compatible distribution identifiers." + }, + "VERSION_ID": { + "type": "string", + "description": "Primary OS version identifier." + }, + "BUILD_ID": { + "type": "string", + "description": "Optional build identifier for the original installation image." + }, + "IMAGE_ID": { + "type": "string", + "description": "Image-family identifier exported through os-release." + }, + "IMAGE_VERSION": { + "type": "string", + "description": "Image version exported through os-release." + }, + "RELEASE_TYPE": { + "type": "string", + "description": "Optional release-type hint such as stable or preview." + } + } + }, + "ociAnnotations": { + "type": "object", + "description": "OCI metadata carried with the bootable image artifact.", + "additionalProperties": true, + "required": [ + "org.opencontainers.image.version", + "org.opencontainers.image.revision", + "org.opencontainers.image.source" + ], + "properties": { + "org.opencontainers.image.version": { + "type": "string", + "description": "OCI version annotation for the built image." + }, + "org.opencontainers.image.revision": { + "type": "string", + "description": "OCI revision annotation referencing the source commit." + }, + "org.opencontainers.image.source": { + "type": "string", + "description": "OCI source annotation referencing the canonical source repository." + }, + "org.opencontainers.image.created": { + "type": "string", + "description": "Optional OCI creation timestamp." + }, + "com.socioprophet.os.channel": { + "type": "string", + "description": "SocioProphet-specific update channel label for the image." + }, + "com.socioprophet.os.capabilities": { + "type": "array", + "items": { + "type": "string", + "description": "Declared substrate capability string." + }, + "description": "Optional SocioProphet-specific substrate capability list." + } + } + }, + "substrateCapabilities": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "description": "Declared substrate capability string." + }, + "description": "Host-level capabilities that affect boot, attestation, extension compatibility, or updates." + }, + "provenance": { + "type": "object", + "description": "Attestation and provenance references for the image artifact.", + "additionalProperties": false, + "required": [ + "statementRef", + "slsaPredicateRef" + ], + "properties": { + "statementRef": { + "type": "string", + "pattern": "^urn:srcos:attestation:[a-z0-9][a-z0-9._:-]*$", + "description": "URN reference to the in-toto or equivalent attestation statement." + }, + "slsaPredicateRef": { + "type": "string", + "pattern": "^urn:srcos:slsa:[a-z0-9][a-z0-9._:-]*$", + "description": "URN reference to the SLSA provenance predicate artifact." + }, + "sbomRef": { + "type": "string", + "description": "Optional URN or URL reference to the SBOM for this image." + }, + "signatureRef": { + "type": "string", + "description": "Optional URN or URL reference to the signature verification material." + } + } + } + } +}