docs: preserve reviewed NetKingdom session artifacts
This commit is contained in:
parent
6cf0012a6e
commit
ad3b052f4d
50 changed files with 6793 additions and 0 deletions
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"command": "python3 -m pytest tests/test_case_version_source.py tests/test_case_plan.py tests/test_case_build.py tests/test_case_structure.py -q",
|
||||
"cwd": "/tmp/netkingdom-mason-browser-20261016",
|
||||
"result": "50 passed in 16.14s",
|
||||
"git_diff_check": "PASS",
|
||||
"ruff": "unavailable: /usr/bin/python3: No module named ruff",
|
||||
"actual_12_projection": {
|
||||
"type_status": "acknowledged",
|
||||
"case_status": "proposed",
|
||||
"case_acceptance": null,
|
||||
"plan_status": "draft",
|
||||
"objects": 6,
|
||||
"digest": "sha256:c0afd5576cdae0cbc807eccb2b9fa87ac668f15e32ec7cf79d295bdb23c75b35"
|
||||
},
|
||||
"native_effects": false,
|
||||
"statehub_consistency": "deferred to parent after canonical owner patch integration; isolated plan status unchanged",
|
||||
"canonical_git_apply_check": "PASS: git apply --check implementation.patch in /home/worsch/ops-mason; no mutation"
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"checkpoint": "3607d3e08ffdbc473d19e9baf7225843c4075ae0",
|
||||
"published": true,
|
||||
"sync_summary": {
|
||||
"fail": 0,
|
||||
"automation_error": 0,
|
||||
"warn": 7,
|
||||
"info": 4
|
||||
},
|
||||
"bindings": "89/89",
|
||||
"original_brief_index_and_worktree_restored_and_verified": true,
|
||||
"agents_terminal": true,
|
||||
"runtime_trial_active": false,
|
||||
"scope_receipt": "pause-publication-scope.json",
|
||||
"requested_status": "paused",
|
||||
"completion_estimate": {
|
||||
"percent": 40,
|
||||
"range_percent": [
|
||||
30,
|
||||
50
|
||||
],
|
||||
"basis": "Rough judgment, not measured gate completion",
|
||||
"acceptance_gates_fully_closed": 0,
|
||||
"acceptance_gates_total": 16
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
"""Root-reviewed remote public metadata collector; stdin Python, no mutations/Secrets/full specs.
|
||||
Capture/discard raw kubectl stdout/stderr on any error. Output selected structured fields only.
|
||||
"""
|
||||
import datetime,json,subprocess,urllib.parse
|
||||
|
||||
def val(path):
|
||||
return '{{with '+path+'}}{{printf "%q" .}}{{else}}null{{end}}'
|
||||
def arr(path,fields):
|
||||
return '[{{range $i,$v := '+path+'}}{{if $i}},{{end}}{'+','.join(json.dumps(k)+':'+val('$v.'+v) for k,v in fields.items())+'}{{end}}]'
|
||||
META='{'+','.join(json.dumps(k)+':'+val(v) for k,v in {'name':'.metadata.name','namespace':'.metadata.namespace','uid':'.metadata.uid','resource_version':'.metadata.resourceVersion'}.items())+','+'"owners":'+arr('.metadata.ownerReferences',{'kind':'kind','name':'name','uid':'uid'})+'}'
|
||||
SOURCE='{'+','.join(json.dumps(x)+':'+val('.'+x) for x in ['repoURL','path','targetRevision','chart'])+'}'
|
||||
APP='{"metadata":'+META+',"source":{{with .spec.source}}'+SOURCE+'{{else}}null{{end}},"sources":[{{range $i,$v := .spec.sources}}{{if $i}},{{end}}'+SOURCE+'{{end}}],"operation_present":{{if .operation}}true{{else}}false{{end}},"automated_present":{{if eq .spec.syncPolicy.automated nil}}false{{else}}true{{end}},"operation_phase":'+val('.status.operationState.phase')+',"resources":'+arr('.status.resources',{'kind':'kind','name':'name','namespace':'namespace'})+'}'
|
||||
OBJECT='{"metadata":'+META+',"conditions":'+arr('.status.conditions',{'type':'type','status':'status','reason':'reason'})+',"field_managers":'+arr('.metadata.managedFields',{'manager':'manager','operation':'operation'})+'}'
|
||||
def query(kind,name,namespace,template):
|
||||
cmd=['kubectl','get',kind,name,'--ignore-not-found','-o','go-template='+template]
|
||||
if namespace:cmd.extend(['-n',namespace])
|
||||
try:
|
||||
r=subprocess.run(cmd,capture_output=True,text=True,timeout=12)
|
||||
if r.returncode or len(r.stdout)>131072:return {'ok':False,'refused':'public-query-failed','stderr_present':bool(r.stderr)}
|
||||
if not r.stdout.strip():return {'ok':True,'exists':False}
|
||||
data=json.loads(r.stdout)
|
||||
# repo source allowlist prevents accidental credential-bearing URL output.
|
||||
for source in ([data.get('source')]+data.get('sources',[])):
|
||||
if not source:continue
|
||||
url=source.get('repoURL')
|
||||
if url:
|
||||
parsed=urllib.parse.urlsplit(url)
|
||||
if parsed.username or parsed.password or parsed.query or parsed.fragment or url not in {'https://forgejo.coulomb.social/coulomb/railiance-platform.git','https://forgejo.coulomb.social/coulomb/informed-decision.git'}:
|
||||
source['repoURL']=None;source['repoURL_redacted']=True
|
||||
return {'ok':True,'exists':True,'selected':data,'stderr_present':bool(r.stderr)}
|
||||
except Exception:return {'ok':False,'refused':'sanitized-public-query-failure'}
|
||||
result={'schema':'platform-native-plan-public-supplement.v1','observed_at':datetime.datetime.now(datetime.timezone.utc).isoformat(),'persistent_mutations':False,'held_fence':False}
|
||||
cluster=query('namespace','kube-system',None,'{"uid":'+val('.metadata.uid')+'}')
|
||||
if cluster.get('selected',{}).get('uid')!='a553c742-0115-43d4-99a4-a5ca56fe0786':result={'refused':'cluster-identity','persistent_mutations':False}
|
||||
else:
|
||||
result['applications']={name:query('applications.argoproj.io',name,'argocd',APP) for name in ['railiance-apps-root','informed-decision','openbao-secretstore','eso-token-renewer']}
|
||||
result['objects']={kind+'/'+name:query(kind,name,'flex-auth',OBJECT) for kind,name in [('deployment','flex-auth-secrets-engine'),('serviceaccount','flex-auth-secrets-engine'),('persistentvolumeclaim','flex-auth-secrets-engine-outbox'),('serviceaccount','flex-auth-envelope-signing-eso'),('secretstore.external-secrets.io','openbao-flex-auth-envelope-signing'),('externalsecret.external-secrets.io','flex-auth-secrets-engine-envelope-signing')]}
|
||||
print(json.dumps(result,sort_keys=True))
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
--- a/credential-change-requests/CCR-2026-0050-flex-auth-envelope-signing.yaml
|
||||
+++ b/credential-change-requests/CCR-2026-0050-flex-auth-envelope-signing.yaml
|
||||
@@ -19,7 +19,10 @@
|
||||
- access-engine-owner
|
||||
- secrets-engine-owner
|
||||
- cluster-runtime-storage-owner
|
||||
- comments: []
|
||||
+ comments:
|
||||
+ - reviewer: railiance-platform-owner (agent railiance-platform, codex)
|
||||
+ decision: approved
|
||||
+ comment: "INTENT Secure Custody/Stable Interfaces/Operable and Observable; RPF-WP-0035-T12. Approved Platform source/plan review only for frozen P4 manifest sha256:9eec861838a8e9138f2c821cff3014998fbe74bb01447642bf50d44e8b3b7cb3 and R2 manifest sha256:5be7a7f438624dcb35b7dbc96e7c9e5a8e2d5d1b1966cb4338aa5aecbe3a8b59; own bounded review artifact docs/evidence/2026-10-16-flex-auth-native-plan-review.json; sanitized supplement sha256:f02a9b595d9246a358d19b37bdad8fde158b7f0c9657a6ff088874732511a32c. Exact read policy/role/ESO proposal and one-use CAS0 writer stay separate. Identified manual Applications and recovery gates are mandatory. No founder decision, native execution, confirmed bindings or completed admission assent; current proposed status unchanged."
|
||||
target:
|
||||
domain: infotech
|
||||
tenant: platform
|
||||
--- a/workplans/RPF-WP-0035-credential-lane-implementation.md
|
||||
+++ b/workplans/RPF-WP-0035-credential-lane-implementation.md
|
||||
@@ -1391,3 +1391,13 @@
|
||||
PV retention, native custody, independent post-key trust or founder/owner
|
||||
approval is inferred. Exact successor source/CCR/plan pins and fresh scoped
|
||||
reviews remain required; T12 native gates and historical T13 outcome unchanged.
|
||||
+
|
||||
+2026-10-16 T12 bounded native plan review: Platform independently approves the
|
||||
+exact P4/R2 custody/read-lane and existing manual Argo Application freeze/recovery
|
||||
+proposal only, per docs/evidence/2026-10-16-flex-auth-native-plan-review.json.
|
||||
+Current observed inventory has no signer Application; root and informed-decision
|
||||
+manual sync are held during ceremony, while shared openbao-secretstore and legacy
|
||||
+eso-token-renewer stay unchanged. Exact CCR read grant and one-use custody writer
|
||||
+remain separate. Actual CCR/founder/entity/expiry/window/fence, native ESO/readback,
|
||||
+independent Secrets public trust and Access/cluster runtime proofs remain gates.
|
||||
+No authority/admission field changes; T12 remains progress.
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Platform signer custody source proposal
|
||||
|
||||
Prepared in `/tmp/netkingdom-platform-signer-custody-20261016`, based on
|
||||
`5f30ddd90c33e7eb1b9efa1093c7324d83210cf2`; canonical Platform checkout untouched.
|
||||
`source-proposal-r1.patch` contains five source files. Exact preimages, output
|
||||
hashes and inputs are in `manifest-r1.json`; 11 offline checks passed in
|
||||
`validation-r1.json`. `owner-source-review-r1.json` approves the source proposal
|
||||
only. No key, CCR admission, rollout, publication or credentials were created/read.
|
||||
|
||||
The proposal reuses namespace SecretStore/ESO and the existing Access read-only
|
||||
seed mount and durable single-writer outbox. It separates public trust admission
|
||||
from private seed projection and leaves exact scope approvals, protected writer,
|
||||
image/consumer provenance, managed-package debt, durability/retention, costs/risk
|
||||
and native acceptance explicit. Source templates end `.proposed` outside deployment
|
||||
manifests. CCR-2026-0048/0049 and WORK-RECORDS are untouched.
|
||||
|
||||
`protected-recovery-next-owner-task-r1.json` documents the separately outstanding
|
||||
restart recovery gap and concrete owner acceptance criteria. No recovery was
|
||||
implemented. Root owns integration, work-record synchronization and session log.
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"schema": "railiance-platform.signer-custody-source-manifest.v1",
|
||||
"base_revision": "5f30ddd90c33e7eb1b9efa1093c7324d83210cf2",
|
||||
"isolated_owner_checkout": "/tmp/netkingdom-platform-signer-custody-20261016",
|
||||
"patch_sha256": "0f31567718023cdf2e2c1b3a5d8c8916312fd44b039b9bc32a2e404011fe4a57",
|
||||
"files": {
|
||||
"docs/credential-lane-designs/flex-auth-envelope-signing.md": {
|
||||
"before_sha256": null,
|
||||
"after_sha256": "5b4f94341c603e684a5cdfe8c947564edf185bcf938bc9e051c786146eee6ae5"
|
||||
},
|
||||
"docs/credential-lane-designs/flex-auth-envelope-signing.proposal.json": {
|
||||
"before_sha256": null,
|
||||
"after_sha256": "94907821addf525fb35b0eebfbbfaa55ecab4bada2858435acddd9dd9af83afd"
|
||||
},
|
||||
"docs/credential-lane-designs/flex-auth-envelope-signing-policy.hcl.proposed": {
|
||||
"before_sha256": null,
|
||||
"after_sha256": "a8ae0ea97e5bde8a9e5d785fc87b8095e4cc24b5fcf358a4b7b1568ab1dd07ee"
|
||||
},
|
||||
"docs/credential-lane-designs/flex-auth-envelope-signing-projection.yaml.proposed": {
|
||||
"before_sha256": null,
|
||||
"after_sha256": "4fde665568dedc5dd800e16f11e6dbfca83ea41d955efdb1fccef3976f35d6dc"
|
||||
},
|
||||
"workplans/RPF-WP-0035-credential-lane-implementation.md": {
|
||||
"before_sha256": "f70c0011cb5d2ef310d6e2bf392a0b6bb6f78b407b9182a1e38bed2f80472f23",
|
||||
"after_sha256": "369b66a7d47307538ef6914af60856bd86d5bc0471660519690b2d3d2c492332"
|
||||
}
|
||||
},
|
||||
"preserved_canonical_dirty": [
|
||||
"WORK-RECORDS.md"
|
||||
],
|
||||
"unrelated_CCR0048_CCR0049": "not edited or admitted",
|
||||
"inputs": {
|
||||
"/home/worsch/railiance-platform/AGENTS.md": "8d2f1a165e3f4eecf2e6e1d6a19c4bac9e8dcb64a76650e7185d55722f233c8b",
|
||||
"/home/worsch/railiance-platform/INTENT.md": "12c6540735af591f7a4a9fe85ff10d3cd00eec55b5b6b1ddf148abf2d3ac9a9c",
|
||||
"/home/worsch/the-custodian/canon/standards/delegated-owner-review_v0.1.md": "d372b2ad21849a18d90d40636fad3c31425b2bd61f0778f43e57c62402a06d6c",
|
||||
"/home/worsch/the-custodian/docs/decisions/2026-10-11-security-unblock-decisions.md": "660b7a3d008cd6000bbefa55ef692bb5879e067aa086f01effd249f68a382209",
|
||||
"/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/access-native-source/source-preparation-r2.patch": "cee7af8e1e8ee8b63f9d5bc5927a6d89ae6c1169b3ec23f59e0958664fc91dca",
|
||||
"/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/access-native-preparation/native-admission-proposal-r1.json": "cfc43a0a9144ec4f4abc11ffaf52614691b335de33393fe290223732cec73cad",
|
||||
"/home/worsch/access-engine/charts/flex-auth/values.yaml": "05a11ad50d64299e1dbc8b0950528e7e40885f98f9157691b40a53b92aba3643",
|
||||
"/home/worsch/access-engine/charts/flex-auth/templates/deployment.yaml": "99991461acd090a0b80c31cd48af529a7dac40a707b4c527c3dd0beb9440ddcc",
|
||||
"/home/worsch/access-engine/internal/emission/outbox.go": "7460e0c856f784b7a7b99110a66b562f889951a0892473bb763afdaa73800f1a"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Native signer adapter source R1
|
||||
|
||||
Apply after canonical7be8b6e and separate native-preparationR1. The five-file
|
||||
patch adds fixed silent attended native adapter, tests, docs and workplan note,
|
||||
and extracts shared receipt initialization without weakening fixture admission.
|
||||
Exact preimages, hashes, checks and source-only review are retained here.
|
||||
|
||||
Actual disposable tests cover metadata/CAS/readback and executor process death
|
||||
followed by independent read-only reconciliation. All identity/review/Kubernetes
|
||||
fence observations are synthetic; no production authority follows. Native
|
||||
transport/tool bounds are separately tested. See validation history for retained
|
||||
initial test defects and their corrections.
|
||||
|
||||
Live action remains refused: current routing plan selects the wrong lane, CCR
|
||||
is proposed/unconfirmed, exact independently approved image/caller/trust/source
|
||||
inputs and distinct reviews are missing. No token/seed/key/grant/deployment was
|
||||
retrieved or changed live. The source supplies no authority defaults.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"checked_at": "2026-10-16T15:46:48.569249+00:00",
|
||||
"patch_sha256": "sha256:f316a53b604f4333414b832691968ede8056d49a61635f0f25831084b1d75f19",
|
||||
"independent_reconstruction": "/tmp/netkingdom-target-root-review/platform-native-adapter-r1",
|
||||
"existing_tests": "44 passed in28.60s",
|
||||
"counterexample": "Actual process death after metadata POST leaves original write-intent-unknown phase metadata-setup, public_key null, current_version0 and data404. Independently scoped read-only recovery refuses original receipt before any target read; phase is not reconcilable.",
|
||||
"counterexample_result": "1 passed in0.77s (test asserts gap); first collection missed imported bao_dev fixture, then corrected; not an application defect",
|
||||
"counterexample_file_sha256": "sha256:9da1cc8a2d5997962921da6e389aeb28b1d9c46112d33a04c24bd87a0eb81582",
|
||||
"disposition": "changes_requested; phase-aware read-only recovery required, no replay/remint permission",
|
||||
"native_accepted": false
|
||||
}
|
||||
|
|
@ -0,0 +1,774 @@
|
|||
--- a/scripts/flex_auth_signer_custody.py
|
||||
+++ b/scripts/flex_auth_signer_custody.py
|
||||
@@ -58,6 +58,10 @@
|
||||
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 @@
|
||||
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(),
|
||||
diff --git a/scripts/flex_auth_signer_native.py b/scripts/flex_auth_signer_native.py
|
||||
new file mode 100644
|
||||
index 0000000..0cd7a34
|
||||
--- /dev/null
|
||||
+++ b/scripts/flex_auth_signer_native.py
|
||||
@@ -0,0 +1,384 @@
|
||||
+#!/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'}
|
||||
+ 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')
|
||||
+ 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('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 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['ccr_sha256'] and receipt.get('writer_sha256')==contract['source_bundle']['scripts/flex_auth_signer_custody.py'] and
|
||||
+ receipt.get('native_custody') is True and receipt.get('bindings')==contract['bindings'] and
|
||||
+ type(receipt.get('public_key')) is str and re.fullmatch(r'[A-Za-z0-9_-]{43}',receipt['public_key']),'original custody receipt 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),'admission_sha256':digest(encoded(contract)),
|
||||
+ 'status':'recovery-read-intent-unconfirmed','kid':contract['kid'],'kv_version':1,
|
||||
+ 'public_key':receipt['public_key'],'public_fingerprint':receipt['public_fingerprint'],
|
||||
+ '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)
|
||||
+ exact_metadata(*io.http('GET',META),created=True)
|
||||
+ status,value=io.http('GET',PATH+'?version=1');require(status==200 and type(value) is dict,'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')
|
||||
+ 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())
|
||||
diff --git a/tests/test_flex_auth_signer_native.py b/tests/test_flex_auth_signer_native.py
|
||||
new file mode 100644
|
||||
index 0000000..a8ee432
|
||||
--- /dev/null
|
||||
+++ b/tests/test_flex_auth_signer_native.py
|
||||
@@ -0,0 +1,214 @@
|
||||
+"""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}
|
||||
+ 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):
|
||||
+ 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==''
|
||||
diff --git a/docs/credential-lane-designs/flex-auth-envelope-signing-native-adapter.md b/docs/credential-lane-designs/flex-auth-envelope-signing-native-adapter.md
|
||||
new file mode 100644
|
||||
index 0000000..2d88649
|
||||
--- /dev/null
|
||||
+++ b/docs/credential-lane-designs/flex-auth-envelope-signing-native-adapter.md
|
||||
@@ -0,0 +1,119 @@
|
||||
+# 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.
|
||||
--- a/workplans/RPF-WP-0035-credential-lane-implementation.md
|
||||
+++ b/workplans/RPF-WP-0035-credential-lane-implementation.md
|
||||
@@ -1354,3 +1354,14 @@
|
||||
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.
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"schema": "railiance-platform.signer-native-adapter-source.v1",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-ADAPTER-20261016-R1",
|
||||
"canonical_base": "7be8b6e76d602a5306052f9e42881fbea250ede8",
|
||||
"requires_native_preparation_patch_sha256": "a9d4631d3320ac3fa56f5a61965005c546eae241d2004cc1e6385e04668b7f7f",
|
||||
"isolated_source": "/tmp/netkingdom-platform-signer-native-adapter-20261016",
|
||||
"patch_sha256": "f316a53b604f4333414b832691968ede8056d49a61635f0f25831084b1d75f19",
|
||||
"files": {
|
||||
"scripts/flex_auth_signer_custody.py": {
|
||||
"before_sha256": "8ed281a49c1f1cdb23772efe3593e75c62572a12b8d7a6a4c574f27b38722e75",
|
||||
"after_sha256": "f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c"
|
||||
},
|
||||
"scripts/flex_auth_signer_native.py": {
|
||||
"before_sha256": null,
|
||||
"after_sha256": "11418b56bf5b5b44d926d8ad7f40c478cfb8f85189ca2ca03826bd0c05fc28c1"
|
||||
},
|
||||
"tests/test_flex_auth_signer_native.py": {
|
||||
"before_sha256": null,
|
||||
"after_sha256": "a368dfaebaa88621b5800236a96ec27efa4b8c568de39bb5108dc84457b7ef2f"
|
||||
},
|
||||
"docs/credential-lane-designs/flex-auth-envelope-signing-native-adapter.md": {
|
||||
"before_sha256": null,
|
||||
"after_sha256": "8a33a92c928d9e78d57d996dc0323045f608fe8ffdd247221d78e1973f44a724"
|
||||
},
|
||||
"workplans/RPF-WP-0035-credential-lane-implementation.md": {
|
||||
"before_sha256": "bab368699a4f80ac0f9092a467c7f4b50c9831c4543dc86a3f51eabb82fdf320",
|
||||
"after_sha256": "19184125dfefdcb71b0be0263c3225a014e6c8cd93185f15b9e65a331ae1e5ed"
|
||||
}
|
||||
},
|
||||
"production_admission": false,
|
||||
"authority_granted": false
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"schema": "railiance-platform.owner-source-review.v1",
|
||||
"reviewer_role": "railiance-platform-owner",
|
||||
"agent": "railiance-platform (codex)",
|
||||
"reviewed_revision_sha256": "f316a53b604f4333414b832691968ede8056d49a61635f0f25831084b1d75f19",
|
||||
"disposition": "approve-source-only-pending-independent-coordinator-review",
|
||||
"basis": "Existing RPF-WP-0035-T12 owner scope and explicit delegated native-adapter source authorization; no live scope inferred",
|
||||
"assessment": "Fixed silent attended adapter reuses existing custody route and protected Ed25519 receipt primitive. Exact reviewed inputs and real identity/cluster/reader metadata/fence checks precede fixed metadata/CAS0 effects; bounded I/O and durable intent preserve ambiguity. Fresh recovery is read-only with exact original receipt/source/bindings and public/private correspondence, preserving old unknown. Fixture live-process gate remains intact.",
|
||||
"limitations": [
|
||||
"Routing mismatch blocks live action until owner repair and exact receipt.",
|
||||
"Canonical proposed CCR is not approved/binding-confirmed; no real owner/founder disposition or candidate image/caller trust selection supplied.",
|
||||
"Admission JSON/digest/review references are independently reviewed exact-command inputs, not self-authorizing evidence.",
|
||||
"Tests prove actual disposable KV effects and process loss; identity and Kubernetes fence facts are synthetic, not native acceptance.",
|
||||
"Cluster fence checks are observations, not atomic locks. Independent Access/cluster exclusive-window control must pause all competing writers/controllers.",
|
||||
"Contained Warden helper/private modes do not prove same-UID hostile-code isolation or autopilot promotion.",
|
||||
"Native scope is bootstrapCAS0/version1 read-only reconciliation only; rotation remains separate reviewed action.",
|
||||
"No ESO/Secret/PVC/outbox/audit egress/producer or consumer deployment activation.",
|
||||
"Distinct Access, Secrets, cluster/GitOps and founder reviews remain unrecorded by Platform."
|
||||
],
|
||||
"preserved": [
|
||||
"signer R1/R2/R3",
|
||||
"native-preparation R1",
|
||||
"budget/fullchain source proof",
|
||||
"CCR0048/CCR0049",
|
||||
"WORK-RECORDS.md"
|
||||
],
|
||||
"incremental_source_spend_eur": 0,
|
||||
"authority_granted": false
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Protected signer CAS primitive. Disposable owner fixture only; no live entry.
|
||||
|
||||
This module deliberately cannot admit production custody. A reviewed native CCR,
|
||||
protected executor, fencing and founder scope are prerequisites for that adapter.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass,field
|
||||
from datetime import datetime,timezone
|
||||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
import http.client
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import secrets
|
||||
import subprocess
|
||||
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
||||
from task_execution_journal import Journal,Refused
|
||||
from task_execution_native_fixture import _absolute_budget
|
||||
from task_execution_recovery import _json_unique,_invalid_constant
|
||||
|
||||
PATH='platform/data/workloads/flex-auth/envelope-signing'
|
||||
FIELDS={'ED25519_PRIVATE_KEY','ED25519_PUBLIC_KEY','kid'}
|
||||
BINDINGS={'proposal_sha256','executed_revision_sha256','deployment_sha256','image_sha256','policy_sha256','trust_sha256'}
|
||||
|
||||
|
||||
def encoded(value):return json.dumps(value,sort_keys=True,separators=(',',':'),allow_nan=False).encode()
|
||||
def b64(value):return base64.urlsafe_b64encode(value).decode().rstrip('=')
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FixtureWriterAdmission:
|
||||
port:int
|
||||
owner_token:str=field(repr=False)
|
||||
server_process:subprocess.Popen=field(repr=False)
|
||||
fenced:bool=False
|
||||
|
||||
|
||||
class SignerWriter:
|
||||
"""Single attempted operation; failed/unknown writes never retry or remint.
|
||||
|
||||
File permissions are accidental-disclosure guards, not same-UID isolation.
|
||||
The receipt directory must be independent owner custody in a native adapter.
|
||||
Python cannot promise seed zeroization; process lifetime is the boundary.
|
||||
"""
|
||||
def __init__(self,admission:FixtureWriterAdmission,directory:Path,operation_id:str,*,kid:str,expected_version:int,bindings:dict):
|
||||
if (type(admission) is not FixtureWriterAdmission or type(admission.port) is not int
|
||||
or not 1<=admission.port<=65535 or type(admission.owner_token) is not str
|
||||
or not admission.owner_token or type(admission.fenced) is not bool or not admission.fenced):
|
||||
raise Refused('exact fenced fixture custody required')
|
||||
process=admission.server_process
|
||||
if (type(process) is not subprocess.Popen or process.poll() is not None or type(process.args) is not list
|
||||
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')
|
||||
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
|
||||
or type(bindings) is not dict or set(bindings)!=BINDINGS
|
||||
or any(type(v) is not str or not re.fullmatch(r'sha256:[0-9a-f]{64}',v) for v in bindings.values())):
|
||||
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.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(),
|
||||
'actor':'synthetic-owner-fixture','created_at':datetime.now(timezone.utc).isoformat(),'path':PATH,'expected_version':expected_version,'kid':kid,
|
||||
'status':'prepared','production_admission':False,'rollback':'forward-recovery-only',
|
||||
'public_key':None,'public_fingerprint':None,'kv_version':None,'exact_readback':False}
|
||||
self._save(initial=True)
|
||||
|
||||
def _save(self,*,initial=False):
|
||||
raw=encoded(self.receipt)
|
||||
if len(raw)>65536:raise Refused('receipt bound exceeded')
|
||||
if initial:
|
||||
target=self.path
|
||||
else:
|
||||
fd=os.open(self.path,os.O_RDONLY|os.O_NOFOLLOW|os.O_NONBLOCK)
|
||||
try:Journal._private(os.fstat(fd))
|
||||
finally:os.close(fd)
|
||||
target=self.directory/('.pending-'+secrets.token_hex(16))
|
||||
fd=os.open(target,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
|
||||
try:
|
||||
with os.fdopen(fd,'wb',closefd=False) as stream:stream.write(raw);stream.flush();os.fsync(fd)
|
||||
finally:os.close(fd)
|
||||
if not initial:os.replace(target,self.path)
|
||||
fd=os.open(self.directory,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW)
|
||||
try:os.fsync(fd)
|
||||
finally:os.close(fd)
|
||||
|
||||
def _http(self,method,path,body=None):
|
||||
# Fixed loopback owner process and fixed data path; never arbitrary URL.
|
||||
if self.admission.server_process.poll() is not None:raise Refused('fixture custody backend unavailable')
|
||||
with _absolute_budget(2.0):
|
||||
conn=http.client.HTTPConnection('127.0.0.1',self.admission.port,timeout=2)
|
||||
try:
|
||||
conn.request(method,'/v1/'+path,body=None if body is None else encoded(body),headers={
|
||||
'X-Vault-Token':self.admission.owner_token,'Content-Type':'application/json'})
|
||||
response=conn.getresponse();raw=response.read(1048577)
|
||||
if len(raw)>1048576:raise Refused('backend response bound exceeded')
|
||||
value=json.loads(raw,object_pairs_hook=_json_unique,parse_constant=_invalid_constant) if raw else {}
|
||||
return response.status,value
|
||||
finally:conn.close()
|
||||
|
||||
def write(self):
|
||||
if self.attempted:raise Refused('signer operation already attempted')
|
||||
self.attempted=True
|
||||
# All source/receipt checks precede generation; no seed import, argv,
|
||||
# environment, temporary file or public output interface exists.
|
||||
private=Ed25519PrivateKey.generate()
|
||||
seed=b64(private.private_bytes(serialization.Encoding.Raw,serialization.PrivateFormat.Raw,serialization.NoEncryption()))
|
||||
public=b64(private.public_key().public_bytes(serialization.Encoding.Raw,serialization.PublicFormat.Raw))
|
||||
document={'ED25519_PRIVATE_KEY':seed,'ED25519_PUBLIC_KEY':public,'kid':self.kid}
|
||||
self.receipt.update(status='write-intent-unknown',public_key=public,
|
||||
public_fingerprint='sha256:'+hashlib.sha256(public.encode()).hexdigest())
|
||||
self._save() # A durable intent must exist before any possible effect.
|
||||
try:
|
||||
status,result=self._http('POST',PATH,{'options':{'cas':self.expected_version},'data':document})
|
||||
if status!=200 or type(result) is not dict:raise Refused('signer write unconfirmed')
|
||||
version=result.get('data',{}).get('version')
|
||||
if type(version) is not int or version!=self.expected_version+1:raise Refused('signer write version unconfirmed')
|
||||
self.receipt['kv_version']=version
|
||||
status,result=self._http('GET',PATH+'?version='+str(version))
|
||||
if status!=200 or type(result) is not dict:raise Refused('signer readback unconfirmed')
|
||||
data=result.get('data',{});stored=data.get('data') if type(data) is dict else None
|
||||
metadata=data.get('metadata') if type(data) is dict else None
|
||||
if (type(stored) is not dict or set(stored)!=FIELDS or any(type(v) is not str for v in stored.values())
|
||||
or type(metadata) is not dict or type(metadata.get('version')) is not int or metadata['version']!=version
|
||||
or any(not hmac.compare_digest(stored[name],document[name]) for name in FIELDS)):
|
||||
raise Refused('signer readback mismatch')
|
||||
self.receipt.update(status='custody-verified-pending-independent-public-admission',exact_readback=True)
|
||||
self._save()
|
||||
return dict(self.receipt)
|
||||
except BaseException:
|
||||
# Persist only closed metadata; unknown never implies absence or retry.
|
||||
# If receipt storage fails, the durable earlier intent remains unknown.
|
||||
self.receipt.update(status='write-intent-unknown',exact_readback=False)
|
||||
self._save()
|
||||
raise Refused('signer custody outcome unconfirmed') from None
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
raise SystemExit('production signer custody unadmitted; no live entry point')
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"command": "warden plan \"attended OpenBao platform administration for reviewed flex-auth signer custody CAS0 and fixed KV metadata\" --json",
|
||||
"observed_lane_id": "openbao-structure-survey",
|
||||
"observed_verdict": "founder_required",
|
||||
"observed_founder_act": "approve",
|
||||
"required_lane_id": "openbao-platform-admin-login",
|
||||
"required_founder_act": "oidc_login",
|
||||
"catalog_source": "wheel-bundled fallback",
|
||||
"package_version": "0.1.2",
|
||||
"catalog_content_hash": "c47065c0892f",
|
||||
"action": "reported to coordinator; execution remains refused; no credential-path workaround"
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"patch_sha256": "f316a53b604f4333414b832691968ede8056d49a61635f0f25831084b1d75f19",
|
||||
"checks": [
|
||||
{
|
||||
"command": "SECRETS_ENGINE_SOURCE_ROOT=/tmp/netkingdom-secrets-trusted-successor PYTHONPATH=/tmp/netkingdom-secrets-trusted-successor/src pytest -q tests/test_flex_auth_signer_native.py tests/test_flex_auth_signer_custody.py tests/test_flex_auth_signer_native_preparation.py",
|
||||
"result": "43 passed in 31.09s",
|
||||
"stage": "final implementation; before adding separate fixed HTTP transport test"
|
||||
},
|
||||
{
|
||||
"command": "same environment pytest -q tests/test_flex_auth_signer_native.py::test_native_fixed_http_transport_against_disposable_backend",
|
||||
"result": "1 passed in 0.83s",
|
||||
"stage": "final test file; exact frozen source"
|
||||
},
|
||||
{
|
||||
"command": "git apply --check implementation.patch on exact preimages/new-file absence",
|
||||
"result": "pass"
|
||||
}
|
||||
],
|
||||
"history": [
|
||||
{
|
||||
"result": "6 failed30passed; Path abstract-class exact-type check rejected PosixPath",
|
||||
"fix": "require concrete pathlib instance; no authority weakening"
|
||||
},
|
||||
{
|
||||
"result": "1 failed38passed; denied-read test fault omitted version query suffix",
|
||||
"fix": "fault exact versioned GET too; no verifier changes"
|
||||
},
|
||||
{
|
||||
"result": "43passed31.40s",
|
||||
"stage": "before exact selected receipt-directory check"
|
||||
}
|
||||
],
|
||||
"proof_scope": [
|
||||
"actual fresh disposable KV v2 metadata/CAS0/readback through native production state machine",
|
||||
"real fork executor process death after actual native CAS; fresh read-only reconciliation preserves original unknown; no POST/remint",
|
||||
"strict admission/source/image/caller/trust/routing/identity/fence refusals; mid-setup new pod blocks seed CAS",
|
||||
"damaged and type-damaged receipts/current-version drift/denied read/no fresh review refuse recovery",
|
||||
"exclusive custody window and operation/recovery replay refusal",
|
||||
"absolute2s tool timeout/1MiB output bounds and silent failures",
|
||||
"fixed native HTTP path/status/body handling against disposable backend; socket port redirected only by test"
|
||||
],
|
||||
"synthetic_only": [
|
||||
"owner review dispositions",
|
||||
"founder identity/session",
|
||||
"Kubernetes fence and deployment observations",
|
||||
"native transport fixture endpoint redirection"
|
||||
],
|
||||
"live_credentials_grants_deployments": false
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Native signer adapter R2
|
||||
|
||||
Frozen R1 and its counterexample remain unchanged. Apply r1-to-r2.patch afterR1,
|
||||
or implementation-r2.patch after canonical7be8b6e plus native-preparationR1.
|
||||
Both have exact source/preimages and apply-check receipts in manifest.json.
|
||||
|
||||
R2 closes metadata-only process-loss recovery, without new write/retry authority.
|
||||
Exact current absence or metadata0/data absence has no key/public admission and
|
||||
requires a new independently reviewed action. Ambiguous/denied reads remain
|
||||
unconfirmed. Original uncertainty is preserved. New recover admissions explicitly
|
||||
bind original_execution as well as the current source bundle.
|
||||
|
||||
The native plan current primitive hash now agrees with actual executed core;
|
||||
the previous pin is historical, and checked_sources enforces the current join.
|
||||
No self-reference is introduced: the plan names the primitive, while an external
|
||||
admission pins the complete executor source bundle.
|
||||
|
||||
120seconds is unchanged. Detailed scheduling assessment and unchanged refusal
|
||||
requirements are in the source document. Source review/native inputs and reviews
|
||||
remain distinct; no credential, key, grant, role or deployment was changed live.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"schema": "railiance-platform.signer-native-adapter-r2.v1",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-ADAPTER-20261016-R2",
|
||||
"preserved_R1_patch_sha256": "f316a53b604f4333414b832691968ede8056d49a61635f0f25831084b1d75f19",
|
||||
"canonical_base": "7be8b6e76d602a5306052f9e42881fbea250ede8",
|
||||
"requires_native_preparation_sha256": "a9d4631d3320ac3fa56f5a61965005c546eae241d2004cc1e6385e04668b7f7f",
|
||||
"isolated_source": "/tmp/netkingdom-platform-signer-native-adapter-r2-20261016",
|
||||
"delta": {
|
||||
"patch_sha256": "81f4c2867d09accc33a22f7537526c0bcf86c4e45803639af0c602b6446c9ab1",
|
||||
"files": {
|
||||
"scripts/flex_auth_signer_native.py": {
|
||||
"before_sha256": "11418b56bf5b5b44d926d8ad7f40c478cfb8f85189ca2ca03826bd0c05fc28c1",
|
||||
"after_sha256": "5a3b3b33bcad45ef2939413bfd3db76e5ee70e3b815d0d64af53147851aa3a0d"
|
||||
},
|
||||
"tests/test_flex_auth_signer_native.py": {
|
||||
"before_sha256": "a368dfaebaa88621b5800236a96ec27efa4b8c568de39bb5108dc84457b7ef2f",
|
||||
"after_sha256": "8a99d1819c54fc20660cd6d16e8b429e2fc9f30cf4cefc90704201a588c825a6"
|
||||
},
|
||||
"docs/credential-lane-designs/flex-auth-envelope-signing-native-plan.json": {
|
||||
"before_sha256": "abc7c97eee8c059ee1d93b6e48e6f6277c4b8810623510d5cede20fd889feb44",
|
||||
"after_sha256": "5f8c37a7d9781884bf06050be80ca192a3b92eab2d20e28f9b304e57e7456844"
|
||||
},
|
||||
"docs/credential-lane-designs/flex-auth-envelope-signing-native-adapter.md": {
|
||||
"before_sha256": "8a33a92c928d9e78d57d996dc0323045f608fe8ffdd247221d78e1973f44a724",
|
||||
"after_sha256": "0e185a651fbbef96996c38ff18ce338f4d6c0117c3fb069a43e766816e56d5db"
|
||||
},
|
||||
"workplans/RPF-WP-0035-credential-lane-implementation.md": {
|
||||
"before_sha256": "19184125dfefdcb71b0be0263c3225a014e6c8cd93185f15b9e65a331ae1e5ed",
|
||||
"after_sha256": "180731a9ac1a2b4ccb4458c6cee54151f43e63008e2c35a33db7f01ea9e9d768"
|
||||
}
|
||||
},
|
||||
"apply_check": "pass"
|
||||
},
|
||||
"full": {
|
||||
"patch_sha256": "c5b8f0d10650f13eb5939874bda1fb457b3dc5835e63e043af9824e43edb9f80",
|
||||
"files": {
|
||||
"scripts/flex_auth_signer_custody.py": {
|
||||
"before_sha256": "8ed281a49c1f1cdb23772efe3593e75c62572a12b8d7a6a4c574f27b38722e75",
|
||||
"after_sha256": "f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c"
|
||||
},
|
||||
"scripts/flex_auth_signer_native.py": {
|
||||
"before_sha256": null,
|
||||
"after_sha256": "5a3b3b33bcad45ef2939413bfd3db76e5ee70e3b815d0d64af53147851aa3a0d"
|
||||
},
|
||||
"tests/test_flex_auth_signer_native.py": {
|
||||
"before_sha256": null,
|
||||
"after_sha256": "8a99d1819c54fc20660cd6d16e8b429e2fc9f30cf4cefc90704201a588c825a6"
|
||||
},
|
||||
"docs/credential-lane-designs/flex-auth-envelope-signing-native-plan.json": {
|
||||
"before_sha256": "abc7c97eee8c059ee1d93b6e48e6f6277c4b8810623510d5cede20fd889feb44",
|
||||
"after_sha256": "5f8c37a7d9781884bf06050be80ca192a3b92eab2d20e28f9b304e57e7456844"
|
||||
},
|
||||
"docs/credential-lane-designs/flex-auth-envelope-signing-native-adapter.md": {
|
||||
"before_sha256": null,
|
||||
"after_sha256": "0e185a651fbbef96996c38ff18ce338f4d6c0117c3fb069a43e766816e56d5db"
|
||||
},
|
||||
"workplans/RPF-WP-0035-credential-lane-implementation.md": {
|
||||
"before_sha256": "bab368699a4f80ac0f9092a467c7f4b50c9831c4543dc86a3f51eabb82fdf320",
|
||||
"after_sha256": "180731a9ac1a2b4ccb4458c6cee54151f43e63008e2c35a33db7f01ea9e9d768"
|
||||
}
|
||||
},
|
||||
"apply_check": "pass"
|
||||
},
|
||||
"source_primitive_sha256": "sha256:f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c",
|
||||
"execution_authority_granted": false,
|
||||
"native_accepted": false
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
# Exact Platform custody packet P2
|
||||
|
||||
`RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P2` proposes one attended bootstrap under native action `RPF35-PDP-SIGNER-NATIVE-20261016-R1`. It selects the existing legacy flex-auth repository's immutable candidate `forgejo.coulomb.social/coulomb/flex-auth@sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f`, source `84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0`. Ordinary publisher402 and both Smoke401 jobs succeeded; independent public package metadata reports the same sole digest and explicit revision label. P1 candidate0a and its post-push failed400 evidence stay unchanged. Candidate provenance grants no runtime/custody/trust admission.
|
||||
|
||||
`evidence-pins.json` binds exact committed Platform a3e8 source modules/plan, installed routing receipt, published candidate and local package d52/tree d4fb. Package baseline2b/current05a remains frozen; its local draft resolution is not adoption of84 or signer activation. `admission-scope.incomplete.json` is approval-free preparation and intentionally cannot execute. Missing nulls require the separate inputs below. Proposed failclosed pre-key consumer baseline and source projection, if filled, are pinned for review; they do not attest live installation or actual native fence. No new public key/fingerprint exists before custody.
|
||||
|
||||
## Next actual scoped owner-review inputs
|
||||
|
||||
1. Platform + founder: independently disposition exact CCR0050/read-policy/role/delivery scope and distinct one-use CAS0 writer scope. Current CCR remains proposed with bound_claims_confirmed=false; its current hash is evidence only and is not inserted as an approved CCR hash. After separately authorized apply/readback, bind exact approved/binding-confirmed bytes in ccr_sha256. Verify dedicated ESO SA/namespace/audience, exact policy, role15m ttl/max/explicit-max/no-default, no metadata/list/sibling/write grants. Source status edits are not native binding evidence.
|
||||
2. Access + cluster/GitOps: review exact P2 rollout source render and candidate image/kid/caller/mount/security/outbox values. Approve actual stopped deployment/frozen competing writers, stopped consumer, zero signer-SA/seed-mount/owned or terminating pods, no HPA, all owned ReplicaSets0 and deployment observed generation caught up. Supply authenticated exact future native deployment_projection artifact computed by the committed projection function and show it equals the proposed hash. A local source projection cannot establish these current observations. Supply exclusive-window artifact/hash naming founder executor/abort owner, competing controllers, readiness120s abort and recovery10m holdfenced. PVC1Gi/local-path/Helmkeep plus actual dynamically bound PV Retain, headroom/server-dry-run/client-diff and rollback are distinct approvals.
|
||||
3. Secrets: review exact built-wheel/helper/verifier/config/empty-trust baseline and planned install location. Pre-key empty trust is proposed failclosed and grants no signer acceptance. After authenticating Platform's public receipt, independently admit new key/kid/publickey fingerprint and successor trust, never discover from KV/responder. Fingerprint is SHA256 of canonical unpadded base64url public-key ASCII43bytes. Bounded TokenRequest/callerfile custody/installed trust/named-pod literal-loopback session remain separate approvals; no session is minted here.
|
||||
4. Founder/Platform: independently identify exact non-root OpenBao founder entity UUID, trusted runtime/dependencies/private custody directory, exact fixed command and admission file hash. No agent token lookup/login is required to prepare it. The child later verifies auth/netkingdom/platform-admin/entity and backend identity. Existing supervised record and Warden containment do not establish same-UID hostile-code isolation.
|
||||
5. Five distinct owner dispositions (Platform, Access, Secrets, cluster/GitOps, founder) must bind the completed approval-free scope SHA256, including exact approved CCR, source/image/projection/trust/window/expiry/entity. Platform's packet assent is preparation only. Collect actual external reference strings after their reviews; never manufacture them. Founder disposition must approve the exact private file hash and command/source plus high-risk custody/outage/loss scope.
|
||||
6. Select a future absolute UTC expires_at after coordinating the ceremony, not by auto-filling now+120. Reviews may finish before its window. Warden OIDC precedes child without proven timing bound; child must start at5<remaining<=120s, aim near120s. Native fixed path26 calls each capped2s (52s) plus local work, all under remaining absolute time. Too early/late, changed bytes or inadequate remaining time refuses; new expiry changes scope and needs new explicit dispositions. No post-login refresh, TTL widening or start-only reinterpretation.
|
||||
|
||||
## Final artifact and execution shape
|
||||
|
||||
Complete scope only from authenticated owner evidence. `finalize.py` is an offline assembler, not an approval authenticator; it rejects changed fixed candidate/source/lifecycle pins and selected baseline/projection pins, mismatched five external scope references and existing/symlink output. It creates0600 under permissive umask, flushes/fsyncs, and reports exact file SHA256. Native admission/source validation remains authoritative, including fresh expiry/CCR/native identity/fence checks. Keep input/output in an owner-private0700 directory outside Git. Do not submit this incomplete scope or synthetic test references as execution inputs.
|
||||
|
||||
After all scopes are actually approved, the exact argv shape to review is:
|
||||
|
||||
`warden access openbao-platform-admin-login --exec -- python3 /home/worsch/railiance-platform/scripts/flex_auth_signer_native.py --admission ABSOLUTE_OWNER_PRIVATE_APPROVED_FILE --admission-sha256 EXACT_APPROVED_FILE_SHA256 --receipt-directory /home/worsch/.local/state/railiance-platform/flex-auth-envelope-signing`
|
||||
|
||||
The two uppercase arguments remain ceremony outputs; no command is ready until established. Founder performs attended OIDC from their terminal; child is silent. Warden printed completion/revocation result is authoritative beyond exit code. No private seed, session or custody directory becomes shared intake. Expected result is independently authenticated public-only durable receipt with version1/kid/publickey/fingerprint, exact executed lineage and fence/readback; independent consumer trust and rollout proofs still follow.
|
||||
|
||||
## Verification, rollback, costs and risk
|
||||
|
||||
Target primary/railiance01 kube-system `a553c742-0115-43d4-99a4-a5ca56fe0786`, OpenBao `fd28df5d-98ec-57dd-42ec-9b3e4f4e53bf`, fixed loopback18200. One fixed metadata setup, in-memory Ed25519 seed CAS0 and exact version1 private correspondence readback; seed never crosses output/argv/env/local files. Final durable receipt/source/CCR/metadata/public correspondence must all verify before custody success; success still waits independent public admission.
|
||||
|
||||
Founder aborts; Platform reconciles custody; Access fences producer/consumer and owns release rollback; cluster owns controllers/PV. Before CAS abort preserves intent/fence. Any ambiguous metadata/write/readback/fsync remains unknown; no retry/remint. Fresh R2 read-only recovery binds immutable original receipt and original/current lineages: current absent, metadata-only0 and seed1 results do not rewrite historical unknown. Metadata-only confirmation does not authorize bootstrap; subsequent write needs separately reviewed source/action. No automatic cleanup/seed restoration/rotation.
|
||||
|
||||
Rollback stops writer, retains PVC/outbox/cursor/receipts, verifies lock release, and uses separately reviewed format-compatible prior image/values. Prior unsigned05a cannot satisfy strict signed consumer trust; holdfailclosed. Compromise withdraws online trust/stops signer, followed only by separately reviewed forward-only successor. Never restore compromised seed.
|
||||
|
||||
Preparation and Platform custody add EUR0 infrastructure/no new resource. Access's separate proposed1Gi local-path PVC is not quota/backup/node-loss durability; current StorageClass Delete/WaitForFirstConsumer requires actual binding/PV Retain disposition before durable acceptance. Outbox source64MiB/OS-lock hardening is published, native behavior remains unaccepted. Seed compromise may forge lifecycle decisions; controlled outage/local evidence loss remain high-risk. No monetary risk ceiling or autopilot grant exists; founder must explicitly disposition this exact risk/resource scope. No taskpilot/audit egress/new public package/standing writer permission is included.
|
||||
|
||||
Platform disposition: approve P2 preparation/source evidence selection for independent review only. Native execution, custody activation and other-owner dispositions remain withheld.
|
||||
|
||||
P2 frozen source selections: rollout manifest `sha256:6b9292aab59f9ca8012be00f50027ded6a12018a3b36b011ef953d448e75e3dc`, proposed native-encoded projection `sha256:8ce375f065552c551538f3acc7300c427407057eeff1d0b43755bfdf8a46169c`; Secrets manifest `sha256:7f4a49064b459a3b087943a00241eb9f744e3768f5c4139d1821d799ff2e584a`, proposed pre-key baseline `sha256:72521ff7729f8ddf5176092f3949cd50ac5f9241b0a5d1d30f3c63ce1d83091b`. These fill proposal projection/trust fields for exact review only. They establish neither actual stopped native evidence nor installed/online trust. Chart refuses replicas0; stop requires separately reviewed maintenance scope. Incomplete exclusive-window draft is deliberately not pinned as authority.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"schema": "railiance-platform.signer-native-admission.v1",
|
||||
"action": "bootstrap",
|
||||
"operation_id": "RPF35-PDP-SIGNER-NATIVE-20261016-R1",
|
||||
"kid": "flex-auth-lifecycle-20261016-r1",
|
||||
"expires_at": null,
|
||||
"founder_entity_id": null,
|
||||
"ccr_sha256": null,
|
||||
"source_bundle": {
|
||||
"scripts/flex_auth_signer_native.py": "sha256:5a3b3b33bcad45ef2939413bfd3db76e5ee70e3b815d0d64af53147851aa3a0d",
|
||||
"scripts/flex_auth_signer_custody.py": "sha256:f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c",
|
||||
"scripts/task_execution_native_fixture.py": "sha256:6d4e4132cd749aafd07c608c261902a2c347a9782f51fe275466f5bb800c68e8",
|
||||
"scripts/task_execution_journal.py": "sha256:604cf285286a8a1be23d9bc0d8a547bd1195b7f80286778e1d4f30c711fd6a7e",
|
||||
"scripts/task_execution_recovery.py": "sha256:910f38919a8b6f57591366a2ef4da40d1ed7dfd7556c845a4b89496eca39ce1c",
|
||||
"scripts/credential-change.py": "sha256:54ca1bc33ed4bb7807917f71ed45b6b35da978bc789e013386e1e90a20bee06d"
|
||||
},
|
||||
"bindings": {
|
||||
"proposal_sha256": "sha256:5f8c37a7d9781884bf06050be80ca192a3b92eab2d20e28f9b304e57e7456844",
|
||||
"executed_revision_sha256": "sha256:74318ae7bc37c91d331817f95306060977333fd4d4d3852f5935bc329f59da87",
|
||||
"deployment_sha256": "sha256:8ce375f065552c551538f3acc7300c427407057eeff1d0b43755bfdf8a46169c",
|
||||
"image_sha256": "sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"policy_sha256": "sha256:bd11c5fe77ce6439c65fea225ad6b71d2110efc5e7b5bc9b499c59cd0a53b8b4",
|
||||
"trust_sha256": "sha256:72521ff7729f8ddf5176092f3949cd50ac5f9241b0a5d1d30f3c63ce1d83091b"
|
||||
},
|
||||
"producer_image": "forgejo.coulomb.social/coulomb/flex-auth@sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"caller_binding": "secrets-engine=system:serviceaccount:secrets-engine:secrets-engine",
|
||||
"deployment_projection_sha256": "sha256:8ce375f065552c551538f3acc7300c427407057eeff1d0b43755bfdf8a46169c",
|
||||
"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:31df44742dc1abc37c6cb13554eacb37b8c0cabe9e2e4d23908e03d80c6ba238"
|
||||
},
|
||||
"exclusive_window_sha256": null,
|
||||
"consumer_trust_sha256": "sha256:72521ff7729f8ddf5176092f3949cd50ac5f9241b0a5d1d30f3c63ce1d83091b",
|
||||
"original_receipt_sha256": null,
|
||||
"original_execution": null
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
{
|
||||
"schema": "railiance-platform.native-packet-preparation.v1",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P2",
|
||||
"state": "incomplete preparation; never pass to native executor",
|
||||
"platform_commit": "a3e8eb64bae110393ba0aeab043de03a92b8421a",
|
||||
"source_bundle": {
|
||||
"scripts/flex_auth_signer_native.py": "sha256:5a3b3b33bcad45ef2939413bfd3db76e5ee70e3b815d0d64af53147851aa3a0d",
|
||||
"scripts/flex_auth_signer_custody.py": "sha256:f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c",
|
||||
"scripts/task_execution_native_fixture.py": "sha256:6d4e4132cd749aafd07c608c261902a2c347a9782f51fe275466f5bb800c68e8",
|
||||
"scripts/task_execution_journal.py": "sha256:604cf285286a8a1be23d9bc0d8a547bd1195b7f80286778e1d4f30c711fd6a7e",
|
||||
"scripts/task_execution_recovery.py": "sha256:910f38919a8b6f57591366a2ef4da40d1ed7dfd7556c845a4b89496eca39ce1c",
|
||||
"scripts/credential-change.py": "sha256:54ca1bc33ed4bb7807917f71ed45b6b35da978bc789e013386e1e90a20bee06d"
|
||||
},
|
||||
"native_plan_sha256": "sha256:5f8c37a7d9781884bf06050be80ca192a3b92eab2d20e28f9b304e57e7456844",
|
||||
"current_proposed_ccr_sha256": "sha256:72fd6e84e866a72f4768dabd66326aebe1fdd00013b2314de961ca72a632679f",
|
||||
"ccr_state": "proposed; bound_claims_confirmed false; intentionally not substituted for approved CCR pin",
|
||||
"published_image_candidate": {
|
||||
"schema": "access-engine.ordinary-ci-published-candidate.v1",
|
||||
"observed_at": "2026-10-16T17:55:53.102623+00:00",
|
||||
"source_revision": "84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0",
|
||||
"ordinary_push": true,
|
||||
"run_url": "https://forgejo.coulomb.social/coulomb/access-engine/actions/runs/402",
|
||||
"job_url": "https://forgejo.coulomb.social/coulomb/access-engine/actions/runs/402/jobs/0",
|
||||
"terminal_status": "success",
|
||||
"latest_commit_statuses": [
|
||||
{
|
||||
"id": 6,
|
||||
"status": "success",
|
||||
"context": "Build and Publish Container Image / build-and-push (push)",
|
||||
"description": "Successful in 58s",
|
||||
"target_url": "/coulomb/access-engine/actions/runs/402/jobs/0",
|
||||
"created_at": "2026-10-16T17:55:15Z",
|
||||
"updated_at": "2026-10-16T17:55:15Z"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"status": "success",
|
||||
"context": "CI Smoke / container-smoke (push)",
|
||||
"description": "Successful in 2s",
|
||||
"target_url": "/coulomb/access-engine/actions/runs/401/jobs/1",
|
||||
"created_at": "2026-10-16T17:54:17Z",
|
||||
"updated_at": "2026-10-16T17:54:17Z"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"status": "success",
|
||||
"context": "CI Smoke / host-smoke (push)",
|
||||
"description": "Successful in 0s",
|
||||
"target_url": "/coulomb/access-engine/actions/runs/401/jobs/0",
|
||||
"created_at": "2026-10-16T17:54:14Z",
|
||||
"updated_at": "2026-10-16T17:54:14Z"
|
||||
}
|
||||
],
|
||||
"registry_repository": "forgejo.coulomb.social/coulomb/flex-auth",
|
||||
"immutable_image": "forgejo.coulomb.social/coulomb/flex-auth@sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"manifest_digest": "sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"publisher_receipt": {
|
||||
"mode": "legacy",
|
||||
"revision": "84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0",
|
||||
"manifest_digest": "sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"verified_repositories": 1,
|
||||
"pulled_revision_label_check": "Passed by reviewed publisher before this final receipt; exact84 source script captured in source-facts.json.",
|
||||
"logs_url": "https://forgejo.coulomb.social/coulomb/access-engine/actions/runs/402/jobs/0/logs",
|
||||
"response_sha256": "e86883ca7b2d2c4fd9dc63be49b6840ea6e2e6f03807c97e0af52825631a8e42",
|
||||
"raw_log_retained": false
|
||||
},
|
||||
"independent_public_registry_package": {
|
||||
"url": "https://forgejo.coulomb.social/coulomb/-/packages/container/flex-auth/main-84c78e0",
|
||||
"http_status": 200,
|
||||
"sole_manifest_digest": "sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"explicit_revision_label": "84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0",
|
||||
"response_sha256": "4e7b2487509b1e58d12388db2a4c48b0c9c2df88ceaeeb53fde2d79ded5c677b"
|
||||
},
|
||||
"scope": {
|
||||
"published_by": "Existing normal main-push legacy publisher, not observer",
|
||||
"observer_actions": "Anonymous public metadata reads only; no dispatch/push/credential/login/deploy/trust operation.",
|
||||
"native_admitted": false,
|
||||
"limits": "Published source/image provenance is verified; exact runtime package/config/mount/caller/trust/record/PVC and owner scope admission remain separate.",
|
||||
"package_extraction_unchanged": "source2b064ee/current05a frozen; future84 adoption is a separate reviewed package/config revision.",
|
||||
"historical_failure_retained": "../image-0a05383/normal-ci-run400-failure.json"
|
||||
}
|
||||
},
|
||||
"managed_package": {
|
||||
"schema": "custodian.local-canonical-package.v1",
|
||||
"destination": "/home/worsch/rapp-flex-auth-secrets-engine",
|
||||
"commit": "d52f811ed5ec0be27c9c367e71d476125b3228be",
|
||||
"tree": "d4fbad71bdc8ef870603526ccd1f53195d0322af",
|
||||
"archive_sha256": "sha256:766279e86749bde4e25aafcb3989134e9879564667dd46a55be29bc995f66d8d",
|
||||
"files_sha256": {
|
||||
".repo-classification.yaml": "sha256:99a5c80dda99a732563dbb64b8ce8b8663d9d619b919f9fed96cb7a63598fbb4",
|
||||
"AGENTS.md": "sha256:a02928b531ce61765bfb09bb1abea548ddcb589ecef7c762a1851d8e7745c7c7",
|
||||
"CLAUDE.md": "sha256:70f13f7bdd8c420fe2820ac879e464e7333c4547a5b0adf1b5e5a7a14113fa00",
|
||||
"INTENT.md": "sha256:a40ff791a4070bcdc59b7f889412d2558c5cd943308890bc0bf0690ea78c3f67",
|
||||
"README.md": "sha256:d8f096b22d4e67ce6eb2a2e201720fbbf3bc54059fec98ddf3f6ec4229087361",
|
||||
"SCOPE.md": "sha256:6167744e2b3f4b82a38f4950463068966fe595c7d62d3674813a0e8ed66789de",
|
||||
"declarations/rapp.yaml": "sha256:0b722431bc5162dd86f72b15f12424e149aca8f2a8790adf2c57e8511ea73b4c",
|
||||
"docs/operations.md": "sha256:690c2140d86c68edcc09d9c9acda10f0a4f14723c4236d02dd9f832e74651d6e",
|
||||
"source-pin.json": "sha256:d28e8ce2f83ebb8dd8bfd2cd03d8308405d189a77d05f7294439ba1da31e1818",
|
||||
"tests/test_render.py": "sha256:5e7cfe42249eda989813275b5cd307e7a2b0be9696443e566d30578b720c8c6a",
|
||||
"tools/render.py": "sha256:93ca85ddd8036952ae98e31ac1f36ebfb1ac9ad0a2f73b1e90dcf067f4ae544c",
|
||||
"workplans/README.md": "sha256:29f2194b1cf0981172cd28928f74224c72778c1771413006a92e4b4ed8214511",
|
||||
"workplans/archived/.gitkeep": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"clean": true,
|
||||
"remote_configured": false,
|
||||
"published": false,
|
||||
"native_accepted": false,
|
||||
"scope": "Reviewed source package locally committed; resolver validation recorded separately. No placement, custody, trust or runtime admission."
|
||||
},
|
||||
"routing_receipt_sha256": "sha256:31df44742dc1abc37c6cb13554eacb37b8c0cabe9e2e4d23908e03d80c6ba238",
|
||||
"supersession": "P2 selects ordinary CI402 successful candidate84c78e0; P1 and historical run400 failure retained unchanged. Package source2b/current05a remains frozen, not signer activation.",
|
||||
"execution_authorized": false,
|
||||
"native_accepted": false,
|
||||
"credentials_requested_or_read": false,
|
||||
"incremental_infrastructure_spend_eur": 0,
|
||||
"predecessor": {
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P1",
|
||||
"evidence_pins_sha256": "sha256:22aa4e4dacea99dd9e77d2e7eae2a5916adcb6b75904001312254133e2b4b583",
|
||||
"assembler_correction_sha256": "sha256:a91e5ceb261a84ac2f3591d9973782660235ae49ad7cdff0d2fd40a22c2ccaff",
|
||||
"preserved": true
|
||||
},
|
||||
"candidate_manifest_sha256": "sha256:4f71eec333ee9bea798f22dc9190a08e0c3251f2f303d6bec636d1e82842efa5",
|
||||
"candidate_receipt_sha256": "sha256:af8e032d28776722ccf2bfe0f884577e568d8d0fb3713aa869e1d7d1c77c576f",
|
||||
"source_projection_selection": {
|
||||
"manifest_sha256": "sha256:6b9292aab59f9ca8012be00f50027ded6a12018a3b36b011ef953d448e75e3dc",
|
||||
"projection_file_sha256": "sha256:056b6bf842713fb9a328dae901401d6867da47da68bfb8a4904bb0d7b8d9e912",
|
||||
"projection_native_encoded_sha256": "sha256:8ce375f065552c551538f3acc7300c427407057eeff1d0b43755bfdf8a46169c",
|
||||
"path": "docs/changes/netkingdom-target-2026-10-16/access-native-admission/rollout-r1",
|
||||
"state": "proposed source-render only; actual stopped native projection/fence absent",
|
||||
"exclusive_window": "intentionally incomplete proposed artifact, not admitted/held; leave exclusive_window_sha256 null",
|
||||
"stop_path": "chart refuses replicas0; separate reviewed maintenance stop required"
|
||||
},
|
||||
"consumer_baseline_selection": {
|
||||
"manifest_sha256": "sha256:7f4a49064b459a3b087943a00241eb9f744e3768f5c4139d1821d799ff2e584a",
|
||||
"consumer_baseline_sha256": "sha256:72521ff7729f8ddf5176092f3949cd50ac5f9241b0a5d1d30f3c63ce1d83091b",
|
||||
"path": "docs/changes/netkingdom-target-2026-10-16/decision-native-preparation/consumer-admission-r1",
|
||||
"state": "proposed pre-key empty-trust source/config baseline only; no live installation, public key, post-key trust or owner approval"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
"""Offline assembler only. Receives externally reviewed facts; never approves or executes."""
|
||||
import hashlib,json,os,sys
|
||||
from pathlib import Path
|
||||
|
||||
def enc(x): return json.dumps(x,sort_keys=True,separators=(',',':'),allow_nan=False).encode()
|
||||
def digest(x): return 'sha256:'+hashlib.sha256(x).hexdigest()
|
||||
def contains_null(x):
|
||||
if isinstance(x,dict): return any(contains_null(v) for k,v in x.items() if k not in {'original_receipt_sha256','original_execution'})
|
||||
if isinstance(x,list): return any(map(contains_null,x))
|
||||
return x is None
|
||||
base=Path(__file__).parent
|
||||
if len(sys.argv)!=4: raise SystemExit('Usage: finalize.py complete-scope.json external-reviews.json output.json')
|
||||
scope=json.loads(Path(sys.argv[1]).read_text()); template=json.loads((base/'admission-scope.incomplete.json').read_text())
|
||||
if set(scope)!=set(template) or contains_null(scope): raise SystemExit('incomplete/extraneous scope')
|
||||
for name in ('schema','action','operation_id','kid','source_bundle','producer_image','caller_binding','authority','routing','original_receipt_sha256','original_execution'):
|
||||
if scope[name]!=template[name]: raise SystemExit('changed prepared scope requires new preparation review: '+name)
|
||||
for name in ('consumer_trust_sha256','deployment_projection_sha256'):
|
||||
if template[name] is not None and scope[name]!=template[name]: raise SystemExit('changed selected baseline requires new preparation review: '+name)
|
||||
for name in ('proposal_sha256','executed_revision_sha256','image_sha256','policy_sha256'):
|
||||
if scope['bindings'][name]!=template['bindings'][name]: raise SystemExit('changed binding requires new preparation review: '+name)
|
||||
if scope['bindings']['deployment_sha256']!=scope['deployment_projection_sha256'] or scope['bindings']['trust_sha256']!=scope['consumer_trust_sha256']: raise SystemExit('binding correspondence')
|
||||
reviews=json.loads(Path(sys.argv[2]).read_text())
|
||||
if set(reviews)!={'founder','railiance-platform-owner','access-engine-owner','secrets-engine-owner','cluster-gitops-owner'}: raise SystemExit('distinct external dispositions required')
|
||||
for review in reviews.values():
|
||||
if set(review)!={'approved_admission_sha256','approval_ref'} or review['approved_admission_sha256']!=digest(enc(scope)) or not review['approval_ref']: raise SystemExit('external review does not bind scope')
|
||||
admission=dict(scope,owner_reviews=reviews); raw=enc(admission)+b'\n'
|
||||
# Native validator remains authoritative, including fresh expiry and approved CCR checks.
|
||||
fd=os.open(sys.argv[3],os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
|
||||
with os.fdopen(fd,'wb') as out:
|
||||
os.fchmod(out.fileno(),0o600)
|
||||
out.write(raw)
|
||||
out.flush()
|
||||
os.fsync(out.fileno())
|
||||
print(json.dumps({'scope_sha256':digest(enc(scope)),'admission_file_sha256':digest(raw),'authority':'No approval authenticated by assembler; external governance and native validation still required'}))
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"schema": "railiance-platform.native-packet-preparation-manifest.v1",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P2",
|
||||
"owner": "railiance-platform-owner",
|
||||
"disposition": "approve-preparation/source-selection-for-independent-review-only",
|
||||
"files_sha256": {
|
||||
"README.md": "sha256:1e0b70c222043456950961c05e1c7cd46bcafcbf8691b0b2632e485b9ac82901",
|
||||
"admission-scope.incomplete.json": "sha256:f2b71aef0f34ba17b23f302a978a18f2caafbd8819624121930e402bb47644ff",
|
||||
"evidence-pins.json": "sha256:1717881298d3461b0f2f84a15c0b24e92c6e20c6f1e2e964ba89854589f85019",
|
||||
"finalize.py": "sha256:691d7e25644bcbec3748172f31f5b92f9d67cdfe205510dc15797566e11e83e9",
|
||||
"remaining-inputs.json": "sha256:a5f77eeac025d3cbdc2fd5a4eb8f29445f90fee7f0ddb3124e6a964cef87046b",
|
||||
"test_finalize.py": "sha256:93430c4c2b5293170589f5421d6314cb5babc29e3b3316f6962b3586de4b3789",
|
||||
"verification.json": "sha256:3ab7a4b0e3d52012eb213f30e5dbee237409ca27f0dbade86433dd6d2b1119fb"
|
||||
},
|
||||
"predecessor_p1_changed": false,
|
||||
"native_execution_authorized": false,
|
||||
"approval_authenticated": false,
|
||||
"source_projection_is_actual_native_fence": false,
|
||||
"pre_key_baseline_is_admitted_online_trust": false
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"schema": "railiance-platform.native-packet-owner-inputs.v1",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P2",
|
||||
"inputs": [
|
||||
{"owner": "Platform+founder", "input": "Actual exact CCR0050 read-lane disposition, native role/policy/bound-claims readback, approved/binding-confirmed CCR bytes and hash; separate fixed one-use CAS0 writer approval", "fills": ["ccr_sha256"], "state": "open; source CCR remains proposed"},
|
||||
{"owner": "Access+cluster/GitOps", "input": "Exact reviewed release/delivery/PVC/retention and maintained stop path; metadata observations proving actual current stopped signer fence and native projection equality; frozen competing creators with actual IDs/revisions/executor/times/abort owner", "fills": ["exclusive_window_sha256"], "state": "source projection selected; no actual fence/held window"},
|
||||
{"owner": "Secrets", "input": "Independently disposition exact proposed pre-key empty-trust baseline and installed bundle/config/custody/transport; post-custody authenticate public receipt and independently approve successor trust/key", "fills": ["consumer_trust_sha256"], "state": "proposed failclosed baseline selection only; no installation/key admission"},
|
||||
{"owner": "founder+Platform attended identity", "input": "Independently established exact non-root OIDC entity and trusted custody execution runtime/private paths; no agent token lookup or login", "fills": ["founder_entity_id"], "state": "open"},
|
||||
{"owner": "all five reviewers+founder", "input": "Coordinated future absolute expiry and exact claim-free scope digest, five distinct externally established dispositions, founder exact file/command/risk/resource approval", "fills": ["expires_at", "owner_reviews", "final admission file SHA256"], "state": "open; no approvals authenticated by assembler"}
|
||||
],
|
||||
"source_projection_selected_not_observed": true,
|
||||
"pre_key_baseline_selected_not_admitted": true,
|
||||
"actual_native_authority": false,
|
||||
"public_key_or_fingerprint_available": false
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
"""Disposable assembler checks; synthetic approvals are never native authority."""
|
||||
import copy,hashlib,json,os,stat,subprocess,sys,tempfile,unittest
|
||||
from pathlib import Path
|
||||
BASE=Path(__file__).resolve().parent
|
||||
|
||||
def enc(x): return json.dumps(x,sort_keys=True,separators=(',',':'),allow_nan=False).encode()
|
||||
class FinalizeTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp=tempfile.TemporaryDirectory();self.addCleanup(self.tmp.cleanup)
|
||||
self.root=Path(self.tmp.name)
|
||||
self.scope=json.loads((BASE/'admission-scope.incomplete.json').read_text())
|
||||
self.scope.update(expires_at='2000-01-01T00:00:00Z',founder_entity_id='00000000-0000-0000-0000-000000000000',ccr_sha256='sha256:'+'1'*64,deployment_projection_sha256='sha256:'+'2'*64,consumer_trust_sha256='sha256:'+'3'*64,exclusive_window_sha256='sha256:'+'4'*64)
|
||||
self.scope['deployment_projection_sha256']=json.loads((BASE/'admission-scope.incomplete.json').read_text())['deployment_projection_sha256'] or self.scope['deployment_projection_sha256']
|
||||
self.scope['consumer_trust_sha256']=json.loads((BASE/'admission-scope.incomplete.json').read_text())['consumer_trust_sha256'] or self.scope['consumer_trust_sha256']
|
||||
self.scope['bindings']['deployment_sha256']=self.scope['deployment_projection_sha256']
|
||||
self.scope['bindings']['trust_sha256']=self.scope['consumer_trust_sha256']
|
||||
def run_assembler(self,scope=None,target=None):
|
||||
scope=self.scope if scope is None else scope
|
||||
(self.root/'scope.json').write_text(json.dumps(scope))
|
||||
pin='sha256:'+hashlib.sha256(enc(scope)).hexdigest()
|
||||
reviews={role:{'approved_admission_sha256':pin,'approval_ref':'synthetic-test-only'} for role in ('founder','railiance-platform-owner','access-engine-owner','secrets-engine-owner','cluster-gitops-owner')}
|
||||
(self.root/'reviews.json').write_text(json.dumps(reviews))
|
||||
return subprocess.run([sys.executable,str(BASE/'finalize.py'),str(self.root/'scope.json'),str(self.root/'reviews.json'),str(target or self.root/'out.json')],capture_output=True,preexec_fn=lambda:os.umask(0))
|
||||
def test_incomplete_preparation_refused(self):
|
||||
scope=json.loads((BASE/'admission-scope.incomplete.json').read_text())
|
||||
self.assertNotEqual(self.run_assembler(scope).returncode,0)
|
||||
self.assertFalse((self.root/'out.json').exists())
|
||||
def test_exact_bytes_and_private_mode_under_permissive_umask(self):
|
||||
result=self.run_assembler();self.assertEqual(result.returncode,0,result.stderr)
|
||||
path=self.root/'out.json';self.assertEqual(stat.S_IMODE(path.stat().st_mode),0o600)
|
||||
value=json.loads(path.read_bytes());self.assertEqual({k:v for k,v in value.items() if k!='owner_reviews'},self.scope)
|
||||
receipt=json.loads(result.stdout);self.assertEqual(receipt['admission_file_sha256'],'sha256:'+hashlib.sha256(path.read_bytes()).hexdigest())
|
||||
self.assertIn('No approval authenticated',receipt['authority'])
|
||||
def test_existing_output_refused_unchanged(self):
|
||||
target=self.root/'out.json';target.write_bytes(b'original');os.chmod(target,0o640)
|
||||
self.assertNotEqual(self.run_assembler().returncode,0)
|
||||
self.assertEqual(target.read_bytes(),b'original');self.assertEqual(stat.S_IMODE(target.stat().st_mode),0o640)
|
||||
def test_symlink_output_refused_without_target_change(self):
|
||||
target=self.root/'original';target.write_bytes(b'original');(self.root/'out.json').symlink_to(target)
|
||||
self.assertNotEqual(self.run_assembler().returncode,0);self.assertEqual(target.read_bytes(),b'original')
|
||||
def test_altered_fixed_bindings_refused_before_output(self):
|
||||
for field in ('proposal_sha256','executed_revision_sha256','image_sha256','policy_sha256'):
|
||||
with self.subTest(field=field):
|
||||
scope=copy.deepcopy(self.scope);scope['bindings'][field]='sha256:'+'f'*64
|
||||
self.assertNotEqual(self.run_assembler(scope).returncode,0)
|
||||
self.assertFalse((self.root/'out.json').exists())
|
||||
def test_selected_baselines_refused_if_changed(self):
|
||||
for field,binding in (('deployment_projection_sha256','deployment_sha256'),('consumer_trust_sha256','trust_sha256')):
|
||||
if json.loads((BASE/'admission-scope.incomplete.json').read_text())[field] is None: continue
|
||||
with self.subTest(field=field):
|
||||
scope=copy.deepcopy(self.scope);scope[field]='sha256:'+'f'*64;scope['bindings'][binding]=scope[field]
|
||||
self.assertNotEqual(self.run_assembler(scope).returncode,0);self.assertFalse((self.root/'out.json').exists())
|
||||
def test_altered_fixed_image_refused_even_matching_reviews(self):
|
||||
scope=copy.deepcopy(self.scope);scope['producer_image']='forgejo.coulomb.social/coulomb/flex-auth@sha256:'+'f'*64
|
||||
self.assertNotEqual(self.run_assembler(scope).returncode,0);self.assertFalse((self.root/'out.json').exists())
|
||||
if __name__=='__main__': unittest.main()
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"schema": "railiance-platform.packet-preparation-verification.v1",
|
||||
"tests": 7,
|
||||
"result": "passed",
|
||||
"command": "python3 test_finalize.py",
|
||||
"checks": [
|
||||
"actual0600 under umask000",
|
||||
"exact file digest",
|
||||
"incomplete scope refusal",
|
||||
"existing path overwrite refusal/bytes+mode retained",
|
||||
"symlink target refusal",
|
||||
"four fixed bindings and candidate image alteration refusal despite synthetic matching reviews",
|
||||
"selected sourceprojection and pre-key baseline alteration refusal"
|
||||
],
|
||||
"candidate_manifest_files_verified": true,
|
||||
"consumer_and_projection_final_manifest_digests_verified": true,
|
||||
"source_projection_native_encoded_hash_recomputed": true,
|
||||
"native_execution": false,
|
||||
"synthetic_test_reviews": "test-only, never approval evidence"
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Exact Platform custody packet P3
|
||||
|
||||
`RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P3` proposes one attended bootstrap under native action `RPF35-PDP-SIGNER-NATIVE-20261016-R1`. It selects the existing legacy flex-auth repository's immutable candidate `forgejo.coulomb.social/coulomb/flex-auth@sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f`, source `84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0`. Ordinary publisher402 and both Smoke401 jobs succeeded; independent public package metadata reports the same sole digest and explicit revision label. P1/P2 candidate/source/review and failed400 evidence stay unchanged. Candidate provenance grants no runtime/custody/trust admission.
|
||||
|
||||
`evidence-pins.json` binds exact committed Platform R5 `84c529f239c81e6d3ca337015700b03e3a862f53` source modules/plan, installed routing receipt, published candidate and local package d52/tree d4fb. Package baseline2b/current05a remains frozen; its local draft resolution is not adoption of84 or signer activation. `admission-scope.incomplete.json` is approval-free preparation and intentionally cannot execute. Missing nulls require the separate inputs below. Proposed failclosed pre-key consumer baseline and source projection, if filled, are pinned for review; they do not attest live installation or actual native fence. No new public key/fingerprint exists before custody.
|
||||
|
||||
## Next actual scoped owner-review inputs
|
||||
|
||||
1. Platform + founder: independently disposition exact CCR0050/read-policy/role/delivery scope and distinct one-use CAS0 writer scope. Current CCR remains proposed with bound_claims_confirmed=false; its current hash is evidence only and is not inserted as an approved CCR hash. After separately authorized apply/readback, bind exact approved/binding-confirmed bytes in ccr_sha256. Verify dedicated ESO SA/namespace/audience, exact policy, role15m ttl/max/explicit-max/no-default, no metadata/list/sibling/write grants. Source status edits are not native binding evidence.
|
||||
2. Access (workload/Helm), Platform (GitOps Application/sync-freeze integration), and cluster-runtime-storage-owner (runtime/storage/headroom), each within its separate scope: review exact P3 rollout source render and candidate image/kid/caller/mount/security/outbox values. Approve actual stopped deployment/frozen competing writers, stopped consumer, zero signer-SA/seed-mount/owned or terminating pods, no HPA, all owned ReplicaSets0 and deployment observed generation caught up. Supply authenticated exact future native deployment_projection artifact computed by the committed projection function and show it equals the proposed hash. A local source projection cannot establish these current observations. Supply exclusive-window artifact/hash naming founder executor/abort owner, competing controllers, readiness120s abort and recovery10m holdfenced. PVC1Gi/local-path/Helmkeep plus actual dynamically bound PV Retain, headroom/server-dry-run/client-diff and rollback are distinct approvals.
|
||||
3. Secrets: review exact built-wheel/helper/verifier/config/empty-trust baseline and planned install location. Pre-key empty trust is proposed failclosed and grants no signer acceptance. After authenticating Platform's public receipt, independently admit new key/kid/publickey fingerprint and successor trust, never discover from KV/responder. Fingerprint is SHA256 of canonical unpadded base64url public-key ASCII43bytes. Bounded TokenRequest/callerfile custody/installed trust/named-pod literal-loopback session remain separate approvals; no session is minted here.
|
||||
4. Founder/Platform: independently identify exact non-root OpenBao founder entity UUID, trusted runtime/dependencies/private custody directory, exact fixed command and admission file hash. No agent token lookup/login is required to prepare it. The child later verifies auth/netkingdom/platform-admin/entity and backend identity. Existing supervised record and Warden containment do not establish same-UID hostile-code isolation.
|
||||
5. Five distinct owner dispositions (Platform, Access, Secrets, cluster-runtime-storage-owner, founder) must bind the completed approval-free scope SHA256, including exact approved CCR, source/image/projection/trust/window/expiry/entity. Platform's packet assent is preparation only. Collect actual external reference strings after their reviews; never manufacture them. Founder disposition must approve the exact private file hash and command/source plus high-risk custody/outage/loss scope.
|
||||
6. Select a future absolute UTC expires_at after coordinating the ceremony, not by auto-filling now+120. Reviews may finish before its window. Warden OIDC precedes child without proven timing bound; child must start at5<remaining<=120s, aim near120s. Native fixed path26 calls each capped2s (52s) plus local work, all under remaining absolute time. Too early/late, changed bytes or inadequate remaining time refuses; new expiry changes scope and needs new explicit dispositions. No post-login refresh, TTL widening or start-only reinterpretation.
|
||||
|
||||
## Final artifact and execution shape
|
||||
|
||||
Complete scope only from authenticated owner evidence. `finalize.py` is an offline assembler, not an approval authenticator; it rejects changed fixed candidate/source/lifecycle pins and selected baseline/projection pins, mismatched five external scope references and existing/symlink output. It creates0600 under permissive umask, flushes/fsyncs, and reports exact file SHA256. Native admission/source validation remains authoritative, including fresh expiry/CCR/native identity/fence checks. Keep input/output in an owner-private0700 directory outside Git. Do not submit this incomplete scope or synthetic test references as execution inputs.
|
||||
|
||||
After all scopes are actually approved, the exact argv shape to review is:
|
||||
|
||||
`warden access openbao-platform-admin-login --exec -- python3 /home/worsch/railiance-platform/scripts/flex_auth_signer_native.py --admission ABSOLUTE_OWNER_PRIVATE_APPROVED_FILE --admission-sha256 EXACT_APPROVED_FILE_SHA256 --receipt-directory /home/worsch/.local/state/railiance-platform/flex-auth-envelope-signing`
|
||||
|
||||
The two uppercase arguments remain ceremony outputs; no command is ready until established. Founder performs attended OIDC from their terminal; child is silent. Warden printed completion/revocation result is authoritative beyond exit code. No private seed, session or custody directory becomes shared intake. Expected result is independently authenticated public-only durable receipt with version1/kid/publickey/fingerprint, exact executed lineage and fence/readback; independent consumer trust and rollout proofs still follow.
|
||||
|
||||
## Verification, rollback, costs and risk
|
||||
|
||||
Target primary/railiance01 kube-system `a553c742-0115-43d4-99a4-a5ca56fe0786`, OpenBao `fd28df5d-98ec-57dd-42ec-9b3e4f4e53bf`, fixed loopback18200. One fixed metadata setup, in-memory Ed25519 seed CAS0 and exact version1 private correspondence readback; seed never crosses output/argv/env/local files. Final durable receipt/source/CCR/metadata/public correspondence must all verify before custody success; success still waits independent public admission.
|
||||
|
||||
Founder aborts; Platform reconciles custody; Access fences producer/consumer and owns release rollback; cluster owns controllers/PV. Before CAS abort preserves intent/fence. Any ambiguous metadata/write/readback/fsync remains unknown; no retry/remint. Fresh R2 read-only recovery binds immutable original receipt and original/current lineages: current absent, metadata-only0 and seed1 results do not rewrite historical unknown. Metadata-only confirmation does not authorize bootstrap; subsequent write needs separately reviewed source/action. No automatic cleanup/seed restoration/rotation.
|
||||
|
||||
Rollback stops writer, retains PVC/outbox/cursor/receipts, verifies lock release, and uses separately reviewed format-compatible prior image/values. Prior unsigned05a cannot satisfy strict signed consumer trust; holdfailclosed. Compromise withdraws online trust/stops signer, followed only by separately reviewed forward-only successor. Never restore compromised seed.
|
||||
|
||||
Preparation and Platform custody add EUR0 infrastructure/no new resource. Access's separate proposed1Gi local-path PVC is not quota/backup/node-loss durability; current StorageClass Delete/WaitForFirstConsumer requires actual binding/PV Retain disposition before durable acceptance. Outbox source64MiB/OS-lock hardening is published, native behavior remains unaccepted. Seed compromise may forge lifecycle decisions; controlled outage/local evidence loss remain high-risk. No monetary risk ceiling or autopilot grant exists; founder must explicitly disposition this exact risk/resource scope. No taskpilot/audit egress/new public package/standing writer permission is included.
|
||||
|
||||
Platform disposition: approve P3 preparation/source evidence selection for independent review only. Native execution, custody activation and other-owner dispositions remain withheld.
|
||||
|
||||
## Corrected authority boundary and source lineage
|
||||
|
||||
Platform R5 commit `84c529f239c81e6d3ca337015700b03e3a862f53` replaces the conflated historical review role with `cluster-runtime-storage-owner`. No frozen P1/P2 disposition is reinterpreted. Platform owns custody/read-lane and existing GitOps Application/sync-freeze integration; Access owns workload/Helm release semantics and freeze of competing workload writers; cluster reviews runtime/storage/headroom/PV retention only. Enablement reviews only a separately proposed actual ArgoCD installation/controller configuration change, excluded here. New exact scope requires corrected five-role dispositions; none are supplied.
|
||||
|
||||
Image source Git revision84c78e0 and chart source Git revisiondc0420 are separate identities; successful image CI does not authenticate a chart pin or stopped native fence. RolloutR2 supplies the final reviewed source projection only. Actual founder entity, absolute expiry, held exclusive-window and native dispositions remain null. Actual native fence evidence is explicitly null in evidence-pins, even when the source projection hash is preselected for exact review. Approved CCR bytes remain null until actual approval/binding evidence exists.
|
||||
|
||||
Final rolloutR2 manifest `sha256:5be7a7f438624dcb35b7dbc96e7c9e5a8e2d5d1b1966cb4338aa5aecbe3a8b59` verified in full. Canonical source projection `sha256:8ce375f065552c551538f3acc7300c427407057eeff1d0b43755bfdf8a46169c` is preselected for proposed scope. Chart Git revision `dc042022f7c30671cf0a7605cf43ef6d96d04242` renders actual source replicas0 and1; image Git revision84 and immutable09a83b remain independently pinned. The old R1 maintenance-stop limitation is superseded by source chartR2 capability only; no native stop was performed. Held window remains false and native stopped fence absent; incomplete window draft cannot fill the admission window hash.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"schema": "railiance-platform.signer-native-admission.v1",
|
||||
"action": "bootstrap",
|
||||
"operation_id": "RPF35-PDP-SIGNER-NATIVE-20261016-R1",
|
||||
"kid": "flex-auth-lifecycle-20261016-r1",
|
||||
"expires_at": null,
|
||||
"founder_entity_id": null,
|
||||
"ccr_sha256": null,
|
||||
"source_bundle": {
|
||||
"scripts/flex_auth_signer_native.py": "sha256:4fb9ce6608ad04c4f5dc98a95796dc88a08a2453dc15c0d40c5c688fc02f573f",
|
||||
"scripts/flex_auth_signer_custody.py": "sha256:f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c",
|
||||
"scripts/task_execution_native_fixture.py": "sha256:6d4e4132cd749aafd07c608c261902a2c347a9782f51fe275466f5bb800c68e8",
|
||||
"scripts/task_execution_journal.py": "sha256:604cf285286a8a1be23d9bc0d8a547bd1195b7f80286778e1d4f30c711fd6a7e",
|
||||
"scripts/task_execution_recovery.py": "sha256:910f38919a8b6f57591366a2ef4da40d1ed7dfd7556c845a4b89496eca39ce1c",
|
||||
"scripts/credential-change.py": "sha256:54ca1bc33ed4bb7807917f71ed45b6b35da978bc789e013386e1e90a20bee06d"
|
||||
},
|
||||
"bindings": {
|
||||
"proposal_sha256": "sha256:22306d50e082fc35c5ff22c31e739a53c7584176969063f7907bc01d77e506e5",
|
||||
"executed_revision_sha256": "sha256:addde682d0e1993f665d05e66dcd9ab56ead72f0b85f292201a46ea494e56b71",
|
||||
"deployment_sha256": "sha256:8ce375f065552c551538f3acc7300c427407057eeff1d0b43755bfdf8a46169c",
|
||||
"image_sha256": "sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"policy_sha256": "sha256:bd11c5fe77ce6439c65fea225ad6b71d2110efc5e7b5bc9b499c59cd0a53b8b4",
|
||||
"trust_sha256": "sha256:72521ff7729f8ddf5176092f3949cd50ac5f9241b0a5d1d30f3c63ce1d83091b"
|
||||
},
|
||||
"producer_image": "forgejo.coulomb.social/coulomb/flex-auth@sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"caller_binding": "secrets-engine=system:serviceaccount:secrets-engine:secrets-engine",
|
||||
"deployment_projection_sha256": "sha256:8ce375f065552c551538f3acc7300c427407057eeff1d0b43755bfdf8a46169c",
|
||||
"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:31df44742dc1abc37c6cb13554eacb37b8c0cabe9e2e4d23908e03d80c6ba238"
|
||||
},
|
||||
"exclusive_window_sha256": null,
|
||||
"consumer_trust_sha256": "sha256:72521ff7729f8ddf5176092f3949cd50ac5f9241b0a5d1d30f3c63ce1d83091b",
|
||||
"original_receipt_sha256": null,
|
||||
"original_execution": null
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
{
|
||||
"schema": "railiance-platform.native-packet-preparation.v1",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P3",
|
||||
"state": "incomplete preparation; never pass to native executor",
|
||||
"platform_commit": "84c529f239c81e6d3ca337015700b03e3a862f53",
|
||||
"source_bundle": {
|
||||
"scripts/flex_auth_signer_native.py": "sha256:4fb9ce6608ad04c4f5dc98a95796dc88a08a2453dc15c0d40c5c688fc02f573f",
|
||||
"scripts/flex_auth_signer_custody.py": "sha256:f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c",
|
||||
"scripts/task_execution_native_fixture.py": "sha256:6d4e4132cd749aafd07c608c261902a2c347a9782f51fe275466f5bb800c68e8",
|
||||
"scripts/task_execution_journal.py": "sha256:604cf285286a8a1be23d9bc0d8a547bd1195b7f80286778e1d4f30c711fd6a7e",
|
||||
"scripts/task_execution_recovery.py": "sha256:910f38919a8b6f57591366a2ef4da40d1ed7dfd7556c845a4b89496eca39ce1c",
|
||||
"scripts/credential-change.py": "sha256:54ca1bc33ed4bb7807917f71ed45b6b35da978bc789e013386e1e90a20bee06d"
|
||||
},
|
||||
"native_plan_sha256": "sha256:22306d50e082fc35c5ff22c31e739a53c7584176969063f7907bc01d77e506e5",
|
||||
"current_proposed_ccr_sha256": "sha256:44bbbf4aff84c3b085afdb14708106fd9a12ac03a0c47774913931a87fb94ada",
|
||||
"ccr_state": "proposed; bound_claims_confirmed false; intentionally not substituted for approved CCR pin",
|
||||
"published_image_candidate": {
|
||||
"schema": "access-engine.ordinary-ci-published-candidate.v1",
|
||||
"observed_at": "2026-10-16T17:55:53.102623+00:00",
|
||||
"source_revision": "84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0",
|
||||
"ordinary_push": true,
|
||||
"run_url": "https://forgejo.coulomb.social/coulomb/access-engine/actions/runs/402",
|
||||
"job_url": "https://forgejo.coulomb.social/coulomb/access-engine/actions/runs/402/jobs/0",
|
||||
"terminal_status": "success",
|
||||
"latest_commit_statuses": [
|
||||
{
|
||||
"id": 6,
|
||||
"status": "success",
|
||||
"context": "Build and Publish Container Image / build-and-push (push)",
|
||||
"description": "Successful in 58s",
|
||||
"target_url": "/coulomb/access-engine/actions/runs/402/jobs/0",
|
||||
"created_at": "2026-10-16T17:55:15Z",
|
||||
"updated_at": "2026-10-16T17:55:15Z"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"status": "success",
|
||||
"context": "CI Smoke / container-smoke (push)",
|
||||
"description": "Successful in 2s",
|
||||
"target_url": "/coulomb/access-engine/actions/runs/401/jobs/1",
|
||||
"created_at": "2026-10-16T17:54:17Z",
|
||||
"updated_at": "2026-10-16T17:54:17Z"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"status": "success",
|
||||
"context": "CI Smoke / host-smoke (push)",
|
||||
"description": "Successful in 0s",
|
||||
"target_url": "/coulomb/access-engine/actions/runs/401/jobs/0",
|
||||
"created_at": "2026-10-16T17:54:14Z",
|
||||
"updated_at": "2026-10-16T17:54:14Z"
|
||||
}
|
||||
],
|
||||
"registry_repository": "forgejo.coulomb.social/coulomb/flex-auth",
|
||||
"immutable_image": "forgejo.coulomb.social/coulomb/flex-auth@sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"manifest_digest": "sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"publisher_receipt": {
|
||||
"mode": "legacy",
|
||||
"revision": "84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0",
|
||||
"manifest_digest": "sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"verified_repositories": 1,
|
||||
"pulled_revision_label_check": "Passed by reviewed publisher before this final receipt; exact84 source script captured in source-facts.json.",
|
||||
"logs_url": "https://forgejo.coulomb.social/coulomb/access-engine/actions/runs/402/jobs/0/logs",
|
||||
"response_sha256": "e86883ca7b2d2c4fd9dc63be49b6840ea6e2e6f03807c97e0af52825631a8e42",
|
||||
"raw_log_retained": false
|
||||
},
|
||||
"independent_public_registry_package": {
|
||||
"url": "https://forgejo.coulomb.social/coulomb/-/packages/container/flex-auth/main-84c78e0",
|
||||
"http_status": 200,
|
||||
"sole_manifest_digest": "sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f",
|
||||
"explicit_revision_label": "84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0",
|
||||
"response_sha256": "4e7b2487509b1e58d12388db2a4c48b0c9c2df88ceaeeb53fde2d79ded5c677b"
|
||||
},
|
||||
"scope": {
|
||||
"published_by": "Existing normal main-push legacy publisher, not observer",
|
||||
"observer_actions": "Anonymous public metadata reads only; no dispatch/push/credential/login/deploy/trust operation.",
|
||||
"native_admitted": false,
|
||||
"limits": "Published source/image provenance is verified; exact runtime package/config/mount/caller/trust/record/PVC and owner scope admission remain separate.",
|
||||
"package_extraction_unchanged": "source2b064ee/current05a frozen; future84 adoption is a separate reviewed package/config revision.",
|
||||
"historical_failure_retained": "../image-0a05383/normal-ci-run400-failure.json"
|
||||
}
|
||||
},
|
||||
"managed_package": {
|
||||
"schema": "custodian.local-canonical-package.v1",
|
||||
"destination": "/home/worsch/rapp-flex-auth-secrets-engine",
|
||||
"commit": "d52f811ed5ec0be27c9c367e71d476125b3228be",
|
||||
"tree": "d4fbad71bdc8ef870603526ccd1f53195d0322af",
|
||||
"archive_sha256": "sha256:766279e86749bde4e25aafcb3989134e9879564667dd46a55be29bc995f66d8d",
|
||||
"files_sha256": {
|
||||
".repo-classification.yaml": "sha256:99a5c80dda99a732563dbb64b8ce8b8663d9d619b919f9fed96cb7a63598fbb4",
|
||||
"AGENTS.md": "sha256:a02928b531ce61765bfb09bb1abea548ddcb589ecef7c762a1851d8e7745c7c7",
|
||||
"CLAUDE.md": "sha256:70f13f7bdd8c420fe2820ac879e464e7333c4547a5b0adf1b5e5a7a14113fa00",
|
||||
"INTENT.md": "sha256:a40ff791a4070bcdc59b7f889412d2558c5cd943308890bc0bf0690ea78c3f67",
|
||||
"README.md": "sha256:d8f096b22d4e67ce6eb2a2e201720fbbf3bc54059fec98ddf3f6ec4229087361",
|
||||
"SCOPE.md": "sha256:6167744e2b3f4b82a38f4950463068966fe595c7d62d3674813a0e8ed66789de",
|
||||
"declarations/rapp.yaml": "sha256:0b722431bc5162dd86f72b15f12424e149aca8f2a8790adf2c57e8511ea73b4c",
|
||||
"docs/operations.md": "sha256:690c2140d86c68edcc09d9c9acda10f0a4f14723c4236d02dd9f832e74651d6e",
|
||||
"source-pin.json": "sha256:d28e8ce2f83ebb8dd8bfd2cd03d8308405d189a77d05f7294439ba1da31e1818",
|
||||
"tests/test_render.py": "sha256:5e7cfe42249eda989813275b5cd307e7a2b0be9696443e566d30578b720c8c6a",
|
||||
"tools/render.py": "sha256:93ca85ddd8036952ae98e31ac1f36ebfb1ac9ad0a2f73b1e90dcf067f4ae544c",
|
||||
"workplans/README.md": "sha256:29f2194b1cf0981172cd28928f74224c72778c1771413006a92e4b4ed8214511",
|
||||
"workplans/archived/.gitkeep": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"clean": true,
|
||||
"remote_configured": false,
|
||||
"published": false,
|
||||
"native_accepted": false,
|
||||
"scope": "Reviewed source package locally committed; resolver validation recorded separately. No placement, custody, trust or runtime admission."
|
||||
},
|
||||
"routing_receipt_sha256": "sha256:31df44742dc1abc37c6cb13554eacb37b8c0cabe9e2e4d23908e03d80c6ba238",
|
||||
"supersession": "P3 pins committed Platform R5 precise owner boundary; frozen P1/P2 and original review/refusal history unchanged. Candidate84 image and consumer pre-key baseline unchanged. New source requires exact fresh native dispositions; none granted.",
|
||||
"execution_authorized": false,
|
||||
"native_accepted": false,
|
||||
"credentials_requested_or_read": false,
|
||||
"incremental_infrastructure_spend_eur": 0,
|
||||
"predecessor": {
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P2",
|
||||
"manifest_sha256": "sha256:1a4c0efbacaf361ae85da9042aebe4e49bc3f879a59b81902ba235b00ea38431",
|
||||
"preserved": true
|
||||
},
|
||||
"candidate_manifest_sha256": "sha256:4f71eec333ee9bea798f22dc9190a08e0c3251f2f303d6bec636d1e82842efa5",
|
||||
"candidate_receipt_sha256": "sha256:af8e032d28776722ccf2bfe0f884577e568d8d0fb3713aa869e1d7d1c77c576f",
|
||||
"source_projection_selection": {
|
||||
"manifest_sha256": "sha256:5be7a7f438624dcb35b7dbc96e7c9e5a8e2d5d1b1966cb4338aa5aecbe3a8b59",
|
||||
"path": "docs/changes/netkingdom-target-2026-10-16/access-native-admission/rollout-r2",
|
||||
"projection_native_encoded_sha256": "sha256:8ce375f065552c551538f3acc7300c427407057eeff1d0b43755bfdf8a46169c",
|
||||
"canonical_projection_file": "source-projection.canonical.json",
|
||||
"chart_source_revision": "dc042022f7c30671cf0a7605cf43ef6d96d04242",
|
||||
"image_source_revision": "84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0",
|
||||
"state": "proposed exact Helm-rendered0/1 source projection only; actual stopped native fence absent",
|
||||
"held_exclusive_window": false,
|
||||
"exclusive_window_sha256": null,
|
||||
"incomplete_window_proposal_sha256": "sha256:15a063dfe1b4237c458ce3e12ec1a19e87bf2a3b98f97fbe7dcdcdb8d121a675",
|
||||
"source_projection_module_platform_revision": "84c529f239c81e6d3ca337015700b03e3a862f53"
|
||||
},
|
||||
"consumer_baseline_selection": {
|
||||
"manifest_sha256": "sha256:7f4a49064b459a3b087943a00241eb9f744e3768f5c4139d1821d799ff2e584a",
|
||||
"consumer_baseline_sha256": "sha256:72521ff7729f8ddf5176092f3949cd50ac5f9241b0a5d1d30f3c63ce1d83091b",
|
||||
"path": "docs/changes/netkingdom-target-2026-10-16/decision-native-preparation/consumer-admission-r1",
|
||||
"state": "proposed pre-key empty-trust source/config baseline only; no live installation, public key, post-key trust or owner approval"
|
||||
},
|
||||
"actual_native_fence_receipt": null,
|
||||
"native_owner_reviews": null,
|
||||
"native_expires_at": null,
|
||||
"founder_entity_id": null,
|
||||
"corrected_owner_roles": [
|
||||
"founder",
|
||||
"railiance-platform-owner",
|
||||
"access-engine-owner",
|
||||
"secrets-engine-owner",
|
||||
"cluster-runtime-storage-owner"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
"""Offline assembler only. Receives externally reviewed facts; never approves or executes."""
|
||||
import hashlib,json,os,sys
|
||||
from pathlib import Path
|
||||
|
||||
def enc(x): return json.dumps(x,sort_keys=True,separators=(',',':'),allow_nan=False).encode()
|
||||
def digest(x): return 'sha256:'+hashlib.sha256(x).hexdigest()
|
||||
def contains_null(x):
|
||||
if isinstance(x,dict): return any(contains_null(v) for k,v in x.items() if k not in {'original_receipt_sha256','original_execution'})
|
||||
if isinstance(x,list): return any(map(contains_null,x))
|
||||
return x is None
|
||||
base=Path(__file__).parent
|
||||
if len(sys.argv)!=4: raise SystemExit('Usage: finalize.py complete-scope.json external-reviews.json output.json')
|
||||
scope=json.loads(Path(sys.argv[1]).read_text()); template=json.loads((base/'admission-scope.incomplete.json').read_text())
|
||||
if set(scope)!=set(template) or contains_null(scope): raise SystemExit('incomplete/extraneous scope')
|
||||
for name in ('schema','action','operation_id','kid','source_bundle','producer_image','caller_binding','authority','routing','original_receipt_sha256','original_execution'):
|
||||
if scope[name]!=template[name]: raise SystemExit('changed prepared scope requires new preparation review: '+name)
|
||||
for name in ('consumer_trust_sha256','deployment_projection_sha256'):
|
||||
if template[name] is not None and scope[name]!=template[name]: raise SystemExit('changed selected baseline requires new preparation review: '+name)
|
||||
for name in ('proposal_sha256','executed_revision_sha256','image_sha256','policy_sha256'):
|
||||
if scope['bindings'][name]!=template['bindings'][name]: raise SystemExit('changed binding requires new preparation review: '+name)
|
||||
if scope['bindings']['deployment_sha256']!=scope['deployment_projection_sha256'] or scope['bindings']['trust_sha256']!=scope['consumer_trust_sha256']: raise SystemExit('binding correspondence')
|
||||
reviews=json.loads(Path(sys.argv[2]).read_text())
|
||||
if set(reviews)!={'founder','railiance-platform-owner','access-engine-owner','secrets-engine-owner','cluster-runtime-storage-owner'}: raise SystemExit('distinct external dispositions required')
|
||||
for review in reviews.values():
|
||||
if set(review)!={'approved_admission_sha256','approval_ref'} or review['approved_admission_sha256']!=digest(enc(scope)) or not review['approval_ref']: raise SystemExit('external review does not bind scope')
|
||||
admission=dict(scope,owner_reviews=reviews); raw=enc(admission)+b'\n'
|
||||
# Native validator remains authoritative, including fresh expiry and approved CCR checks.
|
||||
fd=os.open(sys.argv[3],os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
|
||||
with os.fdopen(fd,'wb') as out:
|
||||
os.fchmod(out.fileno(),0o600)
|
||||
out.write(raw)
|
||||
out.flush()
|
||||
os.fsync(out.fileno())
|
||||
print(json.dumps({'scope_sha256':digest(enc(scope)),'admission_file_sha256':digest(raw),'authority':'No approval authenticated by assembler; external governance and native validation still required'}))
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"schema": "railiance-platform.native-packet-preparation-manifest.v1",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P3",
|
||||
"owner": "railiance-platform-owner",
|
||||
"disposition": "approve-preparation/source-selection-for-independent-review-only",
|
||||
"files_sha256": {
|
||||
"README.md": "sha256:4829434c5df1825f576b962ca9b51632ee19c9e4b87c8efa0a5864ccd1273200",
|
||||
"admission-scope.incomplete.json": "sha256:2531f29734540d9206cddf946faed6b6cf8599e78c737b2c2d7fecf8f614e6a6",
|
||||
"evidence-pins.json": "sha256:672ece6fa2e8787454d2676629a270ee6420edaa95ee8a86a00728fb1e29c61f",
|
||||
"finalize.py": "sha256:1d61324628d77a44aeddd1d1a7e8e413986e2018ac67a1ad715717732c9b0d1c",
|
||||
"remaining-inputs.json": "sha256:b2df50a3528d7b43b90c6d74d8058812b170ca602f216ee44a9c5e82746c708c",
|
||||
"test_finalize.py": "sha256:6c19aebac6ae3b8d76d25278bb45be1c14fe2e9c1a22ff2cafede9d96db09e00",
|
||||
"verification.json": "sha256:93c3f7dade9c65407726769e6e399388f153db9847b8c4aa96e6ecde14d6797b"
|
||||
},
|
||||
"predecessors_p1_p2_changed": false,
|
||||
"native_execution_authorized": false,
|
||||
"approval_authenticated": false,
|
||||
"source_projection_is_actual_native_fence": false,
|
||||
"pre_key_baseline_is_admitted_online_trust": false
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"schema": "railiance-platform.native-packet-owner-inputs.v1",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P3",
|
||||
"inputs": [
|
||||
{
|
||||
"owner": "Platform+founder",
|
||||
"input": "Actual exact CCR0050 read-lane disposition, native role/policy/bound-claims readback, approved/binding-confirmed CCR bytes and hash; separate fixed one-use CAS0 writer approval",
|
||||
"fills": [
|
||||
"ccr_sha256"
|
||||
],
|
||||
"state": "open; source CCR remains proposed"
|
||||
},
|
||||
{
|
||||
"owner": "Access owner",
|
||||
"input": "Exact workload/Helm candidate release/delivery/mount/outbox and stopped workload writers; authenticated actual stopped signer native projection/fence; source render is proposed only",
|
||||
"fills": [
|
||||
"exclusive_window_sha256 supporting workload observations"
|
||||
],
|
||||
"state": "native fence and approved execution open"
|
||||
},
|
||||
{
|
||||
"owner": "Platform owner",
|
||||
"input": "Existing GitOps Application/sync-freeze integration; identify/freeze competing Application writers under separate exact scope; no ArgoCD install/controller configuration change included",
|
||||
"fills": [
|
||||
"exclusive_window_sha256 supporting Application observations"
|
||||
],
|
||||
"state": "actual held window open"
|
||||
},
|
||||
{
|
||||
"owner": "cluster-runtime-storage-owner",
|
||||
"input": "Actual runtime identity/headroom/storage/PVC1Gi and bound PV retention review; excludes workload or GitOps Application authority. Enablement review only for a separately proposed actual ArgoCD installation/controller change.",
|
||||
"fills": [
|
||||
"exclusive_window_sha256 supporting runtime/storage observations"
|
||||
],
|
||||
"state": "independent runtime/storage scope, not combined assent"
|
||||
},
|
||||
{
|
||||
"owner": "Secrets",
|
||||
"input": "Independently disposition exact proposed pre-key empty-trust baseline and installed bundle/config/custody/transport; post-custody authenticate public receipt and independently approve successor trust/key",
|
||||
"fills": [
|
||||
"consumer_trust_sha256"
|
||||
],
|
||||
"state": "proposed failclosed baseline selection only; no installation/key admission"
|
||||
},
|
||||
{
|
||||
"owner": "founder+Platform attended identity",
|
||||
"input": "Independently established exact non-root OIDC entity and trusted custody execution runtime/private paths; no agent token lookup or login",
|
||||
"fills": [
|
||||
"founder_entity_id"
|
||||
],
|
||||
"state": "open"
|
||||
},
|
||||
{
|
||||
"owner": "all five reviewers+founder",
|
||||
"input": "Coordinated future absolute expiry and exact claim-free scope digest, five distinct externally established dispositions, founder exact file/command/risk/resource approval",
|
||||
"fills": [
|
||||
"expires_at",
|
||||
"owner_reviews",
|
||||
"final admission file SHA256"
|
||||
],
|
||||
"state": "open; no approvals authenticated by assembler"
|
||||
}
|
||||
],
|
||||
"source_projection_selected_not_observed": true,
|
||||
"pre_key_baseline_selected_not_admitted": true,
|
||||
"actual_native_authority": false,
|
||||
"public_key_or_fingerprint_available": false
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
"""Disposable assembler checks; synthetic approvals are never native authority."""
|
||||
import copy,hashlib,json,os,stat,subprocess,sys,tempfile,unittest
|
||||
from pathlib import Path
|
||||
BASE=Path(__file__).resolve().parent
|
||||
|
||||
def enc(x): return json.dumps(x,sort_keys=True,separators=(',',':'),allow_nan=False).encode()
|
||||
class FinalizeTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp=tempfile.TemporaryDirectory();self.addCleanup(self.tmp.cleanup)
|
||||
self.root=Path(self.tmp.name)
|
||||
self.scope=json.loads((BASE/'admission-scope.incomplete.json').read_text())
|
||||
self.scope.update(expires_at='2000-01-01T00:00:00Z',founder_entity_id='00000000-0000-0000-0000-000000000000',ccr_sha256='sha256:'+'1'*64,deployment_projection_sha256='sha256:'+'2'*64,consumer_trust_sha256='sha256:'+'3'*64,exclusive_window_sha256='sha256:'+'4'*64)
|
||||
self.scope['deployment_projection_sha256']=json.loads((BASE/'admission-scope.incomplete.json').read_text())['deployment_projection_sha256'] or self.scope['deployment_projection_sha256']
|
||||
self.scope['consumer_trust_sha256']=json.loads((BASE/'admission-scope.incomplete.json').read_text())['consumer_trust_sha256'] or self.scope['consumer_trust_sha256']
|
||||
self.scope['bindings']['deployment_sha256']=self.scope['deployment_projection_sha256']
|
||||
self.scope['bindings']['trust_sha256']=self.scope['consumer_trust_sha256']
|
||||
def run_assembler(self,scope=None,target=None,legacy_role=False):
|
||||
scope=self.scope if scope is None else scope
|
||||
(self.root/'scope.json').write_text(json.dumps(scope))
|
||||
pin='sha256:'+hashlib.sha256(enc(scope)).hexdigest()
|
||||
reviews={role:{'approved_admission_sha256':pin,'approval_ref':'synthetic-test-only'} for role in ('founder','railiance-platform-owner','access-engine-owner','secrets-engine-owner','cluster-runtime-storage-owner')}
|
||||
if legacy_role: reviews['cluster-gitops-owner']=reviews.pop('cluster-runtime-storage-owner')
|
||||
(self.root/'reviews.json').write_text(json.dumps(reviews))
|
||||
return subprocess.run([sys.executable,str(BASE/'finalize.py'),str(self.root/'scope.json'),str(self.root/'reviews.json'),str(target or self.root/'out.json')],capture_output=True,preexec_fn=lambda:os.umask(0))
|
||||
def test_legacy_combined_owner_role_refused(self):
|
||||
self.assertNotEqual(self.run_assembler(legacy_role=True).returncode,0)
|
||||
self.assertFalse((self.root/'out.json').exists())
|
||||
def test_incomplete_preparation_refused(self):
|
||||
scope=json.loads((BASE/'admission-scope.incomplete.json').read_text())
|
||||
self.assertNotEqual(self.run_assembler(scope).returncode,0)
|
||||
self.assertFalse((self.root/'out.json').exists())
|
||||
def test_exact_bytes_and_private_mode_under_permissive_umask(self):
|
||||
result=self.run_assembler();self.assertEqual(result.returncode,0,result.stderr)
|
||||
path=self.root/'out.json';self.assertEqual(stat.S_IMODE(path.stat().st_mode),0o600)
|
||||
value=json.loads(path.read_bytes());self.assertEqual({k:v for k,v in value.items() if k!='owner_reviews'},self.scope)
|
||||
receipt=json.loads(result.stdout);self.assertEqual(receipt['admission_file_sha256'],'sha256:'+hashlib.sha256(path.read_bytes()).hexdigest())
|
||||
self.assertIn('No approval authenticated',receipt['authority'])
|
||||
def test_existing_output_refused_unchanged(self):
|
||||
target=self.root/'out.json';target.write_bytes(b'original');os.chmod(target,0o640)
|
||||
self.assertNotEqual(self.run_assembler().returncode,0)
|
||||
self.assertEqual(target.read_bytes(),b'original');self.assertEqual(stat.S_IMODE(target.stat().st_mode),0o640)
|
||||
def test_symlink_output_refused_without_target_change(self):
|
||||
target=self.root/'original';target.write_bytes(b'original');(self.root/'out.json').symlink_to(target)
|
||||
self.assertNotEqual(self.run_assembler().returncode,0);self.assertEqual(target.read_bytes(),b'original')
|
||||
def test_altered_fixed_bindings_refused_before_output(self):
|
||||
for field in ('proposal_sha256','executed_revision_sha256','image_sha256','policy_sha256'):
|
||||
with self.subTest(field=field):
|
||||
scope=copy.deepcopy(self.scope);scope['bindings'][field]='sha256:'+'f'*64
|
||||
self.assertNotEqual(self.run_assembler(scope).returncode,0)
|
||||
self.assertFalse((self.root/'out.json').exists())
|
||||
def test_selected_baselines_refused_if_changed(self):
|
||||
for field,binding in (('deployment_projection_sha256','deployment_sha256'),('consumer_trust_sha256','trust_sha256')):
|
||||
if json.loads((BASE/'admission-scope.incomplete.json').read_text())[field] is None: continue
|
||||
with self.subTest(field=field):
|
||||
scope=copy.deepcopy(self.scope);scope[field]='sha256:'+'f'*64;scope['bindings'][binding]=scope[field]
|
||||
self.assertNotEqual(self.run_assembler(scope).returncode,0);self.assertFalse((self.root/'out.json').exists())
|
||||
def test_altered_fixed_image_refused_even_matching_reviews(self):
|
||||
scope=copy.deepcopy(self.scope);scope['producer_image']='forgejo.coulomb.social/coulomb/flex-auth@sha256:'+'f'*64
|
||||
self.assertNotEqual(self.run_assembler(scope).returncode,0);self.assertFalse((self.root/'out.json').exists())
|
||||
if __name__=='__main__': unittest.main()
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"schema": "railiance-platform.packet-preparation-verification.v1",
|
||||
"tests": 8,
|
||||
"result": "passed",
|
||||
"command": "python3 test_finalize.py",
|
||||
"checks": [
|
||||
"actual0600 under umask000",
|
||||
"exact output digest",
|
||||
"incomplete scope refused",
|
||||
"overwrite and symlink target preserved",
|
||||
"fixed four bindings and image alteration refused despite synthetic matching reviews",
|
||||
"selected baseline/projection changes refused",
|
||||
"legacy cluster-gitops-owner role refused"
|
||||
],
|
||||
"canonical_R5_six_module_hashes_verified": true,
|
||||
"candidate_manifest_files_verified": "P2 exact unchanged evidence retained",
|
||||
"consumer_final_manifest_sha256": "sha256:7f4a49064b459a3b087943a00241eb9f744e3768f5c4139d1821d799ff2e584a",
|
||||
"rollout_r2_all_manifest_files_verified": true,
|
||||
"canonical_projection_encoding_recomputed": true,
|
||||
"actual_native_fence": null,
|
||||
"owner_dispositions": null,
|
||||
"founder_entity_id": null,
|
||||
"native_expiry": null,
|
||||
"held_window": false,
|
||||
"native_execution": false,
|
||||
"synthetic_reviews": "test-only; not authenticated approval"
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# Exact Platform custody packet P1
|
||||
|
||||
Proposal `RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P1` prepares bootstrap action `RPF35-PDP-SIGNER-NATIVE-20261016-R1`. Scope: one attended non-root platform-admin OIDC child, fixed metadata configuration, one in-memory Ed25519 seed CAS0 and version1/private correspondence readback, public-only durable receipt. It grants no delivery, cluster mutation, caller session, trust, deployment, resource or task execution authority.
|
||||
|
||||
`evidence-pins.json` pins committed Platform a3e8eb64, the six executable modules, current proposed CCR and plan, actual installed Warden routing receipt, actually published candidate0a digest/revision and canonical local package d52f811/tree d4fbad7. Original source-only R1/R2 reviews and failure evidence are preserved. CI400 failed after push/pull because python3 was missing; publication is observed, successful CI/admission is not. Package d52 is locally committed, remote absent; frozen package baseline2b/current05a does not select signer candidate0a.
|
||||
|
||||
`admission-scope.incomplete.json` uses the native closed scope schema without owner_reviews. Nulls mean distinct required evidence, not placeholders to guess. It is deliberately unusable as native admission. Public key/fingerprint do not yet exist; no public trust successor can be asserted.
|
||||
|
||||
## Remaining exact inputs by owner
|
||||
|
||||
1. Platform + founder: resolve CCR0050 independently. Current committed CCR hash is in evidence-pins and remains proposed/bound_claims_confirmed=false. Reviewed read-policy/role apply and exact native readback must establish SA flex-auth-envelope-signing-eso, namespace flex-auth, audience openbao, role external-secrets-flex-auth-envelope-signing, 15m TTL/max/explicit-max, no default policy and exact generated read-policy. Native adapter checks these before CAS. Only resulting approved/binding-confirmed CCR bytes may fill ccr_sha256. Changing status and boolean locally cannot establish those facts or an approval. CAS writer authorization is a separate scope from this read CCR.
|
||||
2. Access + cluster/GitOps: select exact candidate publication disposition (explicitly accept post-push CI failure or independently reviewed successful successor; changed image needs revised packet), exact release values/render/source; disable competing Helm/GitOps writers and HPA, stop consumers and all signer pods, retain target deployment at replicas0. Supply reviewed exclusive-window artifact/hash naming executor, abort owner, frozen controllers, bounded readiness120s/recovery10m, no second signer-SA/seed-mount pod and PVC disposition. Signer projection must include immutable candidate image, caller enforce/flex-auth/fixed binding, selected kid, exact seed0440 volume/mount, UID/GID/fsGroup65532. Compute deployment_projection_sha256 using the committed native module's deployment_projection+encoded function against the exact stopped Deployment. Equalize bindings.deployment_sha256. A package draft or local rendered hash does not prove a stopped native fence.
|
||||
3. Secrets: independently select exact installed verifier/helper/wheel/policy/trust baseline manifest and authenticated source, provide consumer_trust_sha256 and equal bindings.trust_sha256. This selects the pre-bootstrap reviewed baseline, not a fabricated online new key. After custody, independently authenticate Platform public receipt and admit successor key/kid/fingerprint before reopening acceptance. Fingerprint is SHA256 of canonical unpadded base64url public-key ASCII43bytes. No private KV discovery. TokenRequest/callerfile custody, trust installation and named-pod loopback transport remain separately reviewed consumer actions.
|
||||
4. Founder/Platform attended identity owner: supply independently established exact non-root OpenBao founder entity UUID; no agent token lookup or login. Identity must match auth/netkingdom and platform-admin during execution. Confirm custody execution host, trusted Python/dependencies/source, existing Warden containment and owner-private receipt path. Same-UID code isolation is not inferred.
|
||||
5. All five distinct owners: pre-review completed claim-free scope with a specific future absolute UTC expires_at and exact source/CCR/image/trust/projection/window refs. Compute scope SHA256 from native encoded(scope); each external owner disposition must bind it. Founder decision supplies actual approval reference. Platform preparation/source assent below is not a native disposition. No pending review is converted to approval by the assembler.
|
||||
6. Scheduling: choose expires_at only with founder ceremony. Child must start at 5 < expires_at-now <=120s, aim near expires_at-120s. OIDC starts before child and has no proven timeout; reviews may finish beforehand, login may begin early. Fixed path has26 requests each capped2s (52s), plus local work under the same absolute expiry. Too early/late, changed bytes or insufficient time means refuse; new expiry changes scope and requires new explicit dispositions. No now+120s auto-fill, post-login refresh or TTL widening.
|
||||
|
||||
## Concrete finalization and attended action
|
||||
|
||||
Copy scope to an owner-private0700 directory, fill only externally established missing fields, retain its digest, then collect all five exact external dispositions. `finalize.py` assembles a separate admission file only after all fields and matching references are supplied; it does not authenticate approvals. Run it offline, keep result0600, record resulting exact file digest and immutable command in founder decision. Native validate_admission and checked_sources remain authoritative. No invocation command is ready while these inputs are absent.
|
||||
|
||||
The exact native argv shape (each item reviewed as an argument, no shell/secret interpolation) is:
|
||||
|
||||
`warden access openbao-platform-admin-login --exec -- python3 /home/worsch/railiance-platform/scripts/flex_auth_signer_native.py --admission ABSOLUTE_OWNER_PRIVATE_APPROVED_FILE --admission-sha256 EXACT_APPROVED_FILE_SHA256 --receipt-directory /home/worsch/.local/state/railiance-platform/flex-auth-envelope-signing`
|
||||
|
||||
The two capitalized arguments are intentionally unresolved ceremony outputs, not executable defaults. Founder alone performs attended OIDC in their terminal. Source remains silent. Verify Warden printed completion/revocation result rather than exit code alone. Agent receives separately authenticated closed metadata receipt, never private custody directory/session/seed.
|
||||
|
||||
Expected success: durable receipt pins actual executed source/CCR, target IDs, immutable image/deployment/policy/trust, version1/kid/publickey/fingerprint, fence and exact private/native metadata correspondence; acceptance remains pending independent public trust/delivery/runtime proof. Verify receipt authentically with Platform and Secrets; check no secrets in shared artifacts. Exact target: primary/railiance01 kube-system a553c742-0115-43d4-99a4-a5ca56fe0786; OpenBao fd28df5d-98ec-57dd-42ec-9b3e4f4e53bf through fixed18200 loopback.
|
||||
|
||||
## Abort, recovery, cost and risk
|
||||
|
||||
Founder aborts; Platform owns custody reconciliation; Access owns stopped signer/consumers and release rollback, cluster owner owns frozen competing controllers/PV. Before CAS: preserve durable intent/fence, no private write. Any metadata/write/readback/fsync ambiguity: preserve original unknown bytes, keep fence, do not retry/remint. R2 read-only recovery requires fresh exact admission, original receipt SHA and original execution lineage; distinguish current absent, metadata-only version0 and exact version1 correspondence without claiming historic no-effect. Metadata-only result does not authorize bootstrap; subsequent write requires a separately reviewed action. No automatic metadata cleanup, seed restore or rotation.
|
||||
|
||||
Candidate rollback retains PVC/log/cursor/receipt, stops writer and verifies lock released before an independently reviewed format-compatible prior image/values. Prior unsigned image05a can restore evaluation availability only; strict consumer remains failclosed. Compromise: immediately withdraw online old trust/fence, then separately reviewed forward-only successor action; never restore old seed.
|
||||
|
||||
Preparation and proposed Platform custody add EUR0 infrastructure and no new resource. Access separately proposes1Gi local-path PVC, Helmkeep and actual PV Retain before durable acceptance; current Delete/WaitForFirstConsumer requires exact binding/disposition review. Local-path size is no quota/backup/node-loss guarantee. Signer compromise can forge lifecycle decisions; outage and evidence loss are material. No quantified monetary loss ceiling is established: founder must disposition high-risk scope explicitly; no autopilot/risk budget granted. Hard outbox64MiB/process-lock source is in candidate0a, but native acceptance and independent evidence continuity remain open. Existing supervised custodian record confers no new authority.
|
||||
|
||||
Platform owner disposition: approve this exact preparation/source pinning for coordinator review only; withhold native execution/custody activation until the distinct inputs and actual founder disposition exist. No other owner role reviewed or approved here.
|
||||
|
||||
## P1 assembler R2 correction
|
||||
|
||||
Coordinator review found P1 output creation inherited the process umask. The corrected assembler uses exclusive/no-follow0600 creation, explicit0600 fchmod, flush/fsync. Five disposable tests passed, including umask000 mode, overwrite/symlink refusal and altered fixed-binding rejection with recomputed synthetic reviews. Original assembler bytes and manifest are retained; `assembler-r2-revision.json` binds both revisions. P1 image/source/evidence pins remain unchanged. No approvals are authenticated and no native action is admitted by this repair. A changed CI candidate requires a separately pinned packet revision.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"schema": "railiance-platform.signer-native-admission.v1",
|
||||
"action": "bootstrap",
|
||||
"operation_id": "RPF35-PDP-SIGNER-NATIVE-20261016-R1",
|
||||
"kid": "flex-auth-lifecycle-20261016-r1",
|
||||
"expires_at": null,
|
||||
"founder_entity_id": null,
|
||||
"ccr_sha256": null,
|
||||
"source_bundle": {
|
||||
"scripts/flex_auth_signer_native.py": "sha256:5a3b3b33bcad45ef2939413bfd3db76e5ee70e3b815d0d64af53147851aa3a0d",
|
||||
"scripts/flex_auth_signer_custody.py": "sha256:f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c",
|
||||
"scripts/task_execution_native_fixture.py": "sha256:6d4e4132cd749aafd07c608c261902a2c347a9782f51fe275466f5bb800c68e8",
|
||||
"scripts/task_execution_journal.py": "sha256:604cf285286a8a1be23d9bc0d8a547bd1195b7f80286778e1d4f30c711fd6a7e",
|
||||
"scripts/task_execution_recovery.py": "sha256:910f38919a8b6f57591366a2ef4da40d1ed7dfd7556c845a4b89496eca39ce1c",
|
||||
"scripts/credential-change.py": "sha256:54ca1bc33ed4bb7807917f71ed45b6b35da978bc789e013386e1e90a20bee06d"
|
||||
},
|
||||
"bindings": {
|
||||
"proposal_sha256": "sha256:5f8c37a7d9781884bf06050be80ca192a3b92eab2d20e28f9b304e57e7456844",
|
||||
"executed_revision_sha256": "sha256:74318ae7bc37c91d331817f95306060977333fd4d4d3852f5935bc329f59da87",
|
||||
"deployment_sha256": null,
|
||||
"image_sha256": "sha256:2dff23c4b4834a8296ad9d958505d6efc1449443dc0f90a763858f7bbbd1030e",
|
||||
"policy_sha256": "sha256:bd11c5fe77ce6439c65fea225ad6b71d2110efc5e7b5bc9b499c59cd0a53b8b4",
|
||||
"trust_sha256": null
|
||||
},
|
||||
"producer_image": "forgejo.coulomb.social/coulomb/flex-auth@sha256:2dff23c4b4834a8296ad9d958505d6efc1449443dc0f90a763858f7bbbd1030e",
|
||||
"caller_binding": "secrets-engine=system:serviceaccount:secrets-engine:secrets-engine",
|
||||
"deployment_projection_sha256": null,
|
||||
"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:31df44742dc1abc37c6cb13554eacb37b8c0cabe9e2e4d23908e03d80c6ba238"
|
||||
},
|
||||
"exclusive_window_sha256": null,
|
||||
"consumer_trust_sha256": null,
|
||||
"original_receipt_sha256": null,
|
||||
"original_execution": null
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"schema": "railiance-platform.packet-assembler-revision.v1",
|
||||
"revision": "P1-assembler-R2",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P1",
|
||||
"reason": "P1 exclusive open inherited umask and could produce0644. R2 explicitly creates0600 with nofollow/exclusive flags and verifies mode via focused tests.",
|
||||
"original_finalize_sha256": "sha256:585340228c8a23b1770c72d0226b068559e421eef689bf6742da150c5cd7ed78",
|
||||
"original_manifest_sha256": "sha256:3064c06ac9ba0b02fb95caa5176ba349d589775f5cbc5314e5075197018bad35",
|
||||
"files_sha256": {
|
||||
"finalize.py": "sha256:085e179b76abdbb43b61d3e94a0b2efda23d8c943e9e49fbf5d78a7acbbd0385",
|
||||
"p1-original-finalize.py": "sha256:585340228c8a23b1770c72d0226b068559e421eef689bf6742da150c5cd7ed78",
|
||||
"test_finalize.py": "sha256:db552c6699980b538b5b2c242bef68b0db8527f85cfb09951a6c94703dd00138"
|
||||
},
|
||||
"verification": {
|
||||
"command": "python3 test_finalize.py",
|
||||
"tests": 5,
|
||||
"result": "passed",
|
||||
"checks": [
|
||||
"0600 under umask000",
|
||||
"exact output file digest",
|
||||
"overwrite refusal preserving bytes/mode",
|
||||
"symlink refusal preserving target",
|
||||
"all four fixed bindings rejected despite recomputed synthetic reviews",
|
||||
"changed fixed image rejected"
|
||||
]
|
||||
},
|
||||
"original_image_and_evidence_pins_changed": false,
|
||||
"approval_authenticated": false,
|
||||
"native_authority": false,
|
||||
"native_execution": false,
|
||||
"owner_disposition": "Platform source/preparation repair only"
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
"schema": "railiance-platform.native-packet-preparation.v1",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P1",
|
||||
"state": "incomplete preparation; never pass to native executor",
|
||||
"platform_commit": "a3e8eb64bae110393ba0aeab043de03a92b8421a",
|
||||
"source_bundle": {
|
||||
"scripts/flex_auth_signer_native.py": "sha256:5a3b3b33bcad45ef2939413bfd3db76e5ee70e3b815d0d64af53147851aa3a0d",
|
||||
"scripts/flex_auth_signer_custody.py": "sha256:f169ad12f90d729b647085c3bed2c0d8d005db23a2341b6d3d5f1793bcfd110c",
|
||||
"scripts/task_execution_native_fixture.py": "sha256:6d4e4132cd749aafd07c608c261902a2c347a9782f51fe275466f5bb800c68e8",
|
||||
"scripts/task_execution_journal.py": "sha256:604cf285286a8a1be23d9bc0d8a547bd1195b7f80286778e1d4f30c711fd6a7e",
|
||||
"scripts/task_execution_recovery.py": "sha256:910f38919a8b6f57591366a2ef4da40d1ed7dfd7556c845a4b89496eca39ce1c",
|
||||
"scripts/credential-change.py": "sha256:54ca1bc33ed4bb7807917f71ed45b6b35da978bc789e013386e1e90a20bee06d"
|
||||
},
|
||||
"native_plan_sha256": "sha256:5f8c37a7d9781884bf06050be80ca192a3b92eab2d20e28f9b304e57e7456844",
|
||||
"current_proposed_ccr_sha256": "sha256:72fd6e84e866a72f4768dabd66326aebe1fdd00013b2314de961ca72a632679f",
|
||||
"ccr_state": "proposed; bound_claims_confirmed false; intentionally not substituted for approved CCR pin",
|
||||
"published_image_candidate": {
|
||||
"schema": "access-engine.published-image-candidate-observation.v1",
|
||||
"observed_at": "2026-10-16T17:03:02.075463+00:00",
|
||||
"source_revision": "0a05383195213e7eaacd08388ce1d7892ce756df",
|
||||
"repository": "forgejo.coulomb.social/coulomb/flex-auth",
|
||||
"tag": "main-0a05383",
|
||||
"manifest_digest": "sha256:2dff23c4b4834a8296ad9d958505d6efc1449443dc0f90a763858f7bbbd1030e",
|
||||
"immutable_reference": "forgejo.coulomb.social/coulomb/flex-auth@sha256:2dff23c4b4834a8296ad9d958505d6efc1449443dc0f90a763858f7bbbd1030e",
|
||||
"independent_public_package_observation": {
|
||||
"url": "https://forgejo.coulomb.social/coulomb/-/packages/container/flex-auth/main-0a05383",
|
||||
"http_status": 200,
|
||||
"response_sha256": "b25eff0fda847c7d45fffe0023717771c00889621d2c694655eb53ce4019b498",
|
||||
"sole_reported_manifest_digest": "sha256:2dff23c4b4834a8296ad9d958505d6efc1449443dc0f90a763858f7bbbd1030e",
|
||||
"explicit_revision_label": "0a05383195213e7eaacd08388ce1d7892ce756df",
|
||||
"source": "Public registry package metadata from existing TLS origin; no credential or token exchange."
|
||||
},
|
||||
"ordinary_ci": {
|
||||
"run_url": "https://forgejo.coulomb.social/coulomb/access-engine/actions/runs/400",
|
||||
"commit_status": "failure",
|
||||
"push_digest_and_authenticated_pull_digest": "sha256:2dff23c4b4834a8296ad9d958505d6efc1449443dc0f90a763858f7bbbd1030e",
|
||||
"exact_failure": "tools/publish-runtime-images.sh line31 python3: command not found after push and pull",
|
||||
"receipt": "normal-ci-run400-failure.json",
|
||||
"no_fabrication": "Publication is observed; terminal workflow success is not claimed. Existing CI Smoke run399 jobs pass."
|
||||
},
|
||||
"local_build": {
|
||||
"index_digest": "sha256:1318339e44b518f206a77cf50636bbc6f772c173e20c6fc8dd2d91d8e2895e91",
|
||||
"scope": "Independent exact-source disposable build, not the published image; different labels/timestamps/attestation explain distinct digest.",
|
||||
"receipt": "local-provenance.json"
|
||||
},
|
||||
"admission": {
|
||||
"state": "observed published candidate; native acceptance withheld",
|
||||
"required": [
|
||||
"Independent coordinator review of publisher fix and explicit CI failure disposition or successful reviewed successor normal CI receipt.",
|
||||
"Exact native candidate policy/registry/binary/config/mount/caller/trust/record/PVC runtime verification under separately approved scope.",
|
||||
"Package authoritative declaration commit/review/resolution and Platform custody/consumer trust admission."
|
||||
],
|
||||
"no_actions": [
|
||||
"No manual push, workflow rerun, paired/public package creation, deploy, credential fetch, key or trust mutation occurred."
|
||||
]
|
||||
}
|
||||
},
|
||||
"managed_package": {
|
||||
"schema": "custodian.local-canonical-package.v1",
|
||||
"destination": "/home/worsch/rapp-flex-auth-secrets-engine",
|
||||
"commit": "d52f811ed5ec0be27c9c367e71d476125b3228be",
|
||||
"tree": "d4fbad71bdc8ef870603526ccd1f53195d0322af",
|
||||
"archive_sha256": "sha256:766279e86749bde4e25aafcb3989134e9879564667dd46a55be29bc995f66d8d",
|
||||
"files_sha256": {
|
||||
".repo-classification.yaml": "sha256:99a5c80dda99a732563dbb64b8ce8b8663d9d619b919f9fed96cb7a63598fbb4",
|
||||
"AGENTS.md": "sha256:a02928b531ce61765bfb09bb1abea548ddcb589ecef7c762a1851d8e7745c7c7",
|
||||
"CLAUDE.md": "sha256:70f13f7bdd8c420fe2820ac879e464e7333c4547a5b0adf1b5e5a7a14113fa00",
|
||||
"INTENT.md": "sha256:a40ff791a4070bcdc59b7f889412d2558c5cd943308890bc0bf0690ea78c3f67",
|
||||
"README.md": "sha256:d8f096b22d4e67ce6eb2a2e201720fbbf3bc54059fec98ddf3f6ec4229087361",
|
||||
"SCOPE.md": "sha256:6167744e2b3f4b82a38f4950463068966fe595c7d62d3674813a0e8ed66789de",
|
||||
"declarations/rapp.yaml": "sha256:0b722431bc5162dd86f72b15f12424e149aca8f2a8790adf2c57e8511ea73b4c",
|
||||
"docs/operations.md": "sha256:690c2140d86c68edcc09d9c9acda10f0a4f14723c4236d02dd9f832e74651d6e",
|
||||
"source-pin.json": "sha256:d28e8ce2f83ebb8dd8bfd2cd03d8308405d189a77d05f7294439ba1da31e1818",
|
||||
"tests/test_render.py": "sha256:5e7cfe42249eda989813275b5cd307e7a2b0be9696443e566d30578b720c8c6a",
|
||||
"tools/render.py": "sha256:93ca85ddd8036952ae98e31ac1f36ebfb1ac9ad0a2f73b1e90dcf067f4ae544c",
|
||||
"workplans/README.md": "sha256:29f2194b1cf0981172cd28928f74224c72778c1771413006a92e4b4ed8214511",
|
||||
"workplans/archived/.gitkeep": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"clean": true,
|
||||
"remote_configured": false,
|
||||
"published": false,
|
||||
"native_accepted": false,
|
||||
"scope": "Reviewed source package locally committed; resolver validation recorded separately. No placement, custody, trust or runtime admission."
|
||||
},
|
||||
"routing_receipt_sha256": "sha256:31df44742dc1abc37c6cb13554eacb37b8c0cabe9e2e4d23908e03d80c6ba238",
|
||||
"supersession": "Routing installed evidence supersedes the R1 routing blocker only. R1/R2 source reviews unchanged. Current package locally committed; prior absent/draft discovery history preserved. Package baseline 2b/current05a does not activate candidate0a or signer.",
|
||||
"execution_authorized": false,
|
||||
"native_accepted": false,
|
||||
"credentials_requested_or_read": false,
|
||||
"incremental_infrastructure_spend_eur": 0
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
"""Offline assembler only. Receives externally reviewed facts; never approves or executes."""
|
||||
import hashlib,json,os,sys
|
||||
from pathlib import Path
|
||||
|
||||
def enc(x): return json.dumps(x,sort_keys=True,separators=(',',':'),allow_nan=False).encode()
|
||||
def digest(x): return 'sha256:'+hashlib.sha256(x).hexdigest()
|
||||
def contains_null(x):
|
||||
if isinstance(x,dict): return any(contains_null(v) for k,v in x.items() if k not in {'original_receipt_sha256','original_execution'})
|
||||
if isinstance(x,list): return any(map(contains_null,x))
|
||||
return x is None
|
||||
base=Path(__file__).parent
|
||||
if len(sys.argv)!=4: raise SystemExit('Usage: finalize.py complete-scope.json external-reviews.json output.json')
|
||||
scope=json.loads(Path(sys.argv[1]).read_text()); template=json.loads((base/'admission-scope.incomplete.json').read_text())
|
||||
if set(scope)!=set(template) or contains_null(scope): raise SystemExit('incomplete/extraneous scope')
|
||||
for name in ('schema','action','operation_id','kid','source_bundle','producer_image','caller_binding','authority','routing','original_receipt_sha256','original_execution'):
|
||||
if scope[name]!=template[name]: raise SystemExit('changed prepared scope requires new preparation review: '+name)
|
||||
for name in ('proposal_sha256','executed_revision_sha256','image_sha256','policy_sha256'):
|
||||
if scope['bindings'][name]!=template['bindings'][name]: raise SystemExit('changed binding requires new preparation review: '+name)
|
||||
if scope['bindings']['deployment_sha256']!=scope['deployment_projection_sha256'] or scope['bindings']['trust_sha256']!=scope['consumer_trust_sha256']: raise SystemExit('binding correspondence')
|
||||
reviews=json.loads(Path(sys.argv[2]).read_text())
|
||||
if set(reviews)!={'founder','railiance-platform-owner','access-engine-owner','secrets-engine-owner','cluster-gitops-owner'}: raise SystemExit('distinct external dispositions required')
|
||||
for review in reviews.values():
|
||||
if set(review)!={'approved_admission_sha256','approval_ref'} or review['approved_admission_sha256']!=digest(enc(scope)) or not review['approval_ref']: raise SystemExit('external review does not bind scope')
|
||||
admission=dict(scope,owner_reviews=reviews); raw=enc(admission)+b'\n'
|
||||
# Native validator remains authoritative, including fresh expiry and approved CCR checks.
|
||||
fd=os.open(sys.argv[3],os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
|
||||
with os.fdopen(fd,'wb') as out:
|
||||
os.fchmod(out.fileno(),0o600)
|
||||
out.write(raw)
|
||||
out.flush()
|
||||
os.fsync(out.fileno())
|
||||
print(json.dumps({'scope_sha256':digest(enc(scope)),'admission_file_sha256':digest(raw),'authority':'No approval authenticated by assembler; external governance and native validation still required'}))
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P1",
|
||||
"owner": "railiance-platform-owner",
|
||||
"disposition": "approve-preparation-only; native authority withheld",
|
||||
"files_sha256": {
|
||||
"README.md": "sha256:b6709b718cc36d4a09811f28070fa741c83875ea714f999abaaabb937568777a",
|
||||
"admission-scope.incomplete.json": "sha256:d5b1ec3b28d75995e96fb87785dceef773ded780ccf478ea08d96f41fe4926b3",
|
||||
"evidence-pins.json": "sha256:22aa4e4dacea99dd9e77d2e7eae2a5916adcb6b75904001312254133e2b4b583",
|
||||
"finalize.py": "sha256:585340228c8a23b1770c72d0226b068559e421eef689bf6742da150c5cd7ed78",
|
||||
"preparation-verification.json": "sha256:eb99a734d155873f9923296b2cb5ea9b7bf791fbfa7b6fc57a05e9326bf37f83",
|
||||
"prepare.py": "sha256:f76a6aeb7f27341322ae2d236da134140aa18ecaeb9d31026bd219f3e2ad536d"
|
||||
},
|
||||
"original_r1_r2_changed": false,
|
||||
"native_execution": false
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
"""Offline assembler only. Receives externally reviewed facts; never approves or executes."""
|
||||
import hashlib,json,sys
|
||||
from pathlib import Path
|
||||
|
||||
def enc(x): return json.dumps(x,sort_keys=True,separators=(',',':'),allow_nan=False).encode()
|
||||
def digest(x): return 'sha256:'+hashlib.sha256(x).hexdigest()
|
||||
def contains_null(x):
|
||||
if isinstance(x,dict): return any(contains_null(v) for k,v in x.items() if k not in {'original_receipt_sha256','original_execution'})
|
||||
if isinstance(x,list): return any(map(contains_null,x))
|
||||
return x is None
|
||||
base=Path(__file__).parent
|
||||
if len(sys.argv)!=4: raise SystemExit('Usage: finalize.py complete-scope.json external-reviews.json output.json')
|
||||
scope=json.loads(Path(sys.argv[1]).read_text()); template=json.loads((base/'admission-scope.incomplete.json').read_text())
|
||||
if set(scope)!=set(template) or contains_null(scope): raise SystemExit('incomplete/extraneous scope')
|
||||
for name in ('schema','action','operation_id','kid','source_bundle','producer_image','caller_binding','authority','routing','original_receipt_sha256','original_execution'):
|
||||
if scope[name]!=template[name]: raise SystemExit('changed prepared scope requires new preparation review: '+name)
|
||||
for name in ('proposal_sha256','executed_revision_sha256','image_sha256','policy_sha256'):
|
||||
if scope['bindings'][name]!=template['bindings'][name]: raise SystemExit('changed binding requires new preparation review: '+name)
|
||||
if scope['bindings']['deployment_sha256']!=scope['deployment_projection_sha256'] or scope['bindings']['trust_sha256']!=scope['consumer_trust_sha256']: raise SystemExit('binding correspondence')
|
||||
reviews=json.loads(Path(sys.argv[2]).read_text())
|
||||
if set(reviews)!={'founder','railiance-platform-owner','access-engine-owner','secrets-engine-owner','cluster-gitops-owner'}: raise SystemExit('distinct external dispositions required')
|
||||
for review in reviews.values():
|
||||
if set(review)!={'approved_admission_sha256','approval_ref'} or review['approved_admission_sha256']!=digest(enc(scope)) or not review['approval_ref']: raise SystemExit('external review does not bind scope')
|
||||
admission=dict(scope,owner_reviews=reviews); raw=enc(admission)+b'\n'
|
||||
# Native validator remains authoritative, including fresh expiry and approved CCR checks.
|
||||
with Path(sys.argv[3]).open('xb') as out: out.write(raw)
|
||||
print(json.dumps({'scope_sha256':digest(enc(scope)),'admission_file_sha256':digest(raw),'authority':'No approval authenticated by assembler; external governance and native validation still required'}))
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"canonical_source_equals_committed": true,
|
||||
"source_bundle_sha256": "sha256:74318ae7bc37c91d331817f95306060977333fd4d4d3852f5935bc329f59da87",
|
||||
"plan_primitive_pin_corresponds": true,
|
||||
"image_candidate_observed_not_admitted": true,
|
||||
"native_checks_executed": false
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
from pathlib import Path
|
||||
import hashlib,json,subprocess
|
||||
P=Path('/home/worsch/railiance-platform'); C=Path('/home/worsch/the-custodian'); O=Path('/tmp/platform-native-packet-preparation')
|
||||
E=C/'docs/changes/netkingdom-target-2026-10-16'
|
||||
def h(raw): return 'sha256:'+hashlib.sha256(raw).hexdigest()
|
||||
def canonical(x): return json.dumps(x,sort_keys=True,separators=(',',':'),allow_nan=False).encode()
|
||||
def write(name,x): (O/name).write_text(json.dumps(x,indent=2)+'\n')
|
||||
def committed(name): return subprocess.check_output(['git','-C',str(P),'show','a3e8eb64bae110393ba0aeab043de03a92b8421a:'+name])
|
||||
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']
|
||||
source={name:h(committed(name)) for name in files}
|
||||
plan='docs/credential-lane-designs/flex-auth-envelope-signing-native-plan.json'; ccr='credential-change-requests/CCR-2026-0050-flex-auth-envelope-signing.yaml'
|
||||
route=E/'warden-admin-routing/installed-verification.json'; image=json.loads((E/'access-native-admission/image-0a05383/published-candidate.json').read_text())
|
||||
scope={'schema':'railiance-platform.signer-native-admission.v1','action':'bootstrap','operation_id':'RPF35-PDP-SIGNER-NATIVE-20261016-R1','kid':'flex-auth-lifecycle-20261016-r1','expires_at':None,'founder_entity_id':None,'ccr_sha256':None,'source_bundle':source,'bindings':{'proposal_sha256':h(committed(plan)),'executed_revision_sha256':h(canonical(source)),'deployment_sha256':None,'image_sha256':image['manifest_digest'],'policy_sha256':'sha256:bd11c5fe77ce6439c65fea225ad6b71d2110efc5e7b5bc9b499c59cd0a53b8b4','trust_sha256':None},'producer_image':image['immutable_reference'],'caller_binding':'secrets-engine=system:serviceaccount:secrets-engine:secrets-engine','deployment_projection_sha256':None,'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':h(route.read_bytes())},'exclusive_window_sha256':None,'consumer_trust_sha256':None,'original_receipt_sha256':None,'original_execution':None}
|
||||
write('admission-scope.incomplete.json',scope)
|
||||
write('evidence-pins.json',{'schema':'railiance-platform.native-packet-preparation.v1','proposal_id':'RPF35-PDP-SIGNER-NATIVE-PACKET-20261016-P1','state':'incomplete preparation; never pass to native executor','platform_commit':'a3e8eb64bae110393ba0aeab043de03a92b8421a','source_bundle':source,'native_plan_sha256':h(committed(plan)),'current_proposed_ccr_sha256':h(committed(ccr)),'ccr_state':'proposed; bound_claims_confirmed false; intentionally not substituted for approved CCR pin','published_image_candidate':image,'managed_package':json.loads((E/'access-package-identity/admission/canonical-creation.json').read_text()),'routing_receipt_sha256':h(route.read_bytes()),'supersession':'Routing installed evidence supersedes the R1 routing blocker only. R1/R2 source reviews unchanged. Current package locally committed; prior absent/draft discovery history preserved. Package baseline 2b/current05a does not activate candidate0a or signer.','execution_authorized':False,'native_accepted':False,'credentials_requested_or_read':False,'incremental_infrastructure_spend_eur':0})
|
||||
write('preparation-verification.json',{'canonical_source_equals_committed':all((P/name).read_bytes()==committed(name) for name in files+[plan,ccr]),'source_bundle_sha256':h(canonical(source)),'plan_primitive_pin_corresponds':json.loads(committed(plan))['bootstrap']['source_primitive_sha256']==source['scripts/flex_auth_signer_custody.py'],'image_candidate_observed_not_admitted':True,'native_checks_executed':False})
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
"""Disposable assembler checks; synthetic approvals are never native authority."""
|
||||
import copy,hashlib,json,os,stat,subprocess,sys,tempfile,unittest
|
||||
from pathlib import Path
|
||||
BASE=Path(__file__).resolve().parent
|
||||
|
||||
def enc(x): return json.dumps(x,sort_keys=True,separators=(',',':'),allow_nan=False).encode()
|
||||
class FinalizeTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp=tempfile.TemporaryDirectory();self.addCleanup(self.tmp.cleanup)
|
||||
self.root=Path(self.tmp.name)
|
||||
self.scope=json.loads((BASE/'admission-scope.incomplete.json').read_text())
|
||||
self.scope.update(expires_at='2000-01-01T00:00:00Z',founder_entity_id='00000000-0000-0000-0000-000000000000',ccr_sha256='sha256:'+'1'*64,deployment_projection_sha256='sha256:'+'2'*64,consumer_trust_sha256='sha256:'+'3'*64,exclusive_window_sha256='sha256:'+'4'*64)
|
||||
self.scope['bindings']['deployment_sha256']=self.scope['deployment_projection_sha256']
|
||||
self.scope['bindings']['trust_sha256']=self.scope['consumer_trust_sha256']
|
||||
def run_assembler(self,scope=None,target=None):
|
||||
scope=self.scope if scope is None else scope
|
||||
(self.root/'scope.json').write_text(json.dumps(scope))
|
||||
pin='sha256:'+hashlib.sha256(enc(scope)).hexdigest()
|
||||
reviews={role:{'approved_admission_sha256':pin,'approval_ref':'synthetic-test-only'} for role in ('founder','railiance-platform-owner','access-engine-owner','secrets-engine-owner','cluster-gitops-owner')}
|
||||
(self.root/'reviews.json').write_text(json.dumps(reviews))
|
||||
return subprocess.run([sys.executable,str(BASE/'finalize.py'),str(self.root/'scope.json'),str(self.root/'reviews.json'),str(target or self.root/'out.json')],capture_output=True,preexec_fn=lambda:os.umask(0))
|
||||
def test_exact_bytes_and_private_mode_under_permissive_umask(self):
|
||||
result=self.run_assembler();self.assertEqual(result.returncode,0,result.stderr)
|
||||
path=self.root/'out.json';self.assertEqual(stat.S_IMODE(path.stat().st_mode),0o600)
|
||||
value=json.loads(path.read_bytes());self.assertEqual({k:v for k,v in value.items() if k!='owner_reviews'},self.scope)
|
||||
receipt=json.loads(result.stdout);self.assertEqual(receipt['admission_file_sha256'],'sha256:'+hashlib.sha256(path.read_bytes()).hexdigest())
|
||||
self.assertIn('No approval authenticated',receipt['authority'])
|
||||
def test_existing_output_refused_unchanged(self):
|
||||
target=self.root/'out.json';target.write_bytes(b'original');os.chmod(target,0o640)
|
||||
self.assertNotEqual(self.run_assembler().returncode,0)
|
||||
self.assertEqual(target.read_bytes(),b'original');self.assertEqual(stat.S_IMODE(target.stat().st_mode),0o640)
|
||||
def test_symlink_output_refused_without_target_change(self):
|
||||
target=self.root/'original';target.write_bytes(b'original');(self.root/'out.json').symlink_to(target)
|
||||
self.assertNotEqual(self.run_assembler().returncode,0);self.assertEqual(target.read_bytes(),b'original')
|
||||
def test_altered_fixed_bindings_refused_before_output(self):
|
||||
for field in ('proposal_sha256','executed_revision_sha256','image_sha256','policy_sha256'):
|
||||
with self.subTest(field=field):
|
||||
scope=copy.deepcopy(self.scope);scope['bindings'][field]='sha256:'+'f'*64
|
||||
self.assertNotEqual(self.run_assembler(scope).returncode,0)
|
||||
self.assertFalse((self.root/'out.json').exists())
|
||||
def test_altered_fixed_image_refused_even_matching_reviews(self):
|
||||
scope=copy.deepcopy(self.scope);scope['producer_image']='forgejo.coulomb.social/coulomb/flex-auth@sha256:'+'f'*64
|
||||
self.assertNotEqual(self.run_assembler(scope).returncode,0);self.assertFalse((self.root/'out.json').exists())
|
||||
if __name__=='__main__': unittest.main()
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"schema": "railiance-platform.owner-source-review.v1",
|
||||
"reviewer_role": "railiance-platform-owner",
|
||||
"agent": "railiance-platform (codex)",
|
||||
"proposal_id": "RPF35-PDP-SIGNER-NATIVE-ADAPTER-20261016-R2",
|
||||
"reviewed_revision_sha256": "c5b8f0d10650f13eb5939874bda1fb457b3dc5835e63e043af9824e43edb9f80",
|
||||
"reviewed_delta_sha256": "81f4c2867d09accc33a22f7537526c0bcf86c4e45803639af0c602b6446c9ab1",
|
||||
"disposition": "approve-source-only-pending-independent-review",
|
||||
"findings_addressed": [
|
||||
"Coordinator R1 metadata-only real process-loss counterexample",
|
||||
"SECRETS-PUBLIC-TRUST-R1-F01 stale current-looking primitive lineage",
|
||||
"SECRETS-PUBLIC-TRUST-R1-F02 phase-aware read-only reconciliation"
|
||||
],
|
||||
"assessment": "Recovery explicitly distinguishes exact current absence, exact metadata-only version0/data absence, and verified seed-cas version1 correspondence. Unknown originals remain immutable; no public key is admitted for absent/metadata-only/unstored-key results. Ambiguous or denied reads remain unconfirmed; every subsequent write/generation needs a fresh separate exact action. Current plan primitive/source/receipt join is enforced with prior pin explicitly historical. Fresh recovery reviews bind original and current execution lineages while preserving image/deployment/policy/trust authority.",
|
||||
"timing": "120s absolute gate unchanged; reviews cover exact expiry, Warden OIDC precedes child without a proven bound. Pre-review exact future window and start early; otherwise refuse/new explicit revision. No post-login refresh, reused approval, TTL widening or start-only reinterpretation.",
|
||||
"limits": [
|
||||
"Source review authenticates no native owner disposition.",
|
||||
"Exact native image/caller/trust/approval/CCR/routing inputs remain unadmitted.",
|
||||
"Phase outcomes describe current reads only; historical effect remains unresolved.",
|
||||
"Metadata-only outcome does not enable current bootstrap against existing metadata; separate reviewed action required.",
|
||||
"Historic-lineage unit model is synthetic; no production source-migration acceptance inferred.",
|
||||
"Identity/fence/review facts in disposable tests are synthetic; same-UID isolation/atomic cluster fence not inferred.",
|
||||
"Original routing mismatch evidence remains preserved even after separate source matcher repair."
|
||||
],
|
||||
"authority_granted": false,
|
||||
"incremental_source_spend_eur": 0
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
{
|
||||
"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:8ed281a49c1f1cdb23772efe3593e75c62572a12b8d7a6a4c574f27b38722e75"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Protected signer CAS primitive. Disposable owner fixture only; no live entry.
|
||||
|
||||
This module deliberately cannot admit production custody. A reviewed native CCR,
|
||||
protected executor, fencing and founder scope are prerequisites for that adapter.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass,field
|
||||
from datetime import datetime,timezone
|
||||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
import http.client
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import secrets
|
||||
import subprocess
|
||||
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
||||
from task_execution_journal import Journal,Refused
|
||||
from task_execution_native_fixture import _absolute_budget
|
||||
from task_execution_recovery import _json_unique,_invalid_constant
|
||||
|
||||
PATH='platform/data/workloads/flex-auth/envelope-signing'
|
||||
FIELDS={'ED25519_PRIVATE_KEY','ED25519_PUBLIC_KEY','kid'}
|
||||
BINDINGS={'proposal_sha256','executed_revision_sha256','deployment_sha256','image_sha256','policy_sha256','trust_sha256'}
|
||||
|
||||
|
||||
def encoded(value):return json.dumps(value,sort_keys=True,separators=(',',':'),allow_nan=False).encode()
|
||||
def b64(value):return base64.urlsafe_b64encode(value).decode().rstrip('=')
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FixtureWriterAdmission:
|
||||
port:int
|
||||
owner_token:str=field(repr=False)
|
||||
server_process:subprocess.Popen=field(repr=False)
|
||||
fenced:bool=False
|
||||
|
||||
|
||||
class SignerWriter:
|
||||
"""Single attempted operation; failed/unknown writes never retry or remint.
|
||||
|
||||
File permissions are accidental-disclosure guards, not same-UID isolation.
|
||||
The receipt directory must be independent owner custody in a native adapter.
|
||||
Python cannot promise seed zeroization; process lifetime is the boundary.
|
||||
"""
|
||||
def __init__(self,admission:FixtureWriterAdmission,directory:Path,operation_id:str,*,kid:str,expected_version:int,bindings:dict):
|
||||
if (type(admission) is not FixtureWriterAdmission or type(admission.port) is not int
|
||||
or not 1<=admission.port<=65535 or type(admission.owner_token) is not str
|
||||
or not admission.owner_token or type(admission.fenced) is not bool or not admission.fenced):
|
||||
raise Refused('exact fenced fixture custody required')
|
||||
process=admission.server_process
|
||||
if (type(process) is not subprocess.Popen or process.poll() is not None or type(process.args) is not list
|
||||
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')
|
||||
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
|
||||
or type(bindings) is not dict or set(bindings)!=BINDINGS
|
||||
or any(type(v) is not str or not re.fullmatch(r'sha256:[0-9a-f]{64}',v) for v in bindings.values())):
|
||||
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.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(),
|
||||
'actor':'synthetic-owner-fixture','created_at':datetime.now(timezone.utc).isoformat(),'path':PATH,'expected_version':expected_version,'kid':kid,
|
||||
'status':'prepared','production_admission':False,'rollback':'forward-recovery-only',
|
||||
'public_key':None,'public_fingerprint':None,'kv_version':None,'exact_readback':False}
|
||||
self._save(initial=True)
|
||||
|
||||
def _save(self,*,initial=False):
|
||||
raw=encoded(self.receipt)
|
||||
if len(raw)>65536:raise Refused('receipt bound exceeded')
|
||||
if initial:
|
||||
target=self.path
|
||||
else:
|
||||
fd=os.open(self.path,os.O_RDONLY|os.O_NOFOLLOW|os.O_NONBLOCK)
|
||||
try:Journal._private(os.fstat(fd))
|
||||
finally:os.close(fd)
|
||||
target=self.directory/('.pending-'+secrets.token_hex(16))
|
||||
fd=os.open(target,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
|
||||
try:
|
||||
with os.fdopen(fd,'wb',closefd=False) as stream:stream.write(raw);stream.flush();os.fsync(fd)
|
||||
finally:os.close(fd)
|
||||
if not initial:os.replace(target,self.path)
|
||||
fd=os.open(self.directory,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW)
|
||||
try:os.fsync(fd)
|
||||
finally:os.close(fd)
|
||||
|
||||
def _http(self,method,path,body=None):
|
||||
# Fixed loopback owner process and fixed data path; never arbitrary URL.
|
||||
if self.admission.server_process.poll() is not None:raise Refused('fixture custody backend unavailable')
|
||||
with _absolute_budget(2.0):
|
||||
conn=http.client.HTTPConnection('127.0.0.1',self.admission.port,timeout=2)
|
||||
try:
|
||||
conn.request(method,'/v1/'+path,body=None if body is None else encoded(body),headers={
|
||||
'X-Vault-Token':self.admission.owner_token,'Content-Type':'application/json'})
|
||||
response=conn.getresponse();raw=response.read(1048577)
|
||||
if len(raw)>1048576:raise Refused('backend response bound exceeded')
|
||||
value=json.loads(raw,object_pairs_hook=_json_unique,parse_constant=_invalid_constant) if raw else {}
|
||||
return response.status,value
|
||||
finally:conn.close()
|
||||
|
||||
def write(self):
|
||||
if self.attempted:raise Refused('signer operation already attempted')
|
||||
self.attempted=True
|
||||
# All source/receipt checks precede generation; no seed import, argv,
|
||||
# environment, temporary file or public output interface exists.
|
||||
private=Ed25519PrivateKey.generate()
|
||||
seed=b64(private.private_bytes(serialization.Encoding.Raw,serialization.PrivateFormat.Raw,serialization.NoEncryption()))
|
||||
public=b64(private.public_key().public_bytes(serialization.Encoding.Raw,serialization.PublicFormat.Raw))
|
||||
document={'ED25519_PRIVATE_KEY':seed,'ED25519_PUBLIC_KEY':public,'kid':self.kid}
|
||||
self.receipt.update(status='write-intent-unknown',public_key=public,
|
||||
public_fingerprint='sha256:'+hashlib.sha256(public.encode()).hexdigest())
|
||||
self._save() # A durable intent must exist before any possible effect.
|
||||
try:
|
||||
status,result=self._http('POST',PATH,{'options':{'cas':self.expected_version},'data':document})
|
||||
if status!=200 or type(result) is not dict:raise Refused('signer write unconfirmed')
|
||||
version=result.get('data',{}).get('version')
|
||||
if type(version) is not int or version!=self.expected_version+1:raise Refused('signer write version unconfirmed')
|
||||
self.receipt['kv_version']=version
|
||||
status,result=self._http('GET',PATH+'?version='+str(version))
|
||||
if status!=200 or type(result) is not dict:raise Refused('signer readback unconfirmed')
|
||||
data=result.get('data',{});stored=data.get('data') if type(data) is dict else None
|
||||
metadata=data.get('metadata') if type(data) is dict else None
|
||||
if (type(stored) is not dict or set(stored)!=FIELDS or any(type(v) is not str for v in stored.values())
|
||||
or type(metadata) is not dict or type(metadata.get('version')) is not int or metadata['version']!=version
|
||||
or any(not hmac.compare_digest(stored[name],document[name]) for name in FIELDS)):
|
||||
raise Refused('signer readback mismatch')
|
||||
self.receipt.update(status='custody-verified-pending-independent-public-admission',exact_readback=True)
|
||||
self._save()
|
||||
return dict(self.receipt)
|
||||
except BaseException:
|
||||
# Persist only closed metadata; unknown never implies absence or retry.
|
||||
# If receipt storage fails, the durable earlier intent remains unknown.
|
||||
self.receipt.update(status='write-intent-unknown',exact_readback=False)
|
||||
self._save()
|
||||
raise Refused('signer custody outcome unconfirmed') from None
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
raise SystemExit('production signer custody unadmitted; no live entry point')
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,119 @@
|
|||
# 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.
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
{
|
||||
"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:8ed281a49c1f1cdb23772efe3593e75c62572a12b8d7a6a4c574f27b38722e75"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue