{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://railiance.dev/schemas/release-unit.schema.json", "title": "Railiance release-unit declaration", "description": "Normative shape for registry/release-units/*.yaml, authored by railiance-master under ADR-0011 as an amendment to ADR-0007. Evolves rapp.schema.json. This schema is normative; ADR-0011 cites it and does not restate its fields. release-registry validates against this file and does not carry a copy.", "type": "object", "additionalProperties": false, "required": [ "kind", "unit_id", "owner", "contract_version", "data_classification", "criticality", "composition", "rollout_contract", "smoke_contract", "rollback_contract", "workloads", "pins" ], "properties": { "kind": { "const": "release-unit" }, "unit_id": { "$ref": "#/$defs/unitId", "description": "Canonical fate-group identifier. The coverage claim and the railiance.io/release-unit label both use this value." }, "owner": { "$ref": "#/$defs/repoSlug", "description": "The single repository accountable for rollout. A repository slug, not a catalog group." }, "contract_version": { "$ref": "#/$defs/semver" }, "data_classification": { "enum": ["public", "internal", "confidential", "restricted"] }, "criticality": { "enum": ["low", "medium", "high", "critical"] }, "composition": { "$ref": "#/$defs/composition" }, "rollout_contract": { "$ref": "#/$defs/rolloutContract" }, "smoke_contract": { "$ref": "#/$defs/smokeContract" }, "rollback_contract": { "$ref": "#/$defs/rollbackContract" }, "exposure": { "$ref": "#/$defs/exposure" }, "workloads": { "type": "array", "minItems": 1, "description": "Coverage claims. Each entry names one top-level workload by kind, namespace and name. The claim is the coverage authority.", "items": { "$ref": "#/$defs/workloadClaim" } }, "part_of": { "$ref": "#/$defs/unitId", "description": "Optional containment. The named unit is an umbrella. Rollback fate stays with this unit." }, "depends_on": { "type": "array", "uniqueItems": true, "description": "Ordering and compatibility between independently rolled units. Data only; this schema grants no deploy right.", "items": { "$ref": "#/$defs/dependency" } }, "pins": { "$ref": "#/$defs/pins" } }, "allOf": [ { "description": "public exposure requires a grant object, as in rapp.schema.json.", "if": { "required": ["exposure"], "properties": { "exposure": { "required": ["posture"], "properties": { "posture": { "const": "public" } } } } }, "then": { "properties": { "exposure": { "required": ["grant"] } } } } ], "$defs": { "repoSlug": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$", "minLength": 2 }, "unitId": { "type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$", "minLength": 2 }, "semver": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$" }, "notes": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "dnsLabel": { "type": "string", "pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$", "maxLength": 63 }, "composition": { "type": "object", "additionalProperties": false, "required": ["purpose", "member_repos"], "description": "Same composition block as rapp.schema.json. member_repos stay many-to-many across units. Workload claims stay one-to-one.", "properties": { "purpose": { "type": "string", "minLength": 12 }, "member_repos": { "type": "array", "minItems": 1, "items": { "type": "object", "additionalProperties": false, "required": ["repo", "role", "deployables"], "properties": { "repo": { "$ref": "#/$defs/repoSlug" }, "role": { "type": "string", "minLength": 3 }, "deployables": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "minLength": 1 } } } } }, "upstream_components": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["name", "source", "version"], "properties": { "name": { "type": "string", "minLength": 1 }, "source": { "type": "string", "minLength": 1 }, "version": { "type": "string", "minLength": 1 } } } } } }, "rolloutContract": { "type": "object", "additionalProperties": false, "required": ["default_mode"], "properties": { "default_mode": { "type": "string", "minLength": 3 }, "commands": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "production_gate": { "type": "string" }, "notes": { "$ref": "#/$defs/notes" } } }, "smokeContract": { "type": "object", "additionalProperties": false, "required": ["required"], "properties": { "required": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "minLength": 1 } }, "commands": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "notes": { "$ref": "#/$defs/notes" } } }, "rollbackContract": { "type": "object", "additionalProperties": false, "required": ["order"], "properties": { "order": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }, "commands": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "fallback": { "type": "string" }, "notes": { "$ref": "#/$defs/notes" } } }, "exposure": { "type": "object", "additionalProperties": false, "required": ["posture"], "description": "Who may reach the listener. Omitted means private (ADR-0008). Distinct from data_classification.", "properties": { "posture": { "enum": ["private", "operator", "public"] }, "grant": { "$ref": "#/$defs/exposureGrant" }, "tunnel": { "$ref": "#/$defs/exposureTunnel" }, "binding_admission": { "enum": ["declared", "installed", "verified", "production-approved", "deprecated"] } } }, "exposureGrant": { "type": "object", "additionalProperties": false, "required": ["reason", "approved_on", "residual_risk_owner"], "anyOf": [ { "required": ["hostname"] }, { "required": ["port"] } ], "properties": { "hostname": { "type": "string", "minLength": 1 }, "port": { "type": "integer", "minimum": 1, "maximum": 65535 }, "reason": { "type": "string", "minLength": 1 }, "approved_on": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" }, "residual_risk_owner": { "type": "string", "minLength": 1 } } }, "exposureTunnel": { "type": "object", "additionalProperties": false, "required": ["name"], "properties": { "name": { "type": "string", "minLength": 1 }, "local_port": { "type": "integer", "minimum": 1, "maximum": 65535 } } }, "workloadClaim": { "type": "object", "additionalProperties": false, "required": ["kind", "namespace", "name"], "properties": { "kind": { "enum": ["Deployment", "StatefulSet", "DaemonSet", "CronJob", "Job"] }, "namespace": { "$ref": "#/$defs/dnsLabel" }, "name": { "$ref": "#/$defs/dnsLabel" } } }, "dependency": { "type": "object", "additionalProperties": false, "required": ["unit_id"], "properties": { "unit_id": { "$ref": "#/$defs/unitId" }, "readiness": { "type": "string", "minLength": 1, "description": "Readiness the depended-on unit must show before this unit rolls. Recorded as data. No controller is implied." }, "version": { "type": "string", "minLength": 1, "description": "Compatibility constraint on the depended-on unit." } } }, "pins": { "type": "object", "additionalProperties": false, "required": ["resources", "sources"], "description": "Specification-level subset of an OCM component descriptor. No OCM library is required to read it.", "properties": { "component": { "type": "object", "additionalProperties": false, "required": ["name", "version"], "properties": { "name": { "type": "string", "minLength": 1 }, "version": { "type": "string", "minLength": 1 } } }, "resources": { "type": "array", "items": { "$ref": "#/$defs/resource" } }, "sources": { "type": "array", "items": { "$ref": "#/$defs/source" } }, "references": { "type": "array", "description": "OCM component references. Operational containment and ordering are part_of and depends_on.", "items": { "$ref": "#/$defs/reference" } } } }, "digest": { "type": "object", "additionalProperties": false, "required": ["hashAlgorithm", "normalisationAlgorithm", "value"], "properties": { "hashAlgorithm": { "type": "string", "minLength": 1 }, "normalisationAlgorithm": { "type": "string", "minLength": 1 }, "value": { "type": "string", "minLength": 1, "description": "Digest string as the live object shows it. An image digest keeps the sha256: prefix." } } }, "resource": { "type": "object", "additionalProperties": false, "required": ["name", "type", "version"], "properties": { "name": { "type": "string", "minLength": 1 }, "type": { "type": "string", "minLength": 1, "description": "OCM resource type. ociImage and helmChart are the types the drift check reads." }, "version": { "type": "string", "minLength": 1 }, "relation": { "enum": ["local", "external"] }, "digest": { "$ref": "#/$defs/digest" }, "releaseRevision": { "type": "string", "minLength": 1, "description": "Helm release revision. Compared with the revision annotation on the workload object. Absent from the object, the revision is unobservable. It is not read from a Helm release Secret." } }, "allOf": [ { "if": { "properties": { "type": { "const": "ociImage" } }, "required": ["type"] }, "then": { "required": ["digest"] } }, { "if": { "properties": { "type": { "const": "helmChart" } }, "required": ["type"] }, "then": { "required": ["releaseRevision"] } } ] }, "source": { "type": "object", "additionalProperties": false, "required": ["name", "type", "version", "digest"], "properties": { "name": { "type": "string", "minLength": 1 }, "type": { "type": "string", "minLength": 1 }, "version": { "type": "string", "minLength": 1 }, "digest": { "$ref": "#/$defs/digest" } } }, "reference": { "type": "object", "additionalProperties": false, "required": ["name", "componentName", "version"], "properties": { "name": { "type": "string", "minLength": 1 }, "componentName": { "type": "string", "minLength": 1 }, "version": { "type": "string", "minLength": 1 } } } } }