Implement offline catalog lane scaffold generation
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a10391-6355-7b41-b0fa-3e14e874f091
This commit is contained in:
parent
6b9b95bc6e
commit
f5b7ff5118
9 changed files with 869 additions and 1 deletions
63
docs/catalog-lane-scaffold.md
Normal file
63
docs/catalog-lane-scaffold.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Offline exact KV-v2 workload-read lane scaffold
|
||||
|
||||
Owner: railiance-platform, RPF-WP-0050-T04. Warden pointer seam:
|
||||
`ops-warden/wiki/CatalogLaneScaffoldContract.md`, WARDEN-WP-0041.
|
||||
|
||||
Run `python3 scripts/credential-change.py scaffold-lane --input <platform-request.yaml> --catalog-manifest <warden-request.yaml> --output-dir <new-external-staging-dir>`.
|
||||
The catalog manifest is optional for ordinary local preparation and required for
|
||||
cross-owner fixture acceptance. Both inputs are metadata only; this command has
|
||||
no execute, credential, network, approval or apply option. jsonschema and PyYAML
|
||||
are required, as used by the local validation environment.
|
||||
|
||||
`schemas/catalog-lane-scaffold.schema.json` is the closed platform request v1.
|
||||
Explicit owner/consumer/lane, prospective Kubernetes SA/namespace/role/audience,
|
||||
exact KV mount/path/field names, namespace-restricted ESO destination, risk for the
|
||||
entire readable path and required review roles must be supplied. Missing claims
|
||||
and risk are not inferred. The first shape supports one prospective ESO reader,
|
||||
audience openbao and a maximum fifteen-minute token TTL. Tenant/dynamic secret,
|
||||
OIDC/ceremony, wildcard, metadata listing, self-lifecycle and executable fetching
|
||||
need separate reviewed extensions. No credential values or protected URLs belong
|
||||
in this input. Unknown fields, malformed/duplicate YAML keys, control characters,
|
||||
secret markers, invalid calendar dates and mismatched mappings are refused with
|
||||
safe errors. Warden's unknown workload identity remains unknown and does not
|
||||
confirm the prospective platform auth binding.
|
||||
|
||||
The generator reuses `credential-change.py`'s `generated_policy_hcl` and
|
||||
`auth_payload`, preserving one exact data-read path and explicit policies/claims.
|
||||
It emits six draft review artifacts: a CCR under `credential-change-requests/`,
|
||||
its policy under `openbao/policies/`, auth payload under `openbao/auth/`, ESO review
|
||||
manifest under `manifests/`, a non-executed evidence checklist under
|
||||
`docs/evidence/`, and README. The policy path in the CCR is the eventual owner
|
||||
repository destination, relative to the staged bundle. Auth payloads are wrapped
|
||||
in draft metadata; no command executes them. No Secret object or value is emitted.
|
||||
|
||||
CCR status is proposed, review comments empty, bound claims unconfirmed and
|
||||
frontdoor pending-review/unresolvable. Store/ExternalSecret annotations also say
|
||||
pending-review. The ExternalSecret target has explicit annotation metadata to
|
||||
contain source last-applied annotation copying. Evidence is not-executed and
|
||||
verified=false; it lists metadata/capability/negative/cleanup receipts the owner
|
||||
must produce later using its reviewed output-suppressing tools. It is a scaffold,
|
||||
not a successful verification receipt. Nothing is apply-ready authority.
|
||||
|
||||
Generation requires a new output directory outside the platform or other owner
|
||||
repository, refuses existing paths including symlinks, and never overwrites any
|
||||
file. Caller-selected staging beneath the system temporary root is supported.
|
||||
Inputs remain immutable and artifacts are deterministic; explicit dates and
|
||||
request/catalog SHA256 bind provenance without clock/network reads.
|
||||
|
||||
## Cross-owner fixture
|
||||
|
||||
`examples/catalog-lane-scaffold/platform-request.yaml` and `warden-request.yaml`
|
||||
use synthetic lane example-workload-kv, consumer example-consumer, prospective
|
||||
reader example-consumer-eso and CCR-2099-9001. No published workload identity,
|
||||
existing client or credential is claimed. Warden manifest references the platform
|
||||
request and eventual CCR destination, includes explicit reviewed date and whole
|
||||
path risk. Platform checks shared schema/lane/title/consumer/owner/risk and exact
|
||||
CCR destination; Warden validates its own complete pointer schema.
|
||||
|
||||
Run both owner scaffold commands into separate new temporary directories and
|
||||
compare the Warden draft catalog with the proposed CCR. Same lane and pointers,
|
||||
risk/owner/consumer parity, retained unknown identity, unconfirmed platform claims,
|
||||
unresolvable frontdoor and absence of executable fetch/approval state are required.
|
||||
The synthetic fixture does not admit an actual lane. Source parity remains distinct
|
||||
from any founder custody choice, attended dated window or production promotion.
|
||||
33
docs/evidence/2026-10-04-catalog-lane-scaffold-offline.json
Normal file
33
docs/evidence/2026-10-04-catalog-lane-scaffold-offline.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"status": "passed",
|
||||
"scope": "actual offline owner CLIs + independently verified deterministic cross-owner fixture; no admission or runtime act",
|
||||
"platform_stage": "/tmp/p4-cross-platform-tj7_84lv/platform",
|
||||
"warden_stage": "/tmp/warden-cross-owner-catalog",
|
||||
"fixture_sha256": "38647d436ccfa8893974c4b88c93d3bd94a9c0a21a2603b54bd84c0195954e32",
|
||||
"artifact_sha256": {
|
||||
"README.md": "2f48aa6ef925f62722ca32d38283d746c0c386da751dec3f9a76fba125d24245",
|
||||
"credential-change-requests/CCR-2099-9001.yaml": "e9db54694b966f186433aaf1f3de6685d8f34be166af9d966950bcca448f151f",
|
||||
"docs/evidence/example-workload-kv.checklist.json": "7d9a58b160c958d8d46f7029398eef8e70f87f0d9423198fb789bdf8fe3a9f9a",
|
||||
"manifests/example-workload-kv.draft.yaml": "2d23bee7451735e2a0aa74435c65a0968bd05e35c35625a4087e2f286a2408b5",
|
||||
"openbao/auth/example-consumer-eso.draft.json": "545a03c95c50b68b0eb127ae5e72d904faf9f68ecfabe26deac27730905b476d",
|
||||
"openbao/policies/workload-kv-read-example-consumer.hcl": "c2821cd6fe5bb16564366fb12fd37c559048841a41c6f2778ad2dad5901e4df4"
|
||||
},
|
||||
"new_scaffold_tests": 14,
|
||||
"prior_window_tests": 7,
|
||||
"unknown_workload_preserved": true,
|
||||
"prospective_auth_claims_unconfirmed": true,
|
||||
"frontdoor_pending_unresolvable": true,
|
||||
"evidence_not_executed": true,
|
||||
"source_sha256": {
|
||||
"scripts/catalog_lane_scaffold.py": "96d60cc47aa78728f5ba96ce1fd63033adf23c05641de93ebbdda3902082d2d1",
|
||||
"scripts/credential-change.py": "d78c493e7c75f3ad0e013745bc13e9f79b9fffc560e5c33ece62e982d800b391",
|
||||
"schemas/catalog-lane-scaffold.schema.json": "8c2100052a86ebef021398bed662ed8a0fe6970506b785b22cec2398e75adaff",
|
||||
"examples/catalog-lane-scaffold/platform-request.yaml": "2070fa0f937ee03add01d1e78b9dbdebf72472b3d8fcc8fae9a51436ec60ab8e",
|
||||
"examples/catalog-lane-scaffold/warden-request.yaml": "38647d436ccfa8893974c4b88c93d3bd94a9c0a21a2603b54bd84c0195954e32",
|
||||
"tests/test_catalog_lane_scaffold.py": "a4abc80b0a29362296208f7d233b9a206a299d0c47f244929f9851fb543911c6",
|
||||
"docs/catalog-lane-scaffold.md": "f6b46acf6515fc13a7ce64b74306c9225c9384f73e63bc7b9723232b08ad3c3c"
|
||||
},
|
||||
"task_id": "RPF-WP-0050-T04",
|
||||
"task_uuid": "6475d924-92c6-504a-b9a1-4f035273f0e1",
|
||||
"recorded_date": "2026-10-04"
|
||||
}
|
||||
49
examples/catalog-lane-scaffold/platform-request.yaml
Normal file
49
examples/catalog-lane-scaffold/platform-request.yaml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
schema_version: 1
|
||||
lane_id: example-workload-kv
|
||||
ccr_id: CCR-2099-9001
|
||||
title: Synthetic exact workload KV read fixture
|
||||
date: '2026-10-04'
|
||||
consumer_repo: example-consumer
|
||||
owner_repo: railiance-platform
|
||||
requester:
|
||||
agent: example-consumer
|
||||
reason: Offline fixture only; no deployed consumer or credential exists
|
||||
review_approvers:
|
||||
- platform-owner
|
||||
- consumer-owner
|
||||
target:
|
||||
domain: infotech
|
||||
tenant: platform
|
||||
workload: example-consumer
|
||||
environment: build
|
||||
purpose: Offline fixture of proposed exact-path metadata
|
||||
openbao:
|
||||
mount: platform
|
||||
kv_path: platform/workloads/example-consumer/config
|
||||
fields:
|
||||
- CLIENT_SECRET
|
||||
policy_name: workload-kv-read-example-consumer
|
||||
auth:
|
||||
mount: kubernetes
|
||||
role: example-consumer-eso
|
||||
audience: openbao
|
||||
service_account_names:
|
||||
- example-consumer-eso
|
||||
service_account_namespaces:
|
||||
- example-consumer
|
||||
ttl: 15m
|
||||
eso:
|
||||
store_name: openbao-example-consumer
|
||||
server: http://openbao.openbao.svc:8200
|
||||
namespace: example-consumer
|
||||
external_secret_name: example-consumer-config
|
||||
secret_name: example-consumer-config
|
||||
refresh_interval: 5m
|
||||
data:
|
||||
- secret_key: client-secret
|
||||
property: CLIENT_SECRET
|
||||
risk:
|
||||
classification: standard
|
||||
notes:
|
||||
- Synthetic prospective lane risk applies to the entire readable path; no actual
|
||||
credentials.
|
||||
15
examples/catalog-lane-scaffold/warden-request.yaml
Normal file
15
examples/catalog-lane-scaffold/warden-request.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
schema_version: 1
|
||||
lane_id: example-workload-kv
|
||||
title: Synthetic exact workload KV read fixture
|
||||
need_keywords:
|
||||
- synthetic example workload config
|
||||
consumer_repo: example-consumer
|
||||
owner_repo: railiance-platform
|
||||
workload_ref:
|
||||
applicability: applicable
|
||||
unknown_reason: Synthetic fixture has no published deployment identity
|
||||
risk: standard
|
||||
wiki_ref: railiance-platform:docs/catalog-lane-scaffold.md
|
||||
canon_ref: railiance-platform:credential-change-requests/CCR-2099-9001.yaml
|
||||
reviewed: '2026-10-04'
|
||||
platform_request_ref: railiance-platform:examples/catalog-lane-scaffold/platform-request.yaml
|
||||
344
schemas/catalog-lane-scaffold.schema.json
Normal file
344
schemas/catalog-lane-scaffold.schema.json
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"schema_version": {
|
||||
"const": 1
|
||||
},
|
||||
"lane_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"ccr_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^CCR-[0-9]{4}-[0-9]{4}$"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512
|
||||
},
|
||||
"date": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"
|
||||
},
|
||||
"consumer_repo": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"owner_repo": {
|
||||
"const": "railiance-platform"
|
||||
},
|
||||
"requester": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"agent": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agent",
|
||||
"reason"
|
||||
]
|
||||
},
|
||||
"review_approvers": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 32,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
}
|
||||
},
|
||||
"target": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"domain": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"tenant": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"workload": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"environment": {
|
||||
"enum": [
|
||||
"build",
|
||||
"development",
|
||||
"test",
|
||||
"staging",
|
||||
"production"
|
||||
]
|
||||
},
|
||||
"purpose": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"domain",
|
||||
"tenant",
|
||||
"workload",
|
||||
"environment",
|
||||
"purpose"
|
||||
]
|
||||
},
|
||||
"openbao": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"mount": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"kv_path": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9][a-z0-9_-]*(?:/[a-z0-9][a-z0-9_-]*)+$"
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 32,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[A-Z][A-Z0-9_]*$"
|
||||
}
|
||||
},
|
||||
"policy_name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^workload-kv-read-[a-z0-9-]+$"
|
||||
},
|
||||
"auth": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"mount": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"audience": {
|
||||
"const": "openbao"
|
||||
},
|
||||
"service_account_names": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 32,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
}
|
||||
},
|
||||
"service_account_namespaces": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 32,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
}
|
||||
},
|
||||
"ttl": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[1-9][0-9]*[smh]$"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"mount",
|
||||
"role",
|
||||
"audience",
|
||||
"service_account_names",
|
||||
"service_account_namespaces",
|
||||
"ttl"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"mount",
|
||||
"kv_path",
|
||||
"fields",
|
||||
"policy_name",
|
||||
"auth"
|
||||
]
|
||||
},
|
||||
"eso": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"store_name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"server": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^https?://[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::[0-9]+)?$"
|
||||
},
|
||||
"namespace": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"external_secret_name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"secret_name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
|
||||
},
|
||||
"refresh_interval": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[1-9][0-9]*[smh]$"
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 32,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"secret_key": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[a-zA-Z0-9._-]+$"
|
||||
},
|
||||
"property": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512,
|
||||
"pattern": "^[A-Z][A-Z0-9_]*$"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"secret_key",
|
||||
"property"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"store_name",
|
||||
"server",
|
||||
"namespace",
|
||||
"external_secret_name",
|
||||
"secret_name",
|
||||
"refresh_interval",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"risk": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"classification": {
|
||||
"enum": [
|
||||
"standard",
|
||||
"high"
|
||||
]
|
||||
},
|
||||
"notes": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 32,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 512
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"classification",
|
||||
"notes"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"schema_version",
|
||||
"lane_id",
|
||||
"ccr_id",
|
||||
"title",
|
||||
"date",
|
||||
"consumer_repo",
|
||||
"owner_repo",
|
||||
"requester",
|
||||
"review_approvers",
|
||||
"target",
|
||||
"openbao",
|
||||
"eso",
|
||||
"risk"
|
||||
],
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "Draft exact KV-v2 workload-read lane input v1"
|
||||
}
|
||||
203
scripts/catalog_lane_scaffold.py
Normal file
203
scripts/catalog_lane_scaffold.py
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
"""Draft-only offline exact-path lane scaffolding; no execution surface."""
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import deepcopy
|
||||
from datetime import date
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import jsonschema
|
||||
import yaml
|
||||
|
||||
REPO_DIR = Path(__file__).resolve().parents[1]
|
||||
MAX_INPUT_BYTES = 128 * 1024
|
||||
SECRET_MARKERS = ('AGE-SECRET-KEY-1', '-----BEGIN', 'hvs.', 'hvb.', 'hvc.', 'ghp_', 'github_pat_', 'npm_', 'BAO_TOKEN=', 'VAULT_TOKEN=', 'OPENBAO_ROOT_TOKEN=')
|
||||
|
||||
|
||||
class UniqueLoader(yaml.SafeLoader):
|
||||
pass
|
||||
|
||||
|
||||
def unique_mapping(loader, node):
|
||||
result = {}
|
||||
for key_node, value_node in node.value:
|
||||
key = loader.construct_object(key_node)
|
||||
if not isinstance(key, str) or key in result:
|
||||
raise ValueError('Invalid or duplicate metadata key')
|
||||
result[key] = loader.construct_object(value_node)
|
||||
return result
|
||||
|
||||
|
||||
UniqueLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, unique_mapping)
|
||||
|
||||
|
||||
def read_metadata(path):
|
||||
with Path(path).open('rb') as stream:
|
||||
raw = stream.read(MAX_INPUT_BYTES + 1)
|
||||
if len(raw) > MAX_INPUT_BYTES:
|
||||
raise ValueError('Metadata input exceeds size limit')
|
||||
text = raw.decode('utf-8')
|
||||
if any(marker in text for marker in SECRET_MARKERS) or re.search(r'(?<![A-Za-z])sk-[A-Za-z0-9]', text):
|
||||
raise ValueError('Metadata input contains rejected secret material')
|
||||
try:
|
||||
data = yaml.load(text, Loader=UniqueLoader)
|
||||
canonical = json.dumps(data, allow_nan=False)
|
||||
if any(marker in canonical for marker in SECRET_MARKERS) or re.search(r"(?<![A-Za-z])sk-[A-Za-z0-9]", canonical):
|
||||
raise ValueError("Rejected parsed secret material")
|
||||
except (yaml.YAMLError, TypeError, RecursionError, ValueError) as exc:
|
||||
raise ValueError('Invalid metadata document') from None
|
||||
return data, hashlib.sha256(raw).hexdigest()
|
||||
|
||||
|
||||
def validate_request(data):
|
||||
schema = json.loads((REPO_DIR / 'schemas/catalog-lane-scaffold.schema.json').read_text())
|
||||
if not jsonschema.Draft202012Validator(schema).is_valid(data):
|
||||
raise ValueError('Request does not match closed platform scaffold schema v1')
|
||||
def text_values(value):
|
||||
if isinstance(value, dict):
|
||||
return [item for nested in value.values() for item in text_values(nested)]
|
||||
if isinstance(value, list):
|
||||
return [item for nested in value for item in text_values(nested)]
|
||||
return [value] if isinstance(value, str) else []
|
||||
if any(any(ord(char) < 32 for char in value) for value in text_values(data)):
|
||||
raise ValueError('Control characters are not supported in scaffold metadata')
|
||||
date.fromisoformat(data['date'])
|
||||
bao = data['openbao']
|
||||
if not bao['kv_path'].startswith(bao['mount'] + '/'):
|
||||
raise ValueError('KV path must use the explicitly declared mount')
|
||||
auth = bao['auth']
|
||||
units = {'s': 1, 'm': 60, 'h': 3600}
|
||||
ttl = auth['ttl']
|
||||
if int(ttl[:-1]) * units[ttl[-1]] > 900:
|
||||
raise ValueError('Prospective Kubernetes token TTL exceeds fifteen minutes')
|
||||
eso = data['eso']
|
||||
if len(auth['service_account_names']) != 1 or len(auth['service_account_namespaces']) != 1:
|
||||
raise ValueError('One explicit ESO auth identity is supported')
|
||||
keys = [item['secret_key'] for item in eso['data']]
|
||||
properties = [item['property'] for item in eso['data']]
|
||||
if len(set(keys)) != len(keys) or len(set(properties)) != len(properties) or set(properties) != set(bao['fields']):
|
||||
raise ValueError('ESO mappings must cover the explicit field set exactly once')
|
||||
|
||||
|
||||
def verify_catalog_join(request, catalog):
|
||||
# Warden validates its own full input; platform checks only the shared seam.
|
||||
if not isinstance(catalog, dict):
|
||||
raise ValueError('Catalog pointer manifest must be an object')
|
||||
for field in ('schema_version', 'lane_id', 'title', 'consumer_repo', 'owner_repo'):
|
||||
if catalog.get(field) != request[field]:
|
||||
raise ValueError('Cross-owner fixture identity mismatch')
|
||||
if catalog.get('risk') != request['risk']['classification']:
|
||||
raise ValueError('Cross-owner risk mismatch')
|
||||
if catalog.get('canon_ref') != 'railiance-platform:credential-change-requests/' + request['ccr_id'] + '.yaml':
|
||||
raise ValueError('Catalog CCR destination pointer mismatch')
|
||||
ref = catalog.get('platform_request_ref')
|
||||
if not isinstance(ref, str) or not re.fullmatch(r'railiance-platform:[a-zA-Z0-9_./-]+', ref) or '..' in ref:
|
||||
raise ValueError('Catalog needs a non-secret owner request pointer')
|
||||
|
||||
|
||||
def render_artifacts(data, policy_renderer, auth_renderer, request_digest, catalog_digest=None):
|
||||
validate_request(data)
|
||||
data = deepcopy(data)
|
||||
bao = data['openbao']
|
||||
auth = bao['auth']
|
||||
lane = data['lane_id']
|
||||
policy_file = 'openbao/policies/' + bao['policy_name'] + '.hcl'
|
||||
ccr = {'id': data['ccr_id'], 'kind': 'credential-change-request', 'schema_version': 1,
|
||||
'request_type': 'workload-kv-read', 'title': data['title'], 'status': 'proposed',
|
||||
'created': data['date'], 'updated': data['date'], 'requester': data['requester'],
|
||||
'review': {'required': True, 'required_approvers': data['review_approvers'], 'comments': []},
|
||||
'target': data['target'],
|
||||
'openbao': {'mount': bao['mount'], 'kv_path': bao['kv_path'], 'fields': bao['fields'],
|
||||
'policy_name': bao['policy_name'], 'policy_file': policy_file, 'metadata_read': False,
|
||||
'auth': {'method': 'kubernetes', 'mount': auth['mount'], 'role': auth['role'],
|
||||
'audience': auth['audience'], 'ttl': auth['ttl'],
|
||||
'token_max_ttl': auth['ttl'], 'token_explicit_max_ttl': auth['ttl'],
|
||||
'token_no_default_policy': True, 'policies': [bao['policy_name']],
|
||||
'bound_claims': {'service_account_names': auth['service_account_names'],
|
||||
'service_account_namespaces': auth['service_account_namespaces']},
|
||||
'bound_claims_confirmed': False}},
|
||||
'access_frontdoor': {'type': 'external-secrets', 'catalog_id': lane,
|
||||
'readiness': 'pending-review', 'resolvable': False},
|
||||
'risk': data['risk'],
|
||||
'verification': {'positive': ['Owner-reviewed native exact identity and exact path proof required'],
|
||||
'negative': ['Owner-reviewed wrong identity, sibling GET and parent LIST denials required'],
|
||||
'activation_conditions': ['Exact CCR approval, confirmed identity, attended custody and native receipt acceptance required']},
|
||||
'lifecycle': {'deactivate': 'Owner-reviewed delivery withdrawal and token revocation; exact rollback receipt required',
|
||||
'rotate': 'Separate version-guarded owner approval and contained value delivery required',
|
||||
'compromised': 'Owner incident process; no automatic read, reseed or cleanup'},
|
||||
'scaffold': {'draft_only': True, 'consumer_repo': data['consumer_repo'],
|
||||
'owner_repo': data['owner_repo'], 'request_sha256': request_digest,
|
||||
'catalog_request_sha256': catalog_digest}}
|
||||
annotation = {'railiance.io/credential-change': data['ccr_id'], 'railiance.io/admission': 'pending-review'}
|
||||
eso = data['eso']
|
||||
store = {'apiVersion': 'external-secrets.io/v1', 'kind': 'ClusterSecretStore',
|
||||
'metadata': {'name': eso['store_name'], 'annotations': deepcopy(annotation)},
|
||||
'spec': {'provider': {'vault': {'server': eso['server'], 'path': bao['mount'], 'version': 'v2',
|
||||
'auth': {'kubernetes': {'mountPath': auth['mount'], 'role': auth['role'],
|
||||
'audiences': [auth['audience']],
|
||||
'serviceAccountRef': {'name': auth['service_account_names'][0],
|
||||
'namespace': auth['service_account_namespaces'][0]}}}}},
|
||||
'conditions': [{'namespaces': [eso['namespace']]}]}}
|
||||
secret = {'apiVersion': 'external-secrets.io/v1', 'kind': 'ExternalSecret',
|
||||
'metadata': {'name': eso['external_secret_name'], 'namespace': eso['namespace'], 'annotations': deepcopy(annotation)},
|
||||
'spec': {'refreshInterval': eso['refresh_interval'],
|
||||
'secretStoreRef': {'kind': 'ClusterSecretStore', 'name': eso['store_name']},
|
||||
'target': {'name': eso['secret_name'], 'creationPolicy': 'Owner', 'deletionPolicy': 'Retain',
|
||||
'template': {'metadata': {'annotations': deepcopy(annotation)}}},
|
||||
'data': [{'secretKey': item['secret_key'],
|
||||
'remoteRef': {'key': bao['kv_path'][len(bao['mount']) + 1:], 'property': item['property']}}
|
||||
for item in eso['data']]}}
|
||||
evidence = {'schema_version': 1, 'lane_id': lane, 'ccr_id': data['ccr_id'], 'status': 'not-executed',
|
||||
'draft_only': True, 'verified': False, 'request_sha256': request_digest,
|
||||
'required_owner_checks': ['Exact revision/digest and required owner reviews',
|
||||
'Identity/auth claims confirmed separately; no inferred workload identity',
|
||||
'Metadata/capability readback through reviewed output-suppressing owner tool',
|
||||
'No Secret bodies, values or raw subprocess errors in evidence',
|
||||
'Native scoped positive/negative, cleanup/revocation and rollback receipts after approval']}
|
||||
artifacts = {'credential-change-requests/' + data['ccr_id'] + '.yaml': yaml.safe_dump(ccr, sort_keys=False),
|
||||
policy_file: '# DRAFT ONLY: exact CCR review and confirmed identity required.\n' + policy_renderer(ccr),
|
||||
'openbao/auth/' + auth['role'] + '.draft.json': json.dumps({'draft_only': True, 'status': 'proposed',
|
||||
'bound_claims_confirmed': False,
|
||||
'auth_payload': auth_renderer(ccr)}, indent=2) + '\n',
|
||||
'manifests/' + lane + '.draft.yaml': '# DRAFT REVIEW ARTIFACT. Do not apply. No custody/admission supplied.\n' + yaml.safe_dump_all([store, secret], sort_keys=False),
|
||||
'docs/evidence/' + lane + '.checklist.json': json.dumps(evidence, indent=2) + '\n',
|
||||
'README.md': 'Draft-only review bundle. No admission, approval, confirmed identity, credential value or executed receipt.\n'
|
||||
'Review the exact request digest and owner destinations before promoting any file.\n'
|
||||
'ESO target metadata is explicit to contain source last-applied annotations.\n'}
|
||||
return artifacts
|
||||
|
||||
|
||||
def scaffold(input_path, output_dir, policy_renderer, auth_renderer, catalog_manifest=None):
|
||||
data, digest = read_metadata(input_path)
|
||||
catalog_digest = None
|
||||
if catalog_manifest is not None:
|
||||
catalog, catalog_digest = read_metadata(catalog_manifest)
|
||||
validate_request(data)
|
||||
verify_catalog_join(data, catalog)
|
||||
artifacts = render_artifacts(data, policy_renderer, auth_renderer, digest, catalog_digest)
|
||||
output = Path(output_dir).absolute()
|
||||
if output.is_symlink() or output.exists() or output.resolve().is_relative_to(REPO_DIR.resolve()):
|
||||
raise ValueError('Output must be a new staging directory outside the owner repository')
|
||||
if any((p / '.git').exists() for p in output.resolve().parents if p != Path(tempfile.gettempdir()).resolve()):
|
||||
raise ValueError('Output staging must not be inside a repository')
|
||||
output.mkdir(mode=0o700, parents=False)
|
||||
for relative, content in artifacts.items():
|
||||
path = output / relative
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open('x', encoding='utf-8') as stream:
|
||||
stream.write(content)
|
||||
return artifacts
|
||||
|
||||
|
||||
def command_scaffold(args, policy_renderer, auth_renderer):
|
||||
try:
|
||||
artifacts = scaffold(args.input, args.output_dir, policy_renderer, auth_renderer, args.catalog_manifest)
|
||||
except (ValueError, OSError, UnicodeError, RecursionError):
|
||||
print('Scaffold refused: invalid metadata, mismatched owner pointers, or unsafe staging output.', file=sys.stderr)
|
||||
return 2
|
||||
print('Prepared ' + str(len(artifacts)) + ' draft review artifacts; nothing admitted or executed.')
|
||||
return 0
|
||||
|
|
@ -2240,6 +2240,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
)
|
||||
applier_apply.set_defaults(func=command_applier_apply)
|
||||
|
||||
from catalog_lane_scaffold import command_scaffold
|
||||
scaffold = sub.add_parser("scaffold-lane", help="Generate proposed offline CCR/policy/ESO/evidence drafts in a new staging directory")
|
||||
scaffold.add_argument("--input", required=True)
|
||||
scaffold.add_argument("--output-dir", required=True)
|
||||
scaffold.add_argument("--catalog-manifest")
|
||||
scaffold.set_defaults(func=lambda args: command_scaffold(args, generated_policy_hcl, auth_payload))
|
||||
|
||||
from provisioning_window import command_window
|
||||
window = sub.add_parser("window-checklist", help="Render an offline attended-window preparation checklist; never execute")
|
||||
window.add_argument("--window-file", required=True)
|
||||
|
|
|
|||
150
tests/test_catalog_lane_scaffold.py
Normal file
150
tests/test_catalog_lane_scaffold.py
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import argparse
|
||||
from copy import deepcopy
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
import urllib.request
|
||||
|
||||
import yaml
|
||||
|
||||
REPO_DIR = Path(__file__).resolve().parents[1]
|
||||
SCRIPTS = REPO_DIR / 'scripts'
|
||||
sys.path.insert(0, str(SCRIPTS))
|
||||
import catalog_lane_scaffold as scaffold
|
||||
SPEC = importlib.util.spec_from_file_location('ccr_scaffold_helpers', SCRIPTS / 'credential-change.py')
|
||||
ccr = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(ccr)
|
||||
|
||||
|
||||
class ScaffoldTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.tmp.cleanup)
|
||||
self.root = Path(self.tmp.name)
|
||||
self.request = REPO_DIR / 'examples/catalog-lane-scaffold/platform-request.yaml'
|
||||
self.catalog = REPO_DIR / 'examples/catalog-lane-scaffold/warden-request.yaml'
|
||||
self.data, self.digest = scaffold.read_metadata(self.request)
|
||||
|
||||
def render(self, data=None):
|
||||
return scaffold.render_artifacts(data or self.data, ccr.generated_policy_hcl, ccr.auth_payload, self.digest)
|
||||
|
||||
def write_request(self, data):
|
||||
path = self.root / 'request.yaml'
|
||||
path.write_text(yaml.safe_dump(data))
|
||||
return path
|
||||
|
||||
def test_existing_ccr_validator_accepts_only_proposed_unconfirmed_draft(self):
|
||||
output = self.root / 'bundle'
|
||||
scaffold.scaffold(self.request, output, ccr.generated_policy_hcl, ccr.auth_payload, self.catalog)
|
||||
path = output / 'credential-change-requests/CCR-2099-9001.yaml'
|
||||
with patch.object(ccr, 'REPO_DIR', output):
|
||||
data, errors, warnings = ccr.validate_ccr(path)
|
||||
self.assertEqual(errors, [])
|
||||
self.assertTrue(any('not confirmed' in w for w in warnings))
|
||||
self.assertEqual(data['status'], 'proposed')
|
||||
self.assertFalse(data['access_frontdoor']['resolvable'])
|
||||
self.assertFalse(data['openbao']['auth']['bound_claims_confirmed'])
|
||||
self.assertEqual(data['review']['comments'], [])
|
||||
self.assertTrue(ccr.applier_readiness_blockers(data))
|
||||
self.assertEqual(set(ccr.auth_payload(data)['bound_service_account_names']), {'example-consumer-eso'})
|
||||
|
||||
def test_policy_and_eso_exact_scope_annotation_containment_and_no_values(self):
|
||||
artifacts = self.render()
|
||||
policy = artifacts['openbao/policies/workload-kv-read-example-consumer.hcl']
|
||||
self.assertIn('path "platform/data/workloads/example-consumer/config"', policy)
|
||||
self.assertEqual(policy.count('path "'), 1)
|
||||
self.assertNotIn('metadata/', policy)
|
||||
store, secret = list(yaml.safe_load_all(artifacts['manifests/example-workload-kv.draft.yaml']))
|
||||
self.assertEqual(store['spec']['conditions'], [{'namespaces': ['example-consumer']}])
|
||||
self.assertEqual(store['spec']['provider']['vault']['auth']['kubernetes']['audiences'], ['openbao'])
|
||||
target = secret['spec']['target']
|
||||
self.assertEqual(target['template']['metadata']['annotations']['railiance.io/admission'], 'pending-review')
|
||||
self.assertNotIn('kubectl.kubernetes.io/last-applied-configuration', json.dumps(target))
|
||||
self.assertEqual(secret['spec']['data'], [{'secretKey': 'client-secret', 'remoteRef': {'key': 'workloads/example-consumer/config', 'property': 'CLIENT_SECRET'}}])
|
||||
self.assertNotIn('dataFrom', secret['spec'])
|
||||
self.assertNotIn('Secret', [store['kind'], secret['kind']])
|
||||
evidence = json.loads(artifacts['docs/evidence/example-workload-kv.checklist.json'])
|
||||
self.assertFalse(evidence['verified'])
|
||||
self.assertEqual(evidence['status'], 'not-executed')
|
||||
|
||||
def test_closed_schema_and_authority_injection_refused(self):
|
||||
for location, key, value in [('top','status','approved'),('auth','bound_claims_confirmed',True),('top','secret_values',{'CLIENT_SECRET':'not-a-value'})]:
|
||||
data = deepcopy(self.data)
|
||||
target = data if location == 'top' else data['openbao']['auth']
|
||||
target[key] = value
|
||||
with self.assertRaises(ValueError): self.render(data)
|
||||
|
||||
def test_missing_identity_and_risk_are_not_inferred(self):
|
||||
for location, key in [('auth','service_account_names'),('top','risk')]:
|
||||
data = deepcopy(self.data)
|
||||
target = data if location == 'top' else data['openbao']['auth']
|
||||
del target[key]
|
||||
with self.assertRaises(ValueError): self.render(data)
|
||||
|
||||
def test_wildcards_traversal_and_hcl_injection_refused(self):
|
||||
for path in ['platform/workloads/*','platform/../other','platform/workloads/config\n','platform/a/" { capabilities = ["sudo"] }']:
|
||||
data = deepcopy(self.data);data['openbao']['kv_path'] = path
|
||||
with self.assertRaises(ValueError): self.render(data)
|
||||
|
||||
def test_wrong_mount_and_overlong_ttl_refused(self):
|
||||
data = deepcopy(self.data);data['openbao']['mount'] = 'other'
|
||||
with self.assertRaises(ValueError): self.render(data)
|
||||
data = deepcopy(self.data);data['openbao']['auth']['ttl'] = '1h'
|
||||
with self.assertRaises(ValueError): self.render(data)
|
||||
|
||||
def test_field_mapping_omissions_duplicates_and_extra_fields_refused(self):
|
||||
for mapping in [[], [{'secret_key':'one','property':'OTHER'}], [{'secret_key':'a','property':'CLIENT_SECRET'},{'secret_key':'b','property':'CLIENT_SECRET'}]]:
|
||||
data = deepcopy(self.data);data['eso']['data'] = mapping
|
||||
with self.assertRaises(ValueError): self.render(data)
|
||||
|
||||
def test_cross_owner_pointer_fixture_and_unknown_identity_preserved(self):
|
||||
catalog, _ = scaffold.read_metadata(self.catalog)
|
||||
scaffold.verify_catalog_join(self.data, catalog)
|
||||
self.assertIn('unknown_reason', catalog['workload_ref'])
|
||||
for key,value in [('lane_id','other'),('risk','high'),('canon_ref','railiance-platform:credential-change-requests/OTHER.yaml'),('platform_request_ref','other-owner:request.yaml')]:
|
||||
bad=deepcopy(catalog);bad[key]=value
|
||||
with self.assertRaises(ValueError): scaffold.verify_catalog_join(self.data,bad)
|
||||
|
||||
def test_canonical_outputs_deterministic_and_input_immutable(self):
|
||||
before = self.request.read_bytes();data_before=deepcopy(self.data)
|
||||
self.assertEqual(self.render(), self.render())
|
||||
self.assertEqual(self.data,data_before)
|
||||
self.assertEqual(self.request.read_bytes(),before)
|
||||
|
||||
def test_collision_and_repo_output_refused_without_overwrite(self):
|
||||
output=self.root/'occupied';output.mkdir();sentinel=output/'sentinel';sentinel.write_text('keep')
|
||||
with self.assertRaises(ValueError): scaffold.scaffold(self.request,output,ccr.generated_policy_hcl,ccr.auth_payload)
|
||||
self.assertEqual(sentinel.read_text(),'keep')
|
||||
with self.assertRaises(ValueError): scaffold.scaffold(self.request,REPO_DIR/'live-output',ccr.generated_policy_hcl,ccr.auth_payload)
|
||||
|
||||
def test_no_network_or_subprocess_in_generation(self):
|
||||
with patch.object(urllib.request,'urlopen',side_effect=AssertionError('network forbidden')),patch.object(subprocess,'run',side_effect=AssertionError('execution forbidden')):
|
||||
artifacts=scaffold.scaffold(self.request,self.root/'isolated',ccr.generated_policy_hcl,ccr.auth_payload,self.catalog)
|
||||
self.assertEqual(len(artifacts),6)
|
||||
|
||||
def test_duplicate_yaml_and_secret_material_refused_without_echo(self):
|
||||
for text in ['lane_id: a\nlane_id: b\n', 'title: hvs.CANARY_NEVER_ECHO\n', 'title: "\\x68vs.CANARY_NEVER_ECHO"\n']:
|
||||
path=self.root/'bad.yaml';path.write_text(text)
|
||||
args=argparse.Namespace(input=path,output_dir=self.root/'bad-output',catalog_manifest=None)
|
||||
stream=io.StringIO()
|
||||
with patch('sys.stderr',stream): self.assertEqual(scaffold.command_scaffold(args,ccr.generated_policy_hcl,ccr.auth_payload),2)
|
||||
self.assertNotIn('CANARY',stream.getvalue())
|
||||
self.assertFalse((self.root/'bad-output').exists())
|
||||
|
||||
def test_oversized_input_refused(self):
|
||||
path=self.root/'big.yaml';path.write_text('a'*(scaffold.MAX_INPUT_BYTES+1))
|
||||
with self.assertRaises(ValueError): scaffold.read_metadata(path)
|
||||
|
||||
def test_schema_rejects_boolean_version_and_nonexistent_calendar_day(self):
|
||||
for version,day in [(True,'2026-10-04'),(1,'2026-02-31')]:
|
||||
data=deepcopy(self.data);data['schema_version']=version;data['date']=day
|
||||
with self.assertRaises(ValueError): self.render(data)
|
||||
|
||||
|
||||
if __name__ == '__main__': unittest.main()
|
||||
|
|
@ -57,7 +57,7 @@ Generate a checklist from all named authoritative roots, verified Hub decision j
|
|||
|
||||
```task
|
||||
id: RPF-WP-0050-T04
|
||||
status: todo
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "6475d924-92c6-504a-b9a1-4f035273f0e1"
|
||||
```
|
||||
|
|
@ -65,3 +65,7 @@ state_hub_task_id: "6475d924-92c6-504a-b9a1-4f035273f0e1"
|
|||
P4 accepted for platform-side source review and bounded scaffolding after Warden returns its contract. Catalog entry/scaffold never supplies credentials, reviews, admission or live authority. Keep owner acceptance explicit before implementation.
|
||||
|
||||
2026-10-03: Platform source review accepts the delivered Warden seam at `ops-warden/wiki/CatalogLaneScaffoldContract.md` (`ac112d8`). T04 is now a ready source implementation task for the platform-owned rendering and schema fixture. Warden T02 may progress independently; no mutual owner-return wait remains. This is source contract acceptance, not credential admission or production authorization.
|
||||
|
||||
2026-10-04 T04 implementation: closed platform metadata schema and `credential-change.py scaffold-lane` authored with deterministic proposed CCR, existing exact-path policy/auth rendering, namespace-restricted draft ESO with annotation containment, and non-executed owner evidence checklist. Shared synthetic platform/Warden pointer fixtures retain unknown workload identity and unconfirmed auth claims. Fourteen focused offline tests pass in isolated prepared source; cross-owner staging fixture acceptance remains before marking T04 done. No admission, credentials, human answer or production execution.
|
||||
|
||||
2026-10-04 T04 acceptance: actual applied-source scaffold tests 14/14 and existing window tests 7/7 pass, with CLI help and diff checks. Both actual owner CLIs produced matching cross-owner synthetic fixture drafts; platform independently verified Warden catalog/model loading, exact lane/owner/consumer/risk/CCR pointers and fixture digest, preserved unknown workload, unconfirmed prospective auth, proposed/unresolvable frontdoor, explicit ESO target annotations and non-executed evidence. Six platform artifacts match the independently generated second platform stage byte-for-byte. Receipt: `docs/evidence/2026-10-04-catalog-lane-scaffold-offline.json`. T04 is done for offline source/scaffold/fixture acceptance only. T02 remains the founder custody-choice wait; T03 remains the Custodian dated-window handoff. No admission, actual credentials, schedule or production execution was performed.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue