Add attended signer adapter with phase-aware read-only recovery
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
codex 2026-10-16 18:44:07 +02:00
parent 7be8b6e76d
commit a3e8eb64ba
11 changed files with 1385 additions and 1 deletions

View file

@ -0,0 +1,128 @@
id: CCR-2026-0050
kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: Retained flex-auth lifecycle PDP Ed25519 seed ESO read lane
status: proposed
created: '2026-10-16'
updated: '2026-10-16'
requester:
agent: railiance-platform
reason: CUST-WP-0080 / RPF-WP-0035-T12 / FLEX-WP-0024-T06 source proposal. Exact
new custody/read scope requires founder review; no approval or native binding
is inferred.
review:
required: true
required_approvers:
- founder
- railiance-platform-owner
- access-engine-owner
- secrets-engine-owner
- cluster-gitops-owner
comments: []
target:
domain: infotech
tenant: platform
workload: flex-auth-secrets-engine
environment: production
purpose: Deliver the Ed25519 seed only to the retained lifecycle PDP startup file.
No taskpilot enablement, caller policy changes or private consumer trust discovery.
openbao:
mount: platform
kv_path: platform/workloads/flex-auth/envelope-signing
fields:
- ED25519_PRIVATE_KEY
metadata_read: false
token_self_lifecycle: true
policy_name: workload-kv-read-flex-auth-envelope-signing
policy_file: openbao/policies/workload-kv-read-flex-auth-envelope-signing.hcl
auth:
method: kubernetes
mount: kubernetes
role: external-secrets-flex-auth-envelope-signing
bound_claims:
service_account_names:
- flex-auth-envelope-signing-eso
service_account_namespaces:
- flex-auth
bound_claims_confirmed: false
policies:
- workload-kv-read-flex-auth-envelope-signing
ttl: 15m
audience: openbao
token_max_ttl: 15m
token_explicit_max_ttl: 15m
token_no_default_policy: true
access_frontdoor:
type: external-secrets
catalog_id: flex-auth-decision-envelope-signing-key
selector: Retained flex-auth/flex-auth-secrets-engine signer only
readiness: pending-review
resolvable: false
delivery:
surface: external-secrets
target: Namespace flex-auth; dedicated SA flex-auth-envelope-signing-eso; SecretStore
openbao-flex-auth-envelope-signing; ExternalSecret/Secret flex-auth-secrets-engine-envelope-signing;
seed property only -> startup file /var/run/secrets/decision-envelope-signing/seed0440,
readOnly uid/gid/fsGroup65532; no envFrom
bootstrap_command: Attended writer is a distinct exact-source action proposal in
docs/credential-lane-designs/flex-auth-envelope-signing-native-plan.json; this
read CCR authorizes neither seed writes nor any execution by itself.
risk:
classification: high
notes:
- Possession of seed can forge lifecycle PDP envelopes; independent consumer public
trust admission and fixed policy pins remain required.
- 'KV read is document-granular: dedicated ESO identity can read all three fields;
only private field is projected. Signer and Secrets consumer have no KV read role.'
- No parent/list/metadata/write/delete capability on ESO role. No token/Secret read
or ExternalSecret mutation granted to signer/consumer.
- Dedicated role is new scope; founder approval required. No workload rapp tuple
invented; absent managed package declaration remains explicit migration debt.
- Controlled outage fence stops every signer pod and disables autoscaling before
creation/rotation. Recreate single writer and retained PVC remain Access-owned
review gates.
- fields declares only projected ED25519_PRIVATE_KEY. The same KV document also
has public key/kid, and OpenBao data-read is not field-filtered; do not claim
field-level authorization.
verification:
positive:
- Exact SA/namespace/audience login; native exact data read; ESO Ready/SecretSynced;
safe metadata resourceVersion and admitted KV version correspondence.
- All retained signer replicas restart with admitted kid/source/image and seed/public
correspondence; signed non-effect lifecycle allow/deny with durable originals
and independent consumer signature verification.
negative:
- Wrong SA/namespace/audience login refused; sibling/list/metadata/write/delete
denied; signer/consumer TokenRequest and Secret/ExternalSecret mutation denied.
- Unknown/old kid, wrong policy, tamper, expired/unsigned envelope, wrong caller
and missing seed refuse; no taskpilot activation.
- Receipt write failure/lost backend response retained unknown; no blind retry/remint;
stale CAS cannot overwrite version.
activation_conditions:
- All five required approvers independently dispose the exact CCR and native writer/fence
proposal; founder exact new custody scope approved.
- Current retained identity919ce25, immutable published producer image and explicit
new flex-auth-secrets-engine-outbox PVC1Gi/local-path, retention/reclaim and bounded-runtime
plan reviewed by Access; missing managed package migration debt explicitly accepted
for this retained workload only.
- Exact native bound claims verified without printing JWT/Secret; read grant policy
and role native readback match source.
- Protected child source pinned, silent attended OIDC context verified, target cluster/backend
pinned, fence verified and durable receipt custody installed.
- Consumer independently admits owner-authenticated public receipt and exact policy/image/trust
pins before protected acceptance.
evidence: []
lifecycle:
deactivate: Fence all retained signer processes, stop consumers, disable delivery
role/ESO and online trust; retain private KV and audit receipts under explicit
owner retention until reviewed destruction.
rotate: Pre-admit successor public receipt offline; fence consumers/all signer replicas/no
HPA; CAS exact version with new in-memory seed; wait exact ESO version, restart
single writer, verify native allow/deny and old-key refusal before reopening.
compromised: Immediately remove old online trust and stop all signer processes;
forward-only CAS recovery with independently approved successor. Never restore
compromised seed; archive historical public keys for offline audit only.
state_hub:
workplan_id: RPF-WP-0035
task_id: RPF-WP-0035-T12

View file

@ -0,0 +1,187 @@
# Attended native signer custody adapter source
RPF-WP-0035-T12 / CUST-WP-0080. This source implements the protected native
adapter described by the separate frozen native-preparation R1. It does not
admit an execution. Its CLI is silent and intended only as the exact reviewed
child of `warden access openbao-platform-admin-login --exec` in the founder's
attended OIDC envelope. The existing Warden owner contains the token helper,
checks child output and revokes/removes the session on exit. No new service,
credential broker, standing grant, API or key-returning interface is created.
## Current routing blocker
During preparation the required `warden plan` query for attended OpenBao
platform administration of signer CAS0/metadata selected `openbao-structure-survey`
with an approve ceremony, rather than `openbao-platform-admin-login` with
`oidc_login`. The installed CLI reports wheel-bundled catalog0.1.2 fallback.
This is preserved as a live refusal, not routed around. Exact `route show` names
the existing active attended login, but that pointer does not cure dispatch.
The adapter requires an independently retained routing receipt selecting the
correct lane/verdict/identity act. No founder execution step is ready until the
routing defect and all other native approvals are resolved by their owners.
## Independent input and authority boundary
The CLI requires an absolute owner-private admission file, its independently
approved exact SHA256, the exact private receipt directory selected in the
reviewed native proposal, and an original receipt only for recovery. No default
image/package/caller trust, founder identity, approval, route or source pin is
usable. The admission schema is closed in `validate_admission`; it includes the
immutable published producer image, retained caller, deployment projection,
selected consumer trust baseline, exact native plan/CCR/source bundle, fresh
UTC authority expiry (5..120seconds), founder entity and five distinct externally
reviewed owner dispositions. Each disposition binds the same approval-free
scope digest. An admission shape, local digest or review string never approves
itself: the founder and owners must independently review the exact supplied
command/file hash through existing governance before attended invocation.
The trust hash selects the independently reviewed consumer verifier/policy/trust
baseline for this custody action. It is not a claim that the newly generated
public key is already online: generation happens later, and independent delivery
and successor trust installation are required after the public receipt. The
writer never reads consumer private credentials or discovers trust from KV.
All successful custody states remain pending independent public admission.
`checked_sources` verifies the actual selected native action plan, exact proposed
kid, read CCR hash/status/binding, generated policy source, and source bundle.
The read CCR must independently be approved/binding-confirmed; the canonical
proposed CCR remains unapproved, so ordinary source checkout refuses. Source
bundle pins execution modules, not arbitrary request-selected code. The native
image/caller/seed projection is compared to the actual stopped Deployment.
The lifecycle policy digest/version stays unchanged; execute-ccr probes must
prove no ALLOW/effect. Current generic runtime guard can return HTTP503 before
the lifecycle evaluator, so signed unknown_action is not promised.
## Actual checks and effects
Before private work, the child checks OpenBao cluster ID, unsealed state, exact
non-root platform-admin OIDC entity, enabled audit, actual exact read-policy HCL
and role bindings/15m limits. The selected fixed loopback transport is the existing
owner-admin bridge; Kubernetes reads use fixed strict-host SSH to railiance01.
The OpenBao token lookup-self identifier stays only in the contained custody
process to authenticate fixed HTTP calls; it never reaches the agent, a receipt,
argv or another token file. This is a native protected-process implementation,
not evidence that agent runtime isolation has been promoted or granted.
The fence checks the pinned kube-system UID, stopped Deployment generation and
replica status, exact immutable image/caller/seed mount/kid projection, zero owned
ReplicaSet replicas, no live or terminating signer-SA/owned/seed-mount pods, and
no targeting HPA. It rechecks before metadata setup and immediately before CAS.
A private filesystem lock excludes a second local custody window. The lock and
observations do not provide atomic Kubernetes fencing: the independently reviewed
exclusive window must keep GitOps/Helm/other creators stopped. No rollout or
cluster mutation is performed by this adapter, and no concurrency guarantee is
inferred from source alone. Access/cluster owners retain that admission.
Bootstrap requires the exact OpenBao empty-errors HTTP404 metadata absence. A
durable private intent precedes exact CAS-required/max_versions2/no-auto-delete
metadata setup, followed by exact native readback. Only then does the shared
Ed25519 primitive generate one in-memory seed and CAS0 the fixed three-field
KV document. Independent version1 private readback and final metadata verification
precede the durable success receipt. The seed is never put in argv/env/local
files or output. The receipt carries public key, ASCII-base64url fingerprint,
version, bindings and closed phases only. The old fixture's live-dev-process gate
is preserved; its shared receipt initialization was extracted without weakening
that gate. Python cannot promise seed zeroization.
Every backend/body/close and tool call has a two-second absolute budget and
1MiB combined output bound. The complete operation is bounded by the supplied
fresh expiry. CLI validation and runtime failures remain silent. Existing0600/
0700 no-follow/ownership checks, exclusive creation, fsync/atomic replacement and
directory fsync retain evidence. Same-UID hostile code is not isolated by modes;
Warden's contained home, trusted runtime and independently reviewed custody
location are required, not inferred from approval frequency or shell prefixes.
## Process loss and independent read-only recovery
Any failed/ambiguous metadata/write/readback/final receipt outcome remains
unconfirmed. A success receipt is withheld until final metadata verification.
A child death immediately after the real native CAS leaves the earlier public
intent with its derived public key and no private value. A fresh `recover`
admission must bind that exact original receipt SHA and the same immutable scope.
It reserves a new durable recovery intent before private reads, verifies current
version1 metadata and exact private seed/public/kid correspondence, and records
separate public reconciliation. It never creates a writer, generates a seed,
POSTs metadata/data, restores a seed, consumes approval or dispatches business
operations. Original unknown evidence is never rewritten. Changed versions,
damaged/typed-invalid receipts, missing fresh reviews and denied reads refuse;
replaying a recovery ID cannot repeat even the private backend read.
This source implements bootstrap and read-only version1 reconciliation only.
Rotation needs a separately reviewed exact-version native action; the disposable
primitive's CAS1 test is not a native rotation grant. No automatic retry, remint,
rollback or general recovery API exists. Metadata-only setup uncertainty without
a public seed intent requires separate attended reconciliation, not this path.
Access observed zero PVCs in flex-auth. Its proposed new1Gi/local-path outbox,
PV Retain/reclaim disposition and runtime process-lock/capacity/startup bounds
remain separate reviews. This writer grants no PVC, audit egress, policy role,
SecretStore/Secret or producer/consumer deployment admission.
## R2 phase-aware recovery and source lineage
Frozen R1's metadata-only crash counterexample remains valid historical evidence.
R2 accepts a strict original metadata-setup receipt with no public key, or a
seed-cas receipt with a canonical derived public key. Fresh recovery reserves
durable evidence before target reads. Exact current metadata absence AND exact
version1 data absence yields `current-custody-absent`. Exact configured metadata
with current_version0 AND exact version1 data absence yields
`metadata-only-confirmed`. Both results have no public key/fingerprint/KV version,
key_custody_verified=false, historical_effect=unresolved, original_unknown_preserved,
and an explicit fresh independently reviewed action requirement. A locally
generated but unstored seed's public key is not promoted into public admission.
Neither observation means the original attempt historically made no effects.
Denied/malformed/misleading404 metadata or data, timeout, changed configuration,
unexpected versions, or version1 after a metadata-only/no-public phase keep the
new recovery attempt unconfirmed. They never grant generation, write, retry,
remint, consume or business authority. A subsequent metadata-only bootstrap is
not silently enabled: existing metadata still refuses CAS0 bootstrap admission.
A distinct exact reviewed action/source revision must choose any cleanup/reuse
or generation step. The original receipt remains byte-for-byte unchanged.
Native plan bootstrap.source_primitive_sha256 now equals the actual shared
primitive in the admitted source bundle and writer receipt; checked_sources
enforces the join. The prior8ed pin is explicitly historical. The plan pins the
shared primitive, not itself or the complete native executor, so no self-hash
cycle exists. The exact native executor is independently pinned by source_bundle
and its digest in bindings. A recover admission must additionally supply
original_execution (original receipt bindings, writer SHA and CCR SHA). Fresh
owner reviews bind both original and current lineages. This can describe an older
executed source/proposal without labeling it current. Image/deployment/policy/
consumer trust bindings must still match; recovery cannot change that authority.
## Exact120-second expiry and attended scheduling assessment
The unchanged validate_admission rule requires an absolute UTC expires_at with
5 < expires_at-now <=120seconds, and the entire child runs beneath that fixed
remaining absolute/monotonic budget. The five external reviews cover the scope
digest including expires_at. No deadline is generated, refreshed or widened by
the child; rechecking cannot extend it.
Existing ops-warden proxy.proxy_attended_login_exec runs the OIDC client before the child.
Its _contained_run captures output through subprocess.run without a timeout.
Therefore neither OIDC/MFA nor five external reviews have a demonstrated bound
that fits120seconds from preparing an admission. Creating a now+120seconds
admission before unbounded review/login is not a reliable operational ceremony.
Source success tests start from synthetic ready identities; they prove no human
OIDC/review timing SLO.
A coherent ceremony under the current rule is to pre-review one exact future
absolute expiry/window, including all artifact/source/image/trust/identity and
review references, then arrange attended login so the child starts within its
implicit [expires_at-120seconds, expires_at-5seconds) interval. Review may occur
before that interval; it does not change runtime authority. The founder must aim
for the beginning of the interval, not its last five seconds. The fixed path has
26 bounded native/tool requests before success (52seconds at their2second caps),
plus key/source/receipt work. Its expiry still wins if those operations cannot
finish; no completion or filesystem timing guarantee is inferred.
If login completes too early/late, a review changes any bytes, or adequate time
remains uncertain, refuse this revision. A new expires_at changes the approved
scope digest and needs explicit dispositions for the new revision; existing
source-only reviews cannot be copied into native approval. Do not silently
refresh after OIDC, reinterpret expiry as start-only, widen120seconds, skip
reviews or start a general window-grant service. An alternative authority/window
contract would itself require separate owner/founder design and is not supplied
by R2. Current native admission remains unapproved.

View file

@ -0,0 +1,152 @@
{
"schema": "railiance-platform.signer-native-action-proposal.v1",
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-20261016-R1",
"status": "prepared-unapproved",
"read_ccr": "CCR-2026-0050",
"proposal_scope": "attended bootstrap of retained lifecycle PDP seed; no taskpilot enablement, rotation requires a new exact-version action revision",
"actor": "founder attended OIDC via warden openbao-platform-admin-login; reviewed silent Platform custody child",
"executor_ownership": "railiance-platform; founder executes exact child; agent receives closed metadata only",
"abort_owner": "founder; Platform preserves unknown custody/evidence, Access keeps signer/consumers fenced",
"target": {
"cluster": "primary/railiance01",
"kube_system_uid": "a553c742-0115-43d4-99a4-a5ca56fe0786",
"openbao_cluster_id": "fd28df5d-98ec-57dd-42ec-9b3e4f4e53bf",
"openbao_origin": "http://127.0.0.1:18200",
"namespace": "flex-auth",
"deployment": "flex-auth-secrets-engine",
"signer_sa": "flex-auth-secrets-engine"
},
"credential_route": "openbao-platform-admin-login; flex-auth-decision-envelope-signing-key remains draft/nonresolvable; route is not a seed vending service",
"bootstrap": {
"action": "CAS0 fresh in-memory Ed25519 seed",
"kid": "flex-auth-lifecycle-20261016-r1",
"expected_version": 0,
"data_path": "platform/data/workloads/flex-auth/envelope-signing",
"document_fields": [
"ED25519_PRIVATE_KEY",
"ED25519_PUBLIC_KEY",
"kid"
],
"metadata_path": "platform/metadata/workloads/flex-auth/envelope-signing",
"metadata_contract": {
"cas_required": true,
"max_versions": 2,
"delete_version_after": "0s"
},
"allowed_native_methods": [
{
"method": "POST",
"path": "platform/metadata/workloads/flex-auth/envelope-signing",
"purpose": "CAS-required/version-retention configuration after absence/no-drift validation"
},
{
"method": "GET",
"path": "platform/metadata/workloads/flex-auth/envelope-signing",
"purpose": "exact metadata readback, no private value output"
},
{
"method": "POST",
"path": "platform/data/workloads/flex-auth/envelope-signing",
"purpose": "single CAS0 seed document write"
},
{
"method": "GET",
"path": "platform/data/workloads/flex-auth/envelope-signing?version=1",
"purpose": "exact private readback in custody, closed public receipt only"
}
],
"source_primitive_sha256": "sha256:f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c",
"historical_source_primitive_sha256": "sha256:8ed281a49c1f1cdb23772efe3593e75c62572a12b8d7a6a4c574f27b38722e75",
"source_lineage_rule": "source_primitive_sha256 must equal current admitted source_bundle[scripts/flex_auth_signer_custody.py] and exact writer receipt; historical_source_primitive_sha256 is non-authoritative R1 evidence only"
},
"fencing": {
"before_key_generation": [
"Verify cluster/backend IDs, exact current source/CCR and founder disposition hashes.",
"Read exact deployment/ReplicaSets/pods by ownerReference and SA; refuse a second workload/pod using signer SA or selecting signer Secret.",
"Access stops protected consumers and scales retained deployment0 through its reviewed release path; this child does not mutate replicas.",
"Verify deployment spec replicas0, observed generation caught up, no live or terminating owned ReplicaSet/pods, no matching signer-SA pods and no HPA targeting deployment.",
"Reject a differing/previously existing KV entry at bootstrap. Hold exclusive owner action receipt lock and recheck fence immediately before CAS."
],
"after_cas": "Keep consumers/signer stopped until independent public admission and exact ESO/readback/source/image verification. Recreate1 is Access-owned; any drift or uncertain outcome keeps fence closed. Access reports current outbox lacks OS process lock, byte ceiling and bounded startup/compaction; Recreate1 alone is not bounded durable acceptance. Keep protected acceptance closed until reviewed source hardening or explicit bounded attended fixture disposition.",
"concurrency": "Single attended owner window/exclusive receipt; GitOps/Helm autosync/other writers must be paused by Access/cluster owner under distinct reviewed action. Plan is not a Kubernetes atomic fence or same-UID isolation."
},
"protected_adapter": {
"state": "implementation/independent tests still required before executable approval; fixture code must never be promoted by removing its dev-process check",
"reuse": [
"scripts/state_hub_preflight_lane.py attended silent child/cluster/platform-admin/fencing patterns",
"scripts/flex_auth_signer_custody.py protected Ed25519 CAS/readback and receipt state machine",
"scripts/task_execution_native_fixture.py absolute main-thread2s I/O budget",
"scripts/task_execution_recovery.py strict bounded JSON/no-follow/private custody patterns"
],
"requirements": [
"New fixed native adapter consumes exact approved CCR/source/native-action digest; cannot accept arbitrary URL/path/command/payload/seed.",
"Read OIDC token only within warden child from attended context; require platform-admin and reject root; never print/store bearer. No credential comes back to agent.",
"Pin exact kube-system/OpenBao cluster IDs before private calls, refuse ambient address override; enforce2s absolute reads and1MiB cap; no redirect/shell/tracing.",
"Verify metadata absence/exact admitted values before CAS-required change; any differing existing object refuses. Exact response contracts and duplicate/nonfinite JSON rejection.",
"Private seed is generated once, never imported or persisted outside OpenBao; no subprocess argv/env seed; readback compares exact version/fields and derived public correspondence.",
"Receipt intent is durable before native effect; final success requires exact KV/metadata correspondence and receipt fsync. Process loss/error remains unknown without remint/retry."
]
},
"receipts": {
"directory": "/home/worsch/.local/state/railiance-platform/flex-auth-envelope-signing",
"mode_directory": "0700",
"mode_file": "0600",
"native_execution_location": "founder workstation inside reviewed warden child; no agent-owned shared /tmp custody",
"basename": "RPF35-PDP-SIGNER-NATIVE-20261016-R1.json",
"maximum_bytes": 65536,
"atomic": "exclusive initial receipt, fsync-before-effect, atomic replacement and directory fsync, no-follow/owner/mode/nlink validation",
"public_fields": [
"proposal/executed source/CCR/identity/cluster/image/policy/trust digests",
"actor/time",
"closed phase/status",
"exact KV path/version/kid",
"public key and explicitly encoded fingerprint",
"fence/metadata/readback checks",
"rollback or unknown recovery disposition"
],
"fingerprint": "sha256: lowercase SHA256 of canonical unpadded base64url public-key ASCII, exactly43bytes; independent Secrets owner agreement before admission",
"delivery": "Founder/Platform separately authenticate metadata receipt to Secrets owner; public receipt shape/file does not create consumer trust; raw private receipt directory is not a shared agent intake"
},
"rollback": {
"before_cas": "Abort without private write; retain receipt/fence; restore prior Access image/values only via reviewed Access action.",
"after_cas_unknown": "Retain unknown and fence; independently attended owner reconciles exact metadata/version/public correspondence. Never rerun CAS0 or restore prior seed automatically.",
"compromise": "Remove online old public key immediately; stop signer, independent new reviewed forward-only action; old private seed never restored."
},
"requested_scope": {
"read_lane": "Exact CCR0050 policy/role/delivery source only after separate founder review. No extra ESO RBAC permitted.",
"custody_writer": "One attended exact-target CAS0 plus fixed KV metadata setup/readback; no standing writer grant or broker role. This authority is separate from read CCR.",
"kubernetes": "Metadata-only fence observations by child; Access/cluster separately approve stop/restart/delivery manifests and sync control.",
"new_infrastructure_spend_eur": 0,
"new_resources": "Platform creates no storage resources. Access reports zero flex-auth PVCs; Access proposes separate exact new flex-auth-secrets-engine-outbox1Gi local-path Helmkeep. Provisioning, PV reclaim/Delete/WaitForFirstConsumer retention and runtime log bounds require separate founder/cluster/Access review; no durable outbox acceptance yet.",
"risk_bound": "Controlled loss of retained PDP availability during explicit attended fence; seed compromise can forge lifecycle decisions until independent public trust withdrawal; founder must approve this concrete high-risk scope."
},
"reviews_required": [
{
"role": "railiance-platform-owner",
"disposition": "pending exact source/artifact review; no approval recorded"
},
{
"role": "access-engine-owner",
"disposition": "pending exact source/artifact review; no approval recorded"
},
{
"role": "secrets-engine-owner",
"disposition": "pending exact source/artifact review; no approval recorded"
},
{
"role": "cluster-gitops-owner",
"disposition": "pending exact source/artifact review; no approval recorded"
},
{
"role": "founder",
"disposition": "pending exact source/artifact review; no approval recorded"
}
],
"authority_granted": false,
"candidate_delivery_amendments": {
"supersedes_unadmitted_R3_role": "external-secrets-flex-auth-envelope-signing replaces flex-auth-envelope-signing-eso to fit existing delegated-applier role convention; dedicated SA unchanged",
"read_ccr_field_projection": "Only ED25519_PRIVATE_KEY is delivered; public sibling fields remain visible to dedicated document-reader identity but never become consumer trust discovery",
"projection_source": "docs/credential-lane-designs/flex-auth-envelope-signing-native-projection.yaml.proposed",
"managed_declaration": "Missing rapp declaration remains migration debt; retain actual Access919ce25 workload identity, do not create package tuple"
}
}

View file

@ -0,0 +1,37 @@
# Native signer action preparation
CCR-2026-0050 is a schema-valid proposed ESO read grant. It does not authorize
secret writes. The separate exact native-action JSON requests one attended CAS0
and fixed metadata setup/readback; founder disposition is still pending. No
standing writer/broker grant, private consumer lookup, rapp declaration or policy
change is introduced. Native private custody remains in the founder's reviewed
warden child, not an agent prompt or temporary file.
The JSON fixes the target/cluster/backend, new kid, CAS version, exact native
methods, retention/CAS metadata, existing custody machinery, signer fence,
receipt location/encoding, abort owner and forward-only recovery. It separately
identifies Access/cluster-owned stop/restart/ESO/PVC and Secrets-owned independent
public admission. These owners must review their exact inputs; Platform cannot
sign their dispositions. The program that enforces this native contract still
requires source implementation and independent tests before executable approval.
The existing fixture adapter must not be promoted by deleting its process check.
Source preparation validated the real credential-change schema and generated
HCL. The expected unconfirmed-bound-claims warning and proposed status preserve
apply refusal. No live observation/identity confirmation, credential read, CAS,
policy write, role, Kubernetes or route mutation occurred. Missing managed
package declaration remains explicit migration debt for the retained workload.
The native candidate explicitly amends two unadmitted R3 read-lane choices:
role `external-secrets-flex-auth-envelope-signing` follows the existing delegated
applier convention; read CCR fields names the projected ED25519_PRIVATE_KEY only.
The same document has public sibling fields; document-granular read means ESO
can read those too, so this is not field-level authorization. The sibling native
projection proposal and CCR agree on the new role. Historical R3 is not mutated.
Access metadata observation found zero PVCs in flex-auth. Its exact proposed
new outbox PVC1Gi/local-path is a separate resource/retention scope, with Delete/
WaitForFirstConsumer PV behavior needing explicit treatment. Existing outbox code
lacks process locking/byte ceiling/bounded startup and compaction. Neither
Recreate1 nor Helmkeep supplies these guarantees; production durable acceptance
remains closed until Access-owned hardening and cluster/storage review.

View file

@ -0,0 +1,50 @@
# SOURCE PROPOSAL ONLY: no CCR admission, no apply, no plaintext key.
apiVersion: v1
kind: ServiceAccount
metadata:
name: flex-auth-envelope-signing-eso
namespace: flex-auth
automountServiceAccountToken: false
---
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: openbao-flex-auth-envelope-signing
namespace: flex-auth
spec:
provider:
vault:
server: http://openbao.openbao.svc:8200
path: platform
version: v2
auth:
kubernetes:
mountPath: kubernetes
role: external-secrets-flex-auth-envelope-signing
serviceAccountRef:
name: flex-auth-envelope-signing-eso
audiences: [openbao]
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: flex-auth-secrets-engine-envelope-signing
namespace: flex-auth
spec:
refreshInterval: 5m
secretStoreRef:
kind: SecretStore
name: openbao-flex-auth-envelope-signing
target:
name: flex-auth-secrets-engine-envelope-signing
creationPolicy: Owner
deletionPolicy: Retain
template:
metadata:
annotations:
railiance.io/custody-proposal: RPF35-PDP-SIGNER-NATIVE-20261016-R1
data:
- secretKey: seed
remoteRef:
key: workloads/flex-auth/envelope-signing
property: ED25519_PRIVATE_KEY

View file

@ -0,0 +1,11 @@
path "platform/data/workloads/flex-auth/envelope-signing" {
capabilities = ["read"]
}
path "auth/token/lookup-self" {
capabilities = ["read"]
}
path "auth/token/revoke-self" {
capabilities = ["update"]
}

View file

@ -58,6 +58,10 @@ class SignerWriter:
or '-dev' not in process.args or '-dev-no-store-token' not in process.args
or '-dev-listen-address=127.0.0.1:'+str(admission.port) not in process.args):
raise Refused('live owner-spawned disposable dev process required')
self.admission=admission
self._initialize_receipt(directory,operation_id,kid=kid,expected_version=expected_version,bindings=bindings)
def _initialize_receipt(self,directory,operation_id,*,kid,expected_version,bindings):
if (type(operation_id) is not str or not re.fullmatch(r'[a-zA-Z0-9_-]{1,80}',operation_id)
or type(kid) is not str or not re.fullmatch(r'[a-zA-Z0-9_-]{1,80}',kid)
or type(expected_version) is not int or not 0<=expected_version<2**31
@ -66,7 +70,7 @@ class SignerWriter:
raise Refused('exact signer operation contract required')
if directory.absolute()!=directory.resolve() or directory.is_symlink():raise Refused('indirect receipt custody refused')
Journal._private(directory.stat(),directory=True)
self.admission=admission;self.directory=directory;self.path=directory/(operation_id+'.json')
self.directory=directory;self.path=directory/(operation_id+'.json')
self.expected_version=expected_version;self.kid=kid;self.attempted=False
self.receipt={'schema':'railiance-platform.signer-cas-receipt.v1','operation_id':operation_id,
'bindings':dict(bindings),'writer_sha256':'sha256:'+hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),

View file

@ -0,0 +1,417 @@
#!/usr/bin/env python3
"""Silent attended retained-lifecycle signer custody; never a credential front door.
Invoke only through the reviewed warden platform-admin envelope. The externally
reviewed admission SHA is an exact-command input, not approval by JSON shape.
No private value crosses stdout/stderr, argv, receipt or persistent local storage.
"""
from __future__ import annotations
import argparse
import base64
from datetime import datetime,timezone
import fcntl
import hashlib
import hmac
import http.client
import importlib.util
import json
import os
from pathlib import Path
import re
import selectors
import shlex
import subprocess
import sys
import time
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from flex_auth_signer_custody import SignerWriter,PATH,FIELDS,BINDINGS,encoded,b64
from task_execution_journal import Journal,Refused
from task_execution_native_fixture import _absolute_budget
from task_execution_recovery import _json_unique,_invalid_constant,_policy_absent,_same_policy
ROOT=Path(__file__).resolve().parents[1]
META='platform/metadata/workloads/flex-auth/envelope-signing'
CCR='credential-change-requests/CCR-2026-0050-flex-auth-envelope-signing.yaml'
PLAN='docs/credential-lane-designs/flex-auth-envelope-signing-native-plan.json'
CLUSTER='a553c742-0115-43d4-99a4-a5ca56fe0786'
BAO_CLUSTER='fd28df5d-98ec-57dd-42ec-9b3e4f4e53bf'
NAME='flex-auth-secrets-engine'
SECRET='flex-auth-secrets-engine-envelope-signing'
ROLE='external-secrets-flex-auth-envelope-signing'
LIFECYCLE='sha256:bd11c5fe77ce6439c65fea225ad6b71d2110efc5e7b5bc9b499c59cd0a53b8b4'
REVIEWS={'founder','railiance-platform-owner','access-engine-owner','secrets-engine-owner','cluster-gitops-owner'}
SOURCE_FILES={'scripts/flex_auth_signer_native.py','scripts/flex_auth_signer_custody.py','scripts/task_execution_native_fixture.py','scripts/task_execution_journal.py','scripts/task_execution_recovery.py','scripts/credential-change.py'}
def require(ok,reason):
if not ok:raise Refused(reason)
def digest(raw):return 'sha256:'+hashlib.sha256(raw).hexdigest()
def is_digest(value):return type(value) is str and re.fullmatch(r'sha256:[0-9a-f]{64}',value) is not None
def private_read(path,limit=65536):
path=Path(path)
require(path.is_absolute() and path.absolute()==path.resolve() and not path.is_symlink(),'indirect private input')
Journal._private(path.parent.stat(),directory=True)
fd=os.open(path,os.O_RDONLY|os.O_NOFOLLOW|os.O_NONBLOCK)
try:
before=os.fstat(fd);Journal._private(before)
raw=os.read(fd,limit+1);after=os.fstat(fd)
require((before.st_dev,before.st_ino,before.st_size,before.st_mtime_ns,before.st_ctime_ns)==
(after.st_dev,after.st_ino,after.st_size,after.st_mtime_ns,after.st_ctime_ns)
and len(raw)==before.st_size and len(raw)<=limit,'private input changed or oversized')
return raw
finally:os.close(fd)
def parsed(raw):
try:return json.loads(raw,object_pairs_hook=_json_unique,parse_constant=_invalid_constant)
except Exception:raise Refused('invalid closed JSON input') from None
def validate_admission(value):
fields={'schema','action','operation_id','kid','expires_at','founder_entity_id','ccr_sha256','source_bundle','bindings',
'producer_image','caller_binding','deployment_projection_sha256','authority','routing','owner_reviews',
'exclusive_window_sha256','consumer_trust_sha256','original_receipt_sha256','original_execution'}
require(type(value) is dict and set(value)==fields,'exact admission schema required')
require(value['schema']=='railiance-platform.signer-native-admission.v1' and value['action'] in {'bootstrap','recover'},'native admission action refused')
require(type(value['operation_id']) is str and re.fullmatch(r'[A-Za-z0-9_-]{1,80}',value['operation_id']) and
type(value['kid']) is str and re.fullmatch(r'[A-Za-z0-9_-]{1,80}',value['kid']),'native operation identity required')
require(type(value['founder_entity_id']) is str and re.fullmatch(r'[a-f0-9-]{36}',value['founder_entity_id']),'explicit attended identity required')
require(all(is_digest(value[x]) for x in ['ccr_sha256','deployment_projection_sha256','exclusive_window_sha256','consumer_trust_sha256']),'explicit custody/image/trust selections required')
require(type(value['producer_image']) is str and re.fullmatch(r'forgejo\.coulomb\.social/coulomb/flex-auth@sha256:[a-f0-9]{64}',value['producer_image']),'immutable producer image required')
require(value['caller_binding']=='secrets-engine=system:serviceaccount:secrets-engine:secrets-engine','retained caller binding required')
require(value['authority']=='founder-attended-exact-source-scope; no standing grant','independent attended authority required')
route=value['routing'];require(type(route) is dict and set(route)=={'lane_id','verdict','founder_act','receipt_sha256'} and
route['lane_id']=='openbao-platform-admin-login' and route['verdict']=='founder_required' and route['founder_act']=='oidc_login' and is_digest(route['receipt_sha256']),'routing mismatch refused')
reviews=value['owner_reviews'];require(type(reviews) is dict and set(reviews)==REVIEWS,'distinct owner reviews required')
for role,review in reviews.items():
require(type(review) is dict and set(review)=={'approved_admission_sha256','approval_ref'} and
is_digest(review['approved_admission_sha256']) and type(review['approval_ref']) is str and
0<len(review['approval_ref'])<=200,'exact external review required')
# Dispositions bind a claim-free scope digest, avoiding self-referential JSON.
scope={k:v for k,v in value.items() if k!='owner_reviews'};scope_digest=digest(encoded(scope))
require(all(x['approved_admission_sha256']==scope_digest for x in reviews.values()),'owner review scope mismatch')
require(type(value['source_bundle']) is dict and set(value['source_bundle'])==SOURCE_FILES and
all(is_digest(x) for x in value['source_bundle'].values()),'exact execution source bundle required')
require(type(value['bindings']) is dict and set(value['bindings'])==BINDINGS and all(is_digest(x) for x in value['bindings'].values()),'exact execution bindings required')
require(value['bindings']['policy_sha256']==LIFECYCLE and value['bindings']['image_sha256']=='sha256:'+value['producer_image'].rsplit('sha256:',1)[1]
and value['bindings']['trust_sha256']==value['consumer_trust_sha256']
and value['bindings']['deployment_sha256']==value['deployment_projection_sha256']
and value['bindings']['executed_revision_sha256']==digest(encoded(value['source_bundle'])),'immutable lifecycle/image/trust/source mismatch')
require((value['action']=='bootstrap' and value['original_receipt_sha256'] is None) or
(value['action']=='recover' and is_digest(value['original_receipt_sha256'])),'separate recovery disposition required')
original=value['original_execution']
if value['action']=='bootstrap':require(original is None,'bootstrap cannot import execution lineage')
else:
require(type(original) is dict and set(original)=={'bindings','writer_sha256','source_ccr_sha256'} and
is_digest(original['writer_sha256']) and is_digest(original['source_ccr_sha256']) and
type(original['bindings']) is dict and set(original['bindings'])==BINDINGS and
all(is_digest(x) for x in original['bindings'].values()),'independently reviewed original execution lineage required')
require(all(original['bindings'][key]==value['bindings'][key] for key in BINDINGS-{'proposal_sha256','executed_revision_sha256'}),
'recovery cannot change original image/deployment/policy/trust authority')
try:expires=datetime.fromisoformat(value['expires_at'].replace('Z','+00:00'));remaining=(expires-datetime.now(timezone.utc)).total_seconds()
except Exception:raise Refused('invalid authority expiry') from None
require(expires.utcoffset().total_seconds()==0 and 5<remaining<=120,'bounded fresh attended window required')
return remaining
class NativeIO:
"""All tools capture/discard private bytes; fixed transport and no shell."""
def captured(self,argv,payload=None,*,allowed=(0,)):
env=os.environ.copy();env.update(BAO_ADDR='http://127.0.0.1:18200',VAULT_ADDR='http://127.0.0.1:18200')
for key in ('BAO_LOG_LEVEL','VAULT_LOG_LEVEL'):
require(env.get(key,'').lower() not in {'debug','trace'},'verbose custody environment refused')
# A hostile inherited CLI config is not an alternate identity/endpoint.
require(not env.get('BAO_NAMESPACE') and not env.get('VAULT_NAMESPACE') and not env.get('VAULT_SKIP_VERIFY'),'ambient backend override refused')
proc=subprocess.Popen(argv,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,env=env)
try:
if payload is not None:proc.stdin.write(encoded(payload));proc.stdin.flush()
proc.stdin.close()
sel=selectors.DefaultSelector();data={proc.stdout:bytearray(),proc.stderr:bytearray()}
try:
for stream in data:sel.register(stream,selectors.EVENT_READ)
deadline=time.monotonic()+2
while sel.get_map():
remaining=deadline-time.monotonic();require(remaining>0,'absolute custody command timeout')
for item,_ in sel.select(remaining):
chunk=os.read(item.fileobj.fileno(),65536)
if not chunk:sel.unregister(item.fileobj)
else:
data[item.fileobj].extend(chunk)
require(sum(map(len,data.values()))<=1048576,'custody command output bound')
code=proc.wait(timeout=max(.001,deadline-time.monotonic()))
require(code in allowed,'custody command unconfirmed')
return code,bytes(data[proc.stdout])
finally:sel.close()
except BaseException:
proc.kill();proc.wait(timeout=2);raise Refused('custody command unconfirmed') from None
finally:
proc.stdout.close();proc.stderr.close()
def bao(self,args,payload=None,allowed=(0,)):
with _absolute_budget(2):
code,raw=self.captured(['bao',*args],payload,allowed=allowed)
return code,parsed(raw)
def kube(self,resource,name=None):
args=['kubectl','--request-timeout=2s']
if resource!='namespace':args+=['-n','flex-auth']
args+=['get',resource]
if name:args.append(name)
args+=['-o','json']
with _absolute_budget(2):
_,raw=self.captured(['ssh','-o','BatchMode=yes','-o','StrictHostKeyChecking=yes','-o','ConnectTimeout=2','railiance01',shlex.join(args)])
return parsed(raw)
def http(self,method,path,body=None):
require(method in {'GET','POST'} and path in {META,PATH,PATH+'?version=1'},'fixed custody path required')
require(type(getattr(self,'_token',None)) is str and self._token,'contained attended session required')
with _absolute_budget(2):
conn=http.client.HTTPConnection('127.0.0.1',18200,timeout=2)
try:
conn.request(method,'/v1/'+path,body=None if body is None else encoded(body),
headers={'X-Vault-Token':self._token,'Content-Type':'application/json'})
response=conn.getresponse();raw=response.read(1048577)
require(len(raw)<=1048576,'custody response bound')
return response.status,parsed(raw) if raw else {}
finally:conn.close()
def deployment_projection(deployment):
spec=deployment['spec']['template']['spec'];containers=spec.get('containers',[])
require(type(containers) is list and len(containers)==1 and not spec.get('initContainers'),'single fixed signer container required')
item=containers[0]
# Never hash arbitrary env values or an entire Secret/spec response.
return {'service_account':spec.get('serviceAccountName'),'image':item.get('image'),
'args':item.get('args'),'volume_mounts':item.get('volumeMounts',[]),
'volumes':spec.get('volumes',[]),'security_context':spec.get('securityContext')}
def fence(io,contract):
namespace=io.kube('namespace','kube-system')
require(namespace.get('metadata',{}).get('uid')==CLUSTER,'wrong target cluster')
deployment=io.kube('deployment',NAME);spec=deployment.get('spec',{});meta=deployment.get('metadata',{})
require(type(spec.get('replicas')) is int and spec['replicas']==0 and
deployment.get('status',{}).get('observedGeneration',-1)==meta.get('generation') and
not deployment.get('status',{}).get('replicas',0),'all signer replicas must be stopped')
projection=deployment_projection(deployment)
require(digest(encoded(projection))==contract['deployment_projection_sha256'] and
projection['service_account']==NAME and projection['image']==contract['producer_image'],'fenced deployment source/image mismatch')
args=projection['args'];require(type(args) is list and all(type(x) is str for x in args),'exact signer args required')
def option(flag):
require(args.count(flag)==1 and args.index(flag)+1<len(args),'fixed signer flag required')
return args[args.index(flag)+1]
require(option('--caller-auth-mode')=='enforce' and option('--caller-audience')=='flex-auth' and
option('--caller-binding')==contract['caller_binding'],'retained caller guard required')
require(not any(x.startswith(flag+'=') for x in args for flag in ['--caller-auth-mode','--caller-binding','--caller-audience','--envelope-signing-key-file','--envelope-signing-kid']),'ambiguous protected args refused')
require(option('--envelope-signing-key-file')=='/var/run/secrets/decision-envelope-signing/seed' and
option('--envelope-signing-kid')==contract['kid'],'exact seed mount and startup kid required')
volumes=projection['volumes'];mounts=projection['volume_mounts']
matching=[x for x in volumes if x.get('name')=='flex-auth-envelope-signing']
require(len(matching)==1 and matching[0].get('secret')=={'secretName':SECRET,'defaultMode':288,'items':[{'key':'seed','path':'seed'}]},'exact seed volume required')
require(any(x=={'name':'flex-auth-envelope-signing','mountPath':'/var/run/secrets/decision-envelope-signing','readOnly':True} for x in mounts),'readonly seed mount required')
uid=meta.get('uid');require(type(uid) is str and uid,'deployment UID required')
sets=io.kube('replicasets').get('items');pods=io.kube('pods').get('items');hpas=io.kube('hpa').get('items')
require(type(sets) is list and type(pods) is list and type(hpas) is list,'complete fence observations required')
rsids=[]
for item in sets:
if any(x.get('uid')==uid for x in item.get('metadata',{}).get('ownerReferences',[])):
require(type(item.get('spec',{}).get('replicas')) is int and item['spec']['replicas']==0 and
type(item.get('status',{}).get('replicas',0)) is int and not item.get('status',{}).get('replicas',0),'owned ReplicaSet still running')
rsids.append(item['metadata']['uid'])
for item in pods:
podspec=item.get('spec',{})
uses_secret=any(x.get('secret',{}).get('secretName')==SECRET for x in podspec.get('volumes',[]))
owned=any(x.get('uid') in [uid,*rsids] for x in item.get('metadata',{}).get('ownerReferences',[]))
require(not owned and podspec.get('serviceAccountName')!=NAME and not uses_secret,'live or terminating signer pod refused')
require(not any(x.get('spec',{}).get('scaleTargetRef',{}).get('name')==NAME for x in hpas),'signer autoscaler refused')
return True
def checked_sources(contract):
for name,pin in contract['source_bundle'].items():
path=ROOT/name
require(path.resolve()==path.absolute() and not path.is_symlink() and path.stat().st_mode&0o022==0
and path.is_file() and path.stat().st_size<=1048576 and digest(path.read_bytes())==pin,'exact admitted execution source required')
planraw=(ROOT/PLAN).read_bytes();require(digest(planraw)==contract['bindings']['proposal_sha256'],'exact native action proposal required')
plan=parsed(planraw);require(plan.get('bootstrap',{}).get('source_primitive_sha256')==contract['source_bundle']['scripts/flex_auth_signer_custody.py'],'proposal executed primitive lineage mismatch')
require(plan.get('proposal_id')=='RPF35-PDP-SIGNER-NATIVE-20261016-R1' and plan.get('bootstrap',{}).get('kid')==contract['kid'],'exact proposed bootstrap kid required')
ccrraw=(ROOT/CCR).read_bytes();require(digest(ccrraw)==contract['ccr_sha256'],'reviewed read CCR source mismatch')
spec=importlib.util.spec_from_file_location('native_signer_ccr',ROOT/'scripts/credential-change.py');module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
ccr,errors,_=module.validate_ccr(ROOT/CCR)
require(not errors and ccr['status'] in {'approved','applied','verified','active'} and
ccr['openbao']['auth']['bound_claims_confirmed'] is True,'approved exact read CCR required')
scope=ccr['openbao'];require(scope['kv_path']=='platform/workloads/flex-auth/envelope-signing' and
scope['fields']==['ED25519_PRIVATE_KEY'] and scope['metadata_read'] is False and scope['policy_name']=='workload-kv-read-flex-auth-envelope-signing' and
scope['auth']['role']==ROLE and scope['auth']['bound_claims']=={'service_account_names':['flex-auth-envelope-signing-eso'],'service_account_namespaces':['flex-auth']},'read scope correspondence refused')
require((ROOT/scope['policy_file']).read_text()==module.generated_policy_hcl(ccr),'read policy source drift')
def attended(io,contract):
_,status=io.bao(['status','-format=json']);require(status.get('cluster_id')==BAO_CLUSTER and status.get('sealed') is False,'backend cluster pin required')
_,lookup=io.bao(['token','lookup','-format=json']);who=lookup.get('data',{})
require('platform-admin' in who.get('policies',[]) and 'root' not in who.get('policies',[]) and
type(who.get('path')) is str and who['path'].startswith('auth/netkingdom/') and who.get('entity_id')==contract['founder_entity_id'],'exact attended non-root founder required')
_,audit=io.bao(['audit','list','-format=json']);require(type(audit) is dict and 0<len(audit)<=32,'enabled backend audit required')
# Token lookup-self is inside the contained warden child, never an agent
# retrieval. Its identifier stays in custody-process memory for fixed API I/O.
token=who.get('id');require(type(token) is str and 0<len(token)<=4096,'contained session handle unavailable')
io._token=token
_,policy=io.bao(['read','-format=json','sys/policies/acl/workload-kv-read-flex-auth-envelope-signing'])
expected_policy=(ROOT/'openbao/policies/workload-kv-read-flex-auth-envelope-signing.hcl').read_text()
require(_same_policy(policy.get('data',{}).get('policy'),expected_policy),'native reader policy drift')
_,role=io.bao(['read','-format=json','auth/kubernetes/role/'+ROLE]);role=role.get('data',{})
required={'bound_service_account_names':['flex-auth-envelope-signing-eso'],
'bound_service_account_namespaces':['flex-auth'],'bound_service_account_namespace_selector':'',
'audience':'openbao','token_policies':['workload-kv-read-flex-auth-envelope-signing'],
'token_ttl':900,'token_max_ttl':900,'token_explicit_max_ttl':900,'token_no_default_policy':True,
'token_period':0,'token_num_uses':0,'token_bound_cidrs':[]}
require(all(type(role.get(key)) is type(expected) and role.get(key)==expected for key,expected in required.items()),'native delivery role drift')
class NativeWriter(SignerWriter):
def __init__(self,io,contract,directory):
self.io=io;self.contract=contract;self.final_ready=False
self._initialize_receipt(directory,contract['operation_id'],kid=contract['kid'],expected_version=0,bindings=contract['bindings'])
def _save(self,*,initial=False):
if not self.final_ready and self.receipt['status']=='custody-verified-pending-independent-public-admission':
self.receipt['status']='seed-verified-pending-metadata'
self.receipt.update(actor='founder-attended-platform-admin',native_custody=True,
admission_sha256=digest(encoded(self.contract)),source_ccr_sha256=self.contract['ccr_sha256'])
return super()._save(initial=initial)
def _http(self,method,path,body=None):
if method=='POST':
validate_admission(self.contract);checked_sources(self.contract);fence(self.io,self.contract)
return self.io.http(method,path,body)
def exact_metadata(status,value,*,created):
require(status==200 and type(value) is dict and 'errors' not in value and type(value.get('data')) is dict,'metadata readback unconfirmed')
data=value['data'];require(type(data.get('cas_required')) is bool and data['cas_required'] is True and
type(data.get('max_versions')) is int and data['max_versions']==2 and data.get('delete_version_after')=='0s' and
type(data.get('current_version')) is int and data['current_version']==(1 if created else 0),'metadata contract drift')
def reconcile(io,contract,directory,original_path):
# A fresh independently approved recover action may read/reconcile only;
# original write receipt remains untouched and no writer/keygen is created.
raw=private_read(original_path);require(digest(raw)==contract['original_receipt_sha256'],'exact original receipt required')
receipt=parsed(raw)
receipt_fields={'schema','operation_id','bindings','writer_sha256','actor','created_at','path','expected_version','kid','status','production_admission','rollback','public_key','public_fingerprint','kv_version','exact_readback','native_custody','admission_sha256','source_ccr_sha256','phase'}
require(type(receipt) is dict and set(receipt)==receipt_fields and receipt.get('schema')=='railiance-platform.signer-cas-receipt.v1' and
receipt.get('status') in {'write-intent-unknown','seed-verified-pending-metadata'} and receipt.get('path')==PATH and
receipt.get('kid')==contract['kid'] and type(receipt.get('expected_version')) is int and receipt['expected_version']==0 and
(receipt.get('kv_version') is None or (type(receipt['kv_version']) is int and receipt['kv_version']==1)) and
type(receipt.get('exact_readback')) is bool and receipt.get('production_admission') is False and
receipt.get('source_ccr_sha256')==contract['original_execution']['source_ccr_sha256'] and receipt.get('writer_sha256')==contract['original_execution']['writer_sha256'] and
receipt.get('native_custody') is True and receipt.get('bindings')==contract['original_execution']['bindings'] and
receipt.get('phase') in {'metadata-setup','seed-cas'},'original custody receipt mismatch')
has_public=(type(receipt.get('public_key')) is str and re.fullmatch(r'[A-Za-z0-9_-]{43}',receipt['public_key']) is not None
and receipt.get('public_fingerprint')==digest(receipt['public_key'].encode()))
no_public=(receipt.get('public_key') is None and receipt.get('public_fingerprint') is None)
require((receipt['phase']=='metadata-setup' and no_public and receipt['kv_version'] is None and receipt['exact_readback'] is False)
or (receipt['phase']=='seed-cas' and has_public),'original phase/public lineage mismatch')
# Reserve recovery evidence before private native reads. A crash or failure
# leaves this independent attempt unconfirmed and cannot replay its ID.
recovery={'schema':'railiance-platform.signer-cas-recovery.v1','operation_id':contract['operation_id'],
'original_receipt_sha256':digest(raw),'original_execution':contract['original_execution'],
'admission_sha256':digest(encoded(contract)),
'status':'recovery-read-intent-unconfirmed','original_phase':receipt['phase'],
'kid':contract['kid'],'kv_version':None,'public_key':None,'public_fingerprint':None,
'key_custody_verified':False,'historical_effect':'unresolved',
'next_action':'fresh independently reviewed exact action required; no generation/write/retry authority',
'no_write_or_remint':True,'original_unknown_preserved':True}
store=object.__new__(SignerWriter);store.directory=directory;store.path=directory/(contract['operation_id']+'.json');store.receipt=recovery
store._save(initial=True)
status,value=io.http('GET',META)
if _policy_absent(status,value):
absent=True
else:
require(status==200 and type(value) is dict and 'errors' not in value and type(value.get('data')) is dict,'recovery metadata unconfirmed')
current=value['data'].get('current_version')
require(type(current) is int and current in {0,1},'recovery metadata version ambiguous')
exact_metadata(status,value,created=current==1)
absent=False
if current==1:require(has_public and receipt['phase']=='seed-cas','unexpected key custody after metadata-only phase')
if absent or current==0:
status,value=io.http('GET',PATH+'?version=1')
require(_policy_absent(status,value),'recovery data absence unconfirmed')
store.receipt.update(status='current-custody-absent' if absent else 'metadata-only-confirmed')
store._save()
return dict(store.receipt)
status,value=io.http('GET',PATH+'?version=1');require(status==200 and type(value) is dict and 'errors' not in value,'recovery private readback unconfirmed')
data=value.get('data',{});document=data.get('data');metadata=data.get('metadata',{})
require(type(document) is dict and set(document)==FIELDS and all(type(x) is str for x in document.values()) and
type(metadata.get('version')) is int and metadata['version']==1 and document['kid']==contract['kid'],'recovery version/shape mismatch')
try:
seed=base64.urlsafe_b64decode(document['ED25519_PRIVATE_KEY']+'=')
require(len(seed)==32 and b64(seed)==document['ED25519_PRIVATE_KEY'],'noncanonical recovery seed')
public=b64(Ed25519PrivateKey.from_private_bytes(seed).public_key().public_bytes(serialization.Encoding.Raw,serialization.PublicFormat.Raw))
except Exception:raise Refused('recovery key correspondence unconfirmed') from None
require(hmac.compare_digest(public,document['ED25519_PUBLIC_KEY']) and hmac.compare_digest(public,receipt['public_key']) and
receipt.get('public_fingerprint')==digest(public.encode()),'recovery public correspondence unconfirmed')
store.receipt.update(status='custody-reconciled-pending-independent-public-admission',kv_version=1,public_key=public,public_fingerprint=digest(public.encode()),key_custody_verified=True)
store._save()
return dict(store.receipt)
def execute(contract,directory,*,io=None,original_receipt=None):
remaining=validate_admission(contract);checked_sources(contract)
require(isinstance(directory,Path) and directory.absolute()==directory.resolve(),'exact private receipt directory required')
Journal._private(directory.stat(),directory=True)
plan=parsed((ROOT/PLAN).read_bytes())
require(str(directory)==plan['receipts']['directory'],'independently selected native receipt custody required')
if original_receipt is not None:require(Path(original_receipt).parent==directory,'original receipt custody mismatch')
# One native owner window; lock is custody concurrency only, not cluster CAS.
lock=os.open(directory/'.signer-window.lock',os.O_RDWR|os.O_CREAT|os.O_NOFOLLOW,0o600)
try:
Journal._private(os.fstat(lock));fcntl.flock(lock,fcntl.LOCK_EX|fcntl.LOCK_NB)
with _absolute_budget(remaining):
io=NativeIO() if io is None else io
attended(io,contract);fence(io,contract)
if contract['action']=='recover':
require(original_receipt is not None,'independent original receipt required')
return reconcile(io,contract,directory,original_receipt)
require(original_receipt is None,'bootstrap cannot import recovery/private values')
writer=NativeWriter(io,contract,directory)
status,value=io.http('GET',META)
require(_policy_absent(status,value),'bootstrap exact metadata absence required')
writer.receipt.update(status='write-intent-unknown',phase='metadata-setup');writer._save()
try:
validate_admission(contract);checked_sources(contract);fence(io,contract)
status,value=io.http('POST',META,{'cas_required':True,'max_versions':2,'delete_version_after':'0s'})
require(status in {200,204},'metadata write unconfirmed')
exact_metadata(*io.http('GET',META),created=False)
writer.receipt['phase']='seed-cas'
writer.write()
exact_metadata(*io.http('GET',META),created=True)
writer.final_ready=True
writer.receipt.update(status='custody-verified-pending-independent-public-admission',exact_readback=True)
writer._save()
return dict(writer.receipt)
except BaseException:
writer.receipt.update(status='write-intent-unknown',exact_readback=False);writer._save()
raise Refused('native custody outcome unconfirmed') from None
finally:os.close(lock)
class SilentParser(argparse.ArgumentParser):
def error(self,message):raise Refused('invalid custody command arguments')
def main(argv=None):
parser=SilentParser(description=__doc__,add_help=False)
parser.add_argument('--admission',required=True);parser.add_argument('--admission-sha256',required=True)
parser.add_argument('--receipt-directory',required=True);parser.add_argument('--original-receipt')
# argparse output is captured by warden; all execution exceptions are silent.
try:
require(not sys.flags.optimize,'optimized custody interpreter refused')
args=parser.parse_args(argv);raw=private_read(args.admission)
require(is_digest(args.admission_sha256) and digest(raw)==args.admission_sha256,'independently reviewed admission digest required')
execute(parsed(raw),Path(args.receipt_directory),original_receipt=args.original_receipt)
return 0
except BaseException:return 1
if __name__=='__main__':sys.exit(main())

View file

@ -0,0 +1,318 @@
"""Actual disposable KV effects through native production state machine.
Kubernetes, attended identity and owner dispositions are explicitly synthetic
fixture inputs; no production authority/identity/custody claim follows.
"""
from copy import deepcopy
from datetime import datetime,timedelta,timezone
import hashlib
import json
import os
from pathlib import Path
import shutil
import sys
import yaml
import pytest
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'scripts'))
from test_task_execution_native_fixture import bao_dev
import flex_auth_signer_native as m
from task_execution_journal import Refused
@pytest.fixture
def native(bao_dev,tmp_path,monkeypatch):
origin,token,call,process=bao_dev
assert call('POST','sys/mounts/platform',{'type':'kv','options':{'version':'2'}})[0] in {200,204}
source=tmp_path/'source';source.mkdir()
for name in [*m.SOURCE_FILES,m.CCR,m.PLAN,'openbao/policies/workload-kv-read-flex-auth-envelope-signing.hcl']:
p=source/name;p.parent.mkdir(parents=True,exist_ok=True);shutil.copyfile(m.ROOT/name,p)
ccr=yaml.safe_load((source/m.CCR).read_text());ccr['status']='approved';ccr['openbao']['auth']['bound_claims_confirmed']=True
(source/m.CCR).write_text(yaml.safe_dump(ccr,sort_keys=False))
plan=json.loads((source/m.PLAN).read_text());plan['bootstrap']['kid']='synthetic-native-kid';plan['receipts']['directory']=str(tmp_path/'private');(source/m.PLAN).write_text(json.dumps(plan))
monkeypatch.setattr(m,'ROOT',source)
directory=tmp_path/'private';directory.mkdir(mode=0o700)
image='forgejo.coulomb.social/coulomb/flex-auth@sha256:'+'1'*64
dep={'metadata':{'uid':'dep','generation':1},'spec':{'replicas':0,'template':{'spec':{'serviceAccountName':m.NAME,
'containers':[{'image':image,'args':['serve','--caller-auth-mode','enforce','--caller-audience','flex-auth','--caller-binding','secrets-engine=system:serviceaccount:secrets-engine:secrets-engine','--envelope-signing-key-file','/var/run/secrets/decision-envelope-signing/seed','--envelope-signing-kid','synthetic-native-kid'],
'volumeMounts':[{'name':'flex-auth-envelope-signing','mountPath':'/var/run/secrets/decision-envelope-signing','readOnly':True}]}],
'volumes':[{'name':'flex-auth-envelope-signing','secret':{'secretName':m.SECRET,'defaultMode':288,'items':[{'key':'seed','path':'seed'}]}}]}}},'status':{'observedGeneration':1,'replicas':0}}
class IO:
def __init__(self):
self.calls=[];self.dep=deepcopy(dep);self.pods=[];self.hpas=[];self.rs=[];self.cluster=m.CLUSTER
self.entity='11111111-1111-1111-1111-111111111111';self.backend=m.BAO_CLUSTER;self.root=False;self.role_drift=False;self.lost=False;self.denied=False;self.pod_on_recheck=False;self.fences=0
def bao(self,args,payload=None,allowed=(0,)):
self.calls.append(('bao',tuple(args)))
if args[0]=='status':return 0,{'cluster_id':self.backend,'sealed':False}
if args[:2]==['token','lookup']:return 0,{'data':{'policies':['root'] if self.root else ['platform-admin'],'path':'auth/netkingdom/login/x','entity_id':self.entity,'id':token}}
if args[0]=='audit':return 0,{'file/':{}}
if args[-1].startswith('sys/policies/acl/'):
return 0,{'data':{'policy':(source/'openbao/policies/workload-kv-read-flex-auth-envelope-signing.hcl').read_text()}}
if args[-1].startswith('auth/kubernetes/role/'):
return 0,{'data':{'bound_service_account_names':['foreign'] if self.role_drift else ['flex-auth-envelope-signing-eso'],'bound_service_account_namespaces':['flex-auth'],'bound_service_account_namespace_selector':'','audience':'openbao','token_policies':['workload-kv-read-flex-auth-envelope-signing'],'token_ttl':900,'token_max_ttl':900,'token_explicit_max_ttl':900,'token_no_default_policy':True,'token_period':0,'token_num_uses':0,'token_bound_cidrs':[]}}
raise AssertionError('unexpected fixed API')
def kube(self,resource,name=None):
self.calls.append(('kube',resource,name))
if resource=='namespace':return {'metadata':{'uid':self.cluster}}
if resource=='deployment':
self.fences+=1
if self.pod_on_recheck and self.fences>=3:self.pods=[{'spec':{'serviceAccountName':m.NAME}}]
return deepcopy(self.dep)
return {'items':deepcopy({'pods':self.pods,'hpa':self.hpas,'replicasets':self.rs}[resource])}
def http(self,method,path,body=None):
self.calls.append(('http',method,path))
if self.denied and path in {m.PATH,m.PATH+'?version=1'} and method=='GET':return 403,{'errors':['secret backend detail']}
result=call(method,path,body)
if self.lost and method=='POST' and path==m.PATH:raise OSError('secret response lost')
return result
io=IO()
c={'schema':'railiance-platform.signer-native-admission.v1','action':'bootstrap','operation_id':'synthetic-native-operation','kid':'synthetic-native-kid',
'expires_at':(datetime.now(timezone.utc)+timedelta(seconds=100)).isoformat(),'founder_entity_id':io.entity,
'ccr_sha256':m.digest((source/m.CCR).read_bytes()),'source_bundle':{n:m.digest((source/n).read_bytes()) for n in m.SOURCE_FILES},
'bindings':{k:'sha256:'+'2'*64 for k in m.BINDINGS},'producer_image':image,'caller_binding':'secrets-engine=system:serviceaccount:secrets-engine:secrets-engine',
'deployment_projection_sha256':m.digest(m.encoded(m.deployment_projection(dep))),
'authority':'founder-attended-exact-source-scope; no standing grant',
'routing':{'lane_id':'openbao-platform-admin-login','verdict':'founder_required','founder_act':'oidc_login','receipt_sha256':'sha256:'+'3'*64},
'owner_reviews':{},'exclusive_window_sha256':'sha256:'+'4'*64,'consumer_trust_sha256':'sha256:'+'5'*64,'original_receipt_sha256':None,'original_execution':None}
c['bindings'].update(proposal_sha256=m.digest((source/m.PLAN).read_bytes()),policy_sha256=m.LIFECYCLE,image_sha256='sha256:'+'1'*64,trust_sha256=c['consumer_trust_sha256'],deployment_sha256=c['deployment_projection_sha256'],executed_revision_sha256=m.digest(m.encoded(c['source_bundle'])))
def approve(value):
if value['action']=='recover' and value['original_execution'] is None:
old=json.loads((directory/(c['operation_id']+'.json')).read_bytes())
value['original_execution']={key:deepcopy(old[key]) for key in ['bindings','writer_sha256','source_ccr_sha256']}
scope=m.digest(m.encoded({k:v for k,v in value.items() if k!='owner_reviews'}))
value['owner_reviews']={role:{'approved_admission_sha256':scope,'approval_ref':'synthetic-fixture-'+role} for role in m.REVIEWS}
return value
approve(c)
return c,io,directory,call,approve,source
def test_actual_native_bootstrap_exact_readback_and_no_retry(native):
c,io,directory,call,approve,source=native
result=m.execute(c,directory,io=io)
assert result['status']=='custody-verified-pending-independent-public-admission' and result['exact_readback']
assert call('GET',m.META)[1]['data']['current_version']==1
assert [(x[1],x[2]) for x in io.calls if x[0]=='http' and x[1]=='POST']==[('POST',m.META),('POST',m.PATH)]
private=call('GET',m.PATH)[1]['data']['data']['ED25519_PRIVATE_KEY']
raw=(directory/(c['operation_id']+'.json')).read_bytes();assert private.encode() not in raw
before=len(io.calls)
with pytest.raises(FileExistsError):m.execute(c,directory,io=io)
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls[before:])
def test_actual_process_loss_unknown_fresh_recovery_read_only(native):
c,io,directory,call,approve,source=native
child=os.fork()
if child==0:
original_http=io.http
def die_after_native_write(method,path,body=None):
result=original_http(method,path,body)
if method=='POST' and path==m.PATH:os._exit(73)
return result
io.http=die_after_native_write
try:m.execute(c,directory,io=io)
except BaseException:os._exit(74)
os._exit(75)
_,status=os.waitpid(child,0)
assert os.waitstatus_to_exitcode(status)==73
old=directory/(c['operation_id']+'.json');original=old.read_bytes();assert json.loads(original)['status']=='write-intent-unknown'
recovery=deepcopy(c);recovery.update(action='recover',operation_id='synthetic-independent-recovery',original_receipt_sha256=m.digest(original));approve(recovery)
io.lost=False;io.calls.clear()
result=m.execute(recovery,directory,io=io,original_receipt=old)
assert result['status']=='custody-reconciled-pending-independent-public-admission'
assert result['no_write_or_remint'] and old.read_bytes()==original
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls)
assert call('GET',m.META)[1]['data']['current_version']==1
assert (directory/(recovery['operation_id']+'.json')).stat().st_mode&0o777==0o600
io.calls.clear()
with pytest.raises(FileExistsError):m.execute(recovery,directory,io=io,original_receipt=old)
assert not any(x[0]=='http' for x in io.calls)
@pytest.mark.parametrize('case',['missing-image','missing-trust','mismatched-source','bad-review','bad-route','expired','wrong-entity','wrong-backend','wrong-cluster','root','pod','terminating-pod','hpa','replicas','image-drift','role-drift','pod-after-metadata'])
def test_refusals_no_seed_effect(native,case):
c,io,directory,call,approve,source=native
if case=='missing-image':c['producer_image']=''
elif case=='missing-trust':c['consumer_trust_sha256']=''
elif case=='mismatched-source':(source/'scripts/flex_auth_signer_custody.py').write_text('changed source')
elif case=='bad-review':c['owner_reviews']['founder']['approved_admission_sha256']='sha256:'+'f'*64
elif case=='bad-route':c['routing']['lane_id']='openbao-structure-survey';approve(c)
elif case=='expired':c['expires_at']=(datetime.now(timezone.utc)-timedelta(seconds=1)).isoformat();approve(c)
elif case=='wrong-entity':io.entity='other'
elif case=='wrong-backend':io.backend='other'
elif case=='wrong-cluster':io.cluster='other'
elif case=='root':io.root=True
elif case=='pod':io.pods=[{'spec':{'serviceAccountName':m.NAME}}]
elif case=='terminating-pod':io.pods=[{'metadata':{'deletionTimestamp':'now'},'spec':{'volumes':[{'secret':{'secretName':m.SECRET}}]}}]
elif case=='hpa':io.hpas=[{'spec':{'scaleTargetRef':{'name':m.NAME}}}]
elif case=='replicas':io.dep['spec']['replicas']=1
elif case=='image-drift':io.dep['spec']['template']['spec']['containers'][0]['image']='unreviewed'
elif case=='role-drift':io.role_drift=True
elif case=='pod-after-metadata':io.pod_on_recheck=True
with pytest.raises(Refused):m.execute(c,directory,io=io)
assert not any(x[0]=='http' and x[1]=='POST' and x[2]==m.PATH for x in io.calls)
assert call('GET',m.PATH)[0]==404
@pytest.mark.parametrize('case',['damaged-receipt','typed-damaged-receipt','changed-version','read-denied','no-fresh-review'])
def test_recovery_refuses_without_new_effect(native,case):
c,io,directory,call,approve,source=native;io.lost=True
with pytest.raises(Refused):m.execute(c,directory,io=io)
old=directory/(c['operation_id']+'.json');recovery=deepcopy(c);recovery.update(action='recover',operation_id='recovery',original_receipt_sha256=m.digest(old.read_bytes()));approve(recovery)
if case=='damaged-receipt':old.write_text('{}')
elif case=='typed-damaged-receipt':
damaged=json.loads(old.read_text());damaged['expected_version']=False;old.write_text(json.dumps(damaged));recovery['original_receipt_sha256']=m.digest(old.read_bytes());approve(recovery)
elif case=='changed-version':
doc=call('GET',m.PATH)[1]['data']['data'];assert call('POST',m.PATH,{'options':{'cas':1},'data':doc})[0]==200
elif case=='read-denied':io.denied=True
elif case=='no-fresh-review':recovery['owner_reviews']['founder']['approved_admission_sha256']='sha256:'+'f'*64
io.calls.clear();io.lost=False
with pytest.raises(Refused):m.execute(recovery,directory,io=io,original_receipt=old)
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls)
@pytest.mark.parametrize('bad_args',[False,True])
def test_main_missing_approval_silent_and_no_io(tmp_path,capsys,bad_args):
if bad_args:
assert m.main(['--unrecognized'])==1
output=capsys.readouterr();assert output.out==output.err==''
return
assert m.main(['--admission',str(tmp_path/'missing'),'--admission-sha256','sha256:'+'0'*64,'--receipt-directory',str(tmp_path)])==1
output=capsys.readouterr();assert output.out==output.err==''
def test_native_command_byte_and_elapsed_bounds_no_output(capsys):
io=m.NativeIO()
with pytest.raises(Refused):io.captured([sys.executable,'-c',"import sys;sys.stdout.write('x'*1048577)"])
import time
started=time.monotonic()
with pytest.raises(Refused):io.captured([sys.executable,'-c',"import time;time.sleep(10)"])
assert time.monotonic()-started<3
output=capsys.readouterr();assert output.out==output.err==''
def test_exclusive_native_window_refuses_second_writer(native):
import fcntl
c,io,directory,call,approve,source=native
fd=os.open(directory/'.signer-window.lock',os.O_RDWR|os.O_CREAT,0o600)
try:
fcntl.flock(fd,fcntl.LOCK_EX|fcntl.LOCK_NB)
with pytest.raises(BlockingIOError):m.execute(c,directory,io=io)
assert io.calls==[]
finally:os.close(fd)
def test_native_fixed_http_transport_against_disposable_backend(bao_dev,monkeypatch,capsys):
origin,token,call,process=bao_dev
assert call('POST','sys/mounts/platform',{'type':'kv','options':{'version':'2'}})[0] in {200,204}
factory=m.http.client.HTTPConnection;port=int(origin.rsplit(':',1)[1]);destinations=[]
def fixture_socket(host,requested_port,*args,**kwargs):
destinations.append((host,requested_port))
return factory(host,port if requested_port==18200 else requested_port,*args,**kwargs)
monkeypatch.setattr(m.http.client,'HTTPConnection',fixture_socket)
io=m.NativeIO();io._token=token
assert io.http('GET',m.META)==(404,{'errors':[]})
assert io.http('POST',m.META,{'cas_required':True,'max_versions':2,'delete_version_after':'0s'})[0] in {200,204}
m.exact_metadata(*io.http('GET',m.META),created=False)
assert all(host=='127.0.0.1' and selected_port==18200 for host,selected_port in destinations)
with pytest.raises(Refused):io.http('GET','other/path')
output=capsys.readouterr();assert output.out==output.err==''
def metadata_phase_crash(native,*,after):
c,io,directory,call,approve,source=native
child=os.fork()
if child==0:
original=io.http
def die(method,path,body=None):
if method=='POST' and path==m.META:
if after:original(method,path,body)
os._exit(73)
return original(method,path,body)
io.http=die
try:m.execute(c,directory,io=io)
except BaseException:os._exit(74)
os._exit(75)
_,status=os.waitpid(child,0);assert os.waitstatus_to_exitcode(status)==73
old=directory/(c['operation_id']+'.json');raw=old.read_bytes();receipt=json.loads(raw)
assert receipt['phase']=='metadata-setup' and receipt['status']=='write-intent-unknown'
assert receipt['public_key'] is None and receipt['kv_version'] is None
recovery=deepcopy(c);recovery.update(action='recover',operation_id='metadata-recovery',original_receipt_sha256=m.digest(raw));approve(recovery)
return old,raw,recovery
@pytest.mark.parametrize('after',[False,True])
def test_real_metadata_process_loss_read_only_phase_recovery(native,after):
c,io,directory,call,approve,source=native
old,raw,recovery=metadata_phase_crash(native,after=after)
io.calls.clear();result=m.execute(recovery,directory,io=io,original_receipt=old)
assert result['status']==('metadata-only-confirmed' if after else 'current-custody-absent')
assert result['public_key'] is result['public_fingerprint'] is result['kv_version'] is None
assert result['key_custody_verified'] is False and result['historical_effect']=='unresolved'
assert 'fresh independently reviewed exact action required' in result['next_action']
assert old.read_bytes()==raw and result['original_unknown_preserved']
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls)
assert call('GET',m.PATH+'?version=1')==(404,{'errors':[]})
@pytest.mark.parametrize('case',['metadata-denied','metadata-misleading404','metadata-misleading200','metadata-malformed','metadata-timeout','data-denied','unexpected-version'])
def test_metadata_recovery_ambiguous_or_denied_never_classifies_or_writes(native,case):
c,io,directory,call,approve,source=native
old,raw,recovery=metadata_phase_crash(native,after=True)
if case=='unexpected-version':
assert call('POST',m.PATH,{'options':{'cas':0},'data':{'ED25519_PRIVATE_KEY':'synthetic-unadmitted','ED25519_PUBLIC_KEY':'synthetic-unadmitted','kid':c['kid']}})[0]==200
original=io.http
def faulty(method,path,body=None):
if path==m.META:
if case=='metadata-denied':return 403,{'errors':['denied']}
if case=='metadata-misleading404':return 404,{'errors':['permission denied']}
if case=='metadata-misleading200':
result=original(method,path,body);result[1]['errors']=['denied'];return result
if case=='metadata-malformed':return 200,{'data':{'current_version':False}}
if case=='metadata-timeout':raise OSError('synthetic private backend detail')
if path==m.PATH+'?version=1' and case=='data-denied':return 404,{'errors':['denied']}
return original(method,path,body)
io.http=faulty;io.calls.clear()
with pytest.raises((Refused,OSError)):m.execute(recovery,directory,io=io,original_receipt=old)
evidence=json.loads((directory/(recovery['operation_id']+'.json')).read_bytes())
assert evidence['status']=='recovery-read-intent-unconfirmed' and evidence['key_custody_verified'] is False
assert old.read_bytes()==raw
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls)
if case=='unexpected-version':assert not any(x[0]=='http' and x[2]==m.PATH+'?version=1' for x in io.calls)
def test_stale_current_primitive_lineage_refuses_before_native_io(native):
c,io,directory,call,approve,source=native
plan=json.loads((source/m.PLAN).read_bytes());plan['bootstrap']['source_primitive_sha256']='sha256:'+'8'*64
(source/m.PLAN).write_text(json.dumps(plan));c['bindings']['proposal_sha256']=m.digest((source/m.PLAN).read_bytes());approve(c)
with pytest.raises(Refused,match='primitive lineage'):m.execute(c,directory,io=io)
assert io.calls==[]
def test_recovery_explicitly_binds_historical_execution_and_current_source(native):
c,io,directory,call,approve,source=native
old,raw,recovery=metadata_phase_crash(native,after=True)
# Model a separately retained older executed bundle/proposal. It must be
# explicitly selected in fresh recovery review, never treated as current.
original=json.loads(raw);original['bindings']['executed_revision_sha256']='sha256:'+'6'*64
original['bindings']['proposal_sha256']='sha256:'+'7'*64
old.write_text(json.dumps(original));recovery['original_receipt_sha256']=m.digest(old.read_bytes())
recovery['original_execution']={key:deepcopy(original[key]) for key in ['bindings','writer_sha256','source_ccr_sha256']};approve(recovery)
result=m.execute(recovery,directory,io=io,original_receipt=old)
assert result['status']=='metadata-only-confirmed'
assert result['original_execution']['bindings']['executed_revision_sha256']!=recovery['bindings']['executed_revision_sha256']
assert json.loads(old.read_text())==original
def test_generated_but_unstored_seed_phase_recovers_without_public_admission(native):
c,io,directory,call,approve,source=native
io.pod_on_recheck=True
with pytest.raises(Refused):m.execute(c,directory,io=io)
old=directory/(c['operation_id']+'.json');raw=old.read_bytes();receipt=json.loads(raw)
assert receipt['phase']=='seed-cas' and receipt['public_key'] is not None
assert call('GET',m.PATH+'?version=1')==(404,{'errors':[]})
io.pod_on_recheck=False;io.pods=[];io.calls.clear()
recovery=deepcopy(c);recovery.update(action='recover',operation_id='unstored-seed-recovery',original_receipt_sha256=m.digest(raw));approve(recovery)
result=m.execute(recovery,directory,io=io,original_receipt=old)
assert result['status']=='metadata-only-confirmed' and result['key_custody_verified'] is False
assert result['public_key'] is None and result['public_fingerprint'] is None
assert old.read_bytes()==raw
assert not any(x[0]=='http' and x[1]=='POST' for x in io.calls)

View file

@ -0,0 +1,49 @@
"""Real CCR schema/applier contract, no credentials or backend invocation."""
from copy import deepcopy
import importlib.util
import json
from pathlib import Path
import yaml
ROOT=Path(__file__).resolve().parents[1]
spec=importlib.util.spec_from_file_location('signer_ccr',ROOT/'scripts/credential-change.py')
ccr_module=importlib.util.module_from_spec(spec);spec.loader.exec_module(ccr_module)
CCR=ROOT/'credential-change-requests/CCR-2026-0050-flex-auth-envelope-signing.yaml'
def test_real_schema_and_apply_refusal():
ccr,errors,warnings=ccr_module.validate_ccr(CCR)
assert errors==[]
assert warnings==['OIDC/Kubernetes bound claim is not confirmed; apply is blocked']
blockers=ccr_module.applier_readiness_blockers(ccr)
assert len(blockers)==2
assert any('status, got proposed' in x for x in blockers)
assert any('confirmed OpenBao auth binding' in x for x in blockers)
assert ccr['review']['comments']==[] and ccr['access_frontdoor']['resolvable'] is False
# Pure synthetic candidate only: ensure admitted-pattern scope has no latent
# schema/applier role/field failures once the real owners eventually approve.
admitted=deepcopy(ccr);admitted['status']='approved';admitted['openbao']['auth']['bound_claims_confirmed']=True
assert ccr_module.applier_readiness_blockers(admitted)==[]
def test_generated_read_scope_and_projection_join():
ccr,_,_=ccr_module.validate_ccr(CCR)
assert (ROOT/ccr['openbao']['policy_file']).read_text()==ccr_module.generated_policy_hcl(ccr)
hcl=ccr_module.generated_policy_hcl(ccr)
assert 'metadata/' not in hcl and 'list' not in hcl and 'create' not in hcl
assert ccr['openbao']['fields']==['ED25519_PRIVATE_KEY']
docs=list(yaml.safe_load_all((ROOT/'docs/credential-lane-designs/flex-auth-envelope-signing-native-projection.yaml.proposed').read_text()))
store=next(x for x in docs if x['kind']=='SecretStore')
assert store['spec']['provider']['vault']['auth']['kubernetes']['role']==ccr['openbao']['auth']['role']
external=next(x for x in docs if x['kind']=='ExternalSecret')
assert external['spec']['data']==[{'secretKey':'seed','remoteRef':{'key':'workloads/flex-auth/envelope-signing','property':'ED25519_PRIVATE_KEY'}}]
def test_native_scope_separates_read_grant_and_unadmitted_execution():
plan=json.loads((ROOT/'docs/credential-lane-designs/flex-auth-envelope-signing-native-plan.json').read_text())
assert plan['authority_granted'] is False and plan['status']=='prepared-unapproved'
assert plan['bootstrap']['expected_version']==0
assert all(x['disposition'].startswith('pending ') for x in plan['reviews_required'])
assert 'implementation/independent tests still required' in plan['protected_adapter']['state']
assert plan['receipts']['maximum_bytes']==65536
assert all('platform/' in x['path'] for x in plan['bootstrap']['allowed_native_methods'])
assert 'zero flex-auth PVCs' in plan['requested_scope']['new_resources']

View file

@ -1344,3 +1344,34 @@ admission; RPF-WP-0035-T12 remains progress.
R3 source metadata correction aligns proposal IDs across JSON/docs/Secret
annotation and documents existing base64url-ASCII public fingerprint hashing.
Frozen R2 source/evidence is preserved; no writer behavior or admission changes.
2026-10-16 T12 native signer preparation: proposed CCR-2026-0050 and generated
read-only HCL pass real schema checks; current proposed status/unconfirmed claims
keep apply refused. Native candidate uses existing external-secrets-* role
convention with dedicated SA unchanged and projected private field only. Exact
attended CAS0/metadata/fence/receipt/forward-recovery action plan is separate from
read-grant authority; production adapter implementation/independent tests and
founder plus distinct owner reviews remain necessary. Access observed zero PVCs;
new outbox storage/retention/runtime bounds remain separate Access/cluster scope.
No live credentials, grants, roles, key or deployment changed. T12 stays progress.
2026-10-16 T12 native signer adapter source: silent fixed-scope attended child
checks exact independent admission/source/CCR/image/caller/trust/identity inputs,
actual backend/reader role/policy, signer fence and private exclusive receipt
custody before fixed metadata/CAS0 effects. Separate fresh read-only recovery
reconciles real process loss after native write without remint/retry/new effects.
Disposable tests exercise actual KV while owner reviews/identity/fence facts are
explicitly synthetic. Current warden plan dispatch selects structure-survey, not
OIDC admin login; mismatch remains a live refusal. Canonical proposed CCR, native
owner/founder approval and Access/cluster/outbox/PVC gates stay open; no live
key, credential read, grant, role, storage or deployment action performed.
2026-10-16 T12 native signer R2 source correction preserves R1 failures: exact
phase-aware read-only recovery classifies current metadata-only/absent states,
never admits a key or retry and preserves original uncertainty. Fresh recovery
explicitly binds historical execution plus current source; current primitive
plan/source/receipt join is enforced and old pin labeled historical. Real fork
metadata-loss and denied/ambiguous tests added.120second expiry is unchanged;
external reviews and unbounded OIDC require a pre-reviewed exact scheduled
window, otherwise refuse/new explicit revision. No native or owner approval
inferred; no live action.