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,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