Admit reviewed receiver evidence batch 5
This commit is contained in:
parent
62d6e67e42
commit
4af966c2fa
24 changed files with 1165 additions and 0 deletions
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"schema": "state95.guard-successor-readonly-preflight.v1",
|
||||
"observed_at": "2026-10-17T00:33:19.687847+00:00",
|
||||
"target": "railiance01/92.205.62.239",
|
||||
"node_identity_verified": true,
|
||||
"kubeconfig": "/home/worsch/.kube/config-railiance01",
|
||||
"helm_revision": 76,
|
||||
"current_image_manifest": "sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
|
||||
"health": {
|
||||
"status": "ok",
|
||||
"db": "connected",
|
||||
"instance_role": "primary",
|
||||
"instance_label": "railiance01",
|
||||
"schema": {
|
||||
"status": "ok",
|
||||
"applied": "c4d5e6f7a8b9",
|
||||
"expected": "c4d5e6f7a8b9"
|
||||
}
|
||||
},
|
||||
"headroom": {
|
||||
"schema": "state-hub.release-headroom-preflight.v1",
|
||||
"ok": true,
|
||||
"observed_at": "2026-10-17T00:33:19.561580+00:00",
|
||||
"freshness_seconds": 0,
|
||||
"remaining_cpu_m": 420,
|
||||
"remaining_memory_bytes": 6535979008,
|
||||
"pending_unrelated_cpu_m": 0,
|
||||
"api_surge_cpu_m": 100,
|
||||
"mcp_surge_cpu_m": 10,
|
||||
"migrate_cpu_m": 50,
|
||||
"atomic": true,
|
||||
"reasons": [],
|
||||
"notes": [
|
||||
"Aggregate remaining millicores is not a kube-scheduler guarantee.",
|
||||
"Preflight does not lower requests and does not start Helm."
|
||||
],
|
||||
"hook_order": [
|
||||
"pre-upgrade migrate job (helm.sh/hook-weight -5)",
|
||||
"API RollingUpdate maxSurge=1 maxUnavailable=0",
|
||||
"MCP RollingUpdate maxSurge=1 maxUnavailable=0"
|
||||
]
|
||||
},
|
||||
"migration_job_and_pods_absent": true,
|
||||
"runtime_readback_ref": "../receiver-rollout-r4/final-runtime-check.json",
|
||||
"live_changes": false
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"workplan_id": "79579572-8120-5d3b-aca5-dd4aff60d058",
|
||||
"id": "df703266-6719-4c04-ae1b-ba9afd3bca1c",
|
||||
"topic_id": null,
|
||||
"title": "Review exact frozen-binding API guard successor and retained baseline exception",
|
||||
"description": "Pending proposal STATE-WP-0095-T10-frozen-bindings-guard-r1 revision1 SHA25681d365926fc585a186955d8c98ee83501fd3cec12135b2256d71b46a75097932. Image main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1; frozen chart b5c0663d830fc8d3b65080f237a0be8d01f8b2f7fec9dc33aeba91b1b6b281d4. Eight-identity image-only server dry-run and explicit target/headroom/schema pass. Verified installed checker maintenance preserves all18 CURRENT rows; prior three-parent hash failures remain historical exceptions, never restored or backfilled. Requested ADMINISTER @ realm:kubernetes/railiance01 activation=APPROVED includes exact new Job conditional recovery, rollback76 after verified quiescence, current-baseline successor acceptance, unpriced cost/risk and attended stop for unknown mapping. Full review: the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r1/FINAL-PROPOSAL.md. No new runtime is approved or executed.",
|
||||
"decision_type": "pending",
|
||||
"status": "open",
|
||||
"rationale": "Agent-prepared exact new-code proposal. Only the founder may resolve approval; prior receiver revision4 approval does not cover this new image.",
|
||||
"decided_by": null,
|
||||
"decided_at": null,
|
||||
"deadline": null,
|
||||
"escalation_note": null,
|
||||
"superseded_by": null,
|
||||
"created_at": "2026-10-17T00:37:26.205185Z",
|
||||
"updated_at": "2026-10-17T00:37:26.205185Z",
|
||||
"workstream_id": "79579572-8120-5d3b-aca5-dd4aff60d058"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"status": "prepared-helper-contract-checked",
|
||||
"actual_workplan_binding_schema_accepts_exact_three": true,
|
||||
"commit_interception_checked_with_actual_unbound_AsyncSession": true,
|
||||
"DB_connected": false,
|
||||
"native_probes_executed": false
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
{
|
||||
"schema": "custodian.supervised-runtime-proposal.v1",
|
||||
"proposal_id": "STATE-WP-0095-T10-frozen-bindings-guard-r1",
|
||||
"revision": 1,
|
||||
"status": "pending_supervisor_approval_not_executed",
|
||||
"prepared_at": "2026-10-17T00:37:11.095872+00:00",
|
||||
"owner_task_id": "STATE-WP-0095-T10",
|
||||
"owner_task_uuid": "88585c69-d9b9-5e1f-a256-6416ff71e824",
|
||||
"target": "railiance01/92.205.62.239 state-hub release API/MCP and exact-new-trial migration Job",
|
||||
"authority_requested": "ADMINISTER @ realm:kubernetes/railiance01 activation=APPROVED",
|
||||
"source_commit": "c103cbf3c8bed5dba87a03e320595ddded9301a3",
|
||||
"image": "forgejo.coulomb.social/coulomb/state-hub:main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1",
|
||||
"image_digest": "sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1",
|
||||
"chart_archive": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/cluster-historical-publication-r1/revisions/r4-rollout-review/state-hub-0.1.0.tgz",
|
||||
"chart_sha256": "b5c0663d830fc8d3b65080f237a0be8d01f8b2f7fec9dc33aeba91b1b6b281d4",
|
||||
"remote_owned_chart": "/tmp/statehub-binding-guard-b5c0663d830f/state-hub-0.1.0.tgz",
|
||||
"expected_prior_helm_revision": 76,
|
||||
"expected_schema": "c4d5e6f7a8b9",
|
||||
"migration_files_changed_since_deployed_source": false,
|
||||
"exact_upgrade_argv": [
|
||||
"ssh",
|
||||
"railiance01",
|
||||
"KUBECONFIG=/etc/rancher/k3s/k3s.yaml helm -n state-hub upgrade state-hub /tmp/statehub-binding-guard-b5c0663d830f/state-hub-0.1.0.tgz --reuse-values --set-string image.tag=main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1 --atomic --timeout 3m"
|
||||
],
|
||||
"staging": "Create exact new private owned directory, refuse unexpected existing ownership/content, copy only frozen chart, verify remote SHA before execution and remove only owned exact archive/directory after proof.",
|
||||
"readiness_refresh_required_before_execution": true,
|
||||
"native_probe_cases": 8,
|
||||
"verification": [
|
||||
"Delivered manifest and guard module hashes, API/MCP Ready, primary/railiance01 and expected schema.",
|
||||
"Fresh CURRENT full historical-row hashes before and after; enforce rollback and commit interception for eight exact native cases, then fresh-transaction comparison including ordinary parent.",
|
||||
"Verified installed local CLI guard, ordinary consistency/fast projection between current snapshots, archive bytes and unrelated staged work preserved; source/remote/Hub IDs/statuses read back."
|
||||
],
|
||||
"acceptance_disposition_requested": "Use current hashed baseline for successor acceptance; retain the earlier three-parent mismatch as a historical exception, without guessed restoration or backfilling its failed result. T07/RMGR19T06 close only after new receiving and corrected pipeline pass with exception recorded.",
|
||||
"conditional_recovery_included": true,
|
||||
"rollback_helm_revision": 76,
|
||||
"recovery_limits": "Exact observed new Job UID/image/ownership and bound Pods only; foreground UID-precondition cancellation after revalidation. Verify private owner DB connection/transaction/lock quiescence; unknown mapping/mixed state stops for attended recovery. No schema downgrade or broad backend termination.",
|
||||
"cost_estimate_eur": null,
|
||||
"risk_estimate_eur": null,
|
||||
"cost_and_risk_disposition": "Unpriced attributable cost and credible loss/outage exposure on existing infrastructure require supervised founder acceptance. Helm timeout is not Job/outage bound.",
|
||||
"autopilot_grant": false,
|
||||
"bound_receipts": [
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r1/FINAL-PROPOSAL.md",
|
||||
"sha256": "7addca35af1973be4a2f2c1aeec658a00b581108da03e6509e645045f2767ae4"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r1/RECOVERY.md",
|
||||
"sha256": "dc72dea7cdf519a712ee8f6d28fc6aa5667b44655d948f2c0160869383f33cef"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r1/native_binding_probes.py",
|
||||
"sha256": "ef5c07affb15d20b4194edda68fd14b4d0fee1e9dab857e3242c3a1a1e49046f"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r1/probe-helper-preparation-check.json",
|
||||
"sha256": "e11f48b18cdb57e1b8f2bddcf7e6ffc7473ab2ab2b031c3c1a26afae609f831c"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r1/watch_rollout.py",
|
||||
"sha256": "a496da3c9a415ac800921ce755446be4bb55bc17e4e6b913143a5d6baf48f815"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r1/reuse_values_dryrun.py",
|
||||
"sha256": "5e653ae8439c773b27a167464b620805055e9d820a907761ab30652e65fae200"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r1/exact-server-dryrun.json",
|
||||
"sha256": "2bfb02130876aeb71a773585dae8b446ad5c671050ebc14d0fc7bcce86dffea5"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r1/fresh-readonly-preflight.json",
|
||||
"sha256": "01bbdc94ebd6fb266ab21bdc0851eac6eddeafbabcd7105cce23806931ebfbce"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-image-c103cbf-inspection.json",
|
||||
"sha256": "21b4ddd3fa65743bed5eae568e0c08f2d150fcecacb7949263cef06bab99a683"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/before-verified-client.json",
|
||||
"sha256": "ee41be2d74085603abf2bfe2c91c1bb430b4ea0539a6cbf988528b18f3f5ef46"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/after-verified-client.json",
|
||||
"sha256": "8cd8921271cd647fe40dde0f00bc55b714313426e14091397d8fd11314c5e610"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/verified-loaded-client.json",
|
||||
"sha256": "0745b5df92d2533714e2a87a43c73b3b685e7bab7bcf4444f7a5fbd82dd4722f"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/verified-client-cluster-projection.json",
|
||||
"sha256": "c25b43575273df0e648842d447979aca1b41ade16fcdaf9f5f870024e7ca06e9"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/verified-client-cluster-consistency.json",
|
||||
"sha256": "fd8a7d58d9e08b4866420ad77d01fbc7c28c0c2c9610ffbe95ddc77dddd9b1a6"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/ordinary-pipeline-preservation-finding.json",
|
||||
"sha256": "43d4adc0ffbf61345865eebb3f8948f82a3c33a28fd0436eccd08bc93ba973c1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Read-only exact owner Helm reuse-values render/diff; no raw output retained."""
|
||||
import copy
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import yaml
|
||||
OWNER=Path('/home/worsch/state-hub')
|
||||
KUBE='/home/worsch/.kube/config-railiance01'
|
||||
CHART='/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/cluster-historical-publication-r1/revisions/r4-rollout-review/state-hub-0.1.0.tgz'
|
||||
CHART_SHA256='b5c0663d830fc8d3b65080f237a0be8d01f8b2f7fec9dc33aeba91b1b6b281d4'
|
||||
TAG='main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1'
|
||||
HELM=['helm','--kubeconfig',KUBE,'-n','state-hub']
|
||||
DRYRUN=HELM+['upgrade','state-hub',CHART,'--reuse-values','--set-string','image.tag='+TAG,
|
||||
'--dry-run=server','--hide-secret','--no-hooks','--timeout','3m']
|
||||
PUBLIC_COORDINATES={'OPENBAO_K8S_TOKEN_PATH':'/var/run/secrets/openbao/token',
|
||||
'FORGE_READ_SECRET_PATH':'platform/data/workloads/state-hub/forge-derivation',
|
||||
'FORGE_READ_SECRET_KEY':'FORGE_READ_TOKEN'}
|
||||
ALLOWED={'ConfigMap','Deployment','Job','Service','ServiceAccount','Namespace','Ingress'}
|
||||
LIMIT=2*1024*1024
|
||||
class Refusal(RuntimeError):pass
|
||||
|
||||
def digest(obj):return 'sha256:'+hashlib.sha256(json.dumps(obj,sort_keys=True,separators=(',',':')).encode()).hexdigest()
|
||||
|
||||
def parse(raw):
|
||||
if len(raw)>LIMIT:raise Refusal('render_size_refused')
|
||||
result=[]
|
||||
for obj in yaml.safe_load_all(raw):
|
||||
if not obj:continue
|
||||
if not isinstance(obj,dict) or obj.get('kind') not in ALLOWED:raise Refusal('unexpected_or_secret_resource_refused')
|
||||
result.append(obj)
|
||||
return result
|
||||
|
||||
def sections(raw):
|
||||
text=raw.decode()
|
||||
if '\nMANIFEST:\n' not in text:raise Refusal('helm_render_sections_unrecognized')
|
||||
before,manifest=text.split('\nMANIFEST:\n',1)
|
||||
manifest=manifest.split('\nNOTES:',1)[0]
|
||||
hooks=before.split('\nHOOKS:\n',1)[1] if '\nHOOKS:\n' in before else ''
|
||||
return parse(hooks)+parse(manifest)
|
||||
|
||||
def identity(obj):
|
||||
meta=obj.get('metadata') or {}
|
||||
return (obj['apiVersion'],obj['kind'],meta.get('namespace') or ('' if obj['kind']=='Namespace' else 'state-hub'),meta['name'])
|
||||
|
||||
def diff_paths(a,b,prefix=''):
|
||||
if type(a)!=type(b):return [prefix]
|
||||
if isinstance(a,dict):
|
||||
paths=[]
|
||||
for key in sorted(set(a)|set(b)):
|
||||
path=prefix+'/'+key.replace('~','~0').replace('/','~1')
|
||||
paths.extend([path] if key not in a or key not in b else diff_paths(a[key],b[key],path))
|
||||
return paths
|
||||
if isinstance(a,list):
|
||||
if len(a)!=len(b):return [prefix]
|
||||
return [p for i,(x,y) in enumerate(zip(a,b)) for p in diff_paths(x,y,prefix+'/'+str(i))]
|
||||
return [] if a==b else [prefix]
|
||||
|
||||
def compare(old,new):
|
||||
baseline={identity(o):o for o in old};intended={identity(o):o for o in new}
|
||||
if len(baseline)!=len(old) or len(intended)!=len(new):raise Refusal('duplicate_object_identity')
|
||||
rows=[]
|
||||
for key in sorted(set(baseline)|set(intended)):
|
||||
a=baseline.get(key);b=intended.get(key);paths=diff_paths(a,b)
|
||||
images=[]
|
||||
if b and b['kind'] in {'Deployment','Job'}:
|
||||
spec=b.get('spec',{}).get('template',{}).get('spec',{})
|
||||
for container in (spec.get('containers',[])+spec.get('initContainers',[])):
|
||||
image=container.get('image','')
|
||||
if not re.fullmatch(r'forgejo\.coulomb\.social/coulomb/state-hub:main-[0-9a-f]{7}(?:@sha256:[0-9a-f]{64})?',image):raise Refusal('unexpected_image_refused')
|
||||
images.append(image)
|
||||
for env in container.get('env',[]):
|
||||
if 'value' in env and re.search(r'PASSWORD|TOKEN|SECRET|DATABASE_URL',env.get('name',''),re.I):
|
||||
if env.get('name') not in PUBLIC_COORDINATES or env['value']!=PUBLIC_COORDINATES[env['name']]:raise Refusal('literal_sensitive_env_refused')
|
||||
rows.append({'api_version':key[0],'kind':key[1],'namespace':key[2],'name':key[3],
|
||||
'intended_images':images,'intended_spec_sha256':digest(b.get('spec',{})) if b else None,
|
||||
'baseline_object_sha256':digest(a) if a else None,'intended_object_sha256':digest(b) if b else None,
|
||||
'changed_paths':paths})
|
||||
image_path=re.compile(r'^/spec/template/spec/(?:initContainers|containers)/[0-9]+/image$')
|
||||
only_images=all(image_path.fullmatch(p) for row in rows for p in row['changed_paths'])
|
||||
return {'objects':rows,'changed_object_count':sum(bool(r['changed_paths']) for r in rows),
|
||||
'intended_diff_only_images':only_images,'object_identity_set_unchanged':set(baseline)==set(intended)}
|
||||
|
||||
def command(args):
|
||||
env=dict(os.environ);env['KUBECONFIG']=KUBE
|
||||
p=subprocess.run(args,cwd=OWNER,env=env,capture_output=True,timeout=45)
|
||||
if len(p.stdout)>LIMIT:raise Refusal('command_output_size_refused')
|
||||
if p.returncode:
|
||||
lower=p.stderr.decode(errors='replace').lower()
|
||||
label=next((label for marker,label in [('reject-secret-last-applied','secret_annotation_policy_refused'),('forbidden','admission_or_auth_refused'),('cannot patch','patch_validation_refused'),('is invalid','resource_validation_refused'),('timeout','bounded_timeout'),('connection refused','api_unavailable')] if marker in lower),'unknown_refusal')
|
||||
raise Refusal(label)
|
||||
return p.stdout
|
||||
|
||||
def main():
|
||||
result={'schema':'statehub.exact-reuse-values-readonly-diff.v1','status':'failed',
|
||||
'dryrun_argv':DRYRUN,'production_mutations':False,'hooks_executed':False,
|
||||
'raw_helm_values_release_content_logs_retained':False,
|
||||
'limitations':['Dry-run admission/render only, not an actual image pull/migration/deployment.',
|
||||
'Helm timeout does not stop a later live migration Job; exact scoped cancellation/quiescence remains required.']}
|
||||
try:
|
||||
archive=Path(CHART)
|
||||
if archive.is_symlink() or not archive.is_file() or hashlib.sha256(archive.read_bytes()).hexdigest()!=CHART_SHA256:raise Refusal('reviewed_chart_archive_drift')
|
||||
result['chart_archive_sha256']='sha256:'+CHART_SHA256
|
||||
nodes=json.loads(command(['kubectl','--kubeconfig',KUBE,'get','nodes','-o','json']))
|
||||
if not any(a.get('address')=='92.205.62.239' for n in nodes.get('items',[]) for a in n.get('status',{}).get('addresses',[])):raise Refusal('target_identity_refused')
|
||||
result['stage']='baseline_release_manifest'
|
||||
old=parse(command(HELM+['get','manifest','state-hub']))
|
||||
result['stage']='baseline_release_hooks'
|
||||
old+=parse(command(HELM+['get','hooks','state-hub']))
|
||||
result['stage']='exact_upgrade_server_dryrun'
|
||||
new=sections(command(DRYRUN))
|
||||
result.update(compare(old,new));result['status']='passed_images_only' if result['intended_diff_only_images'] and result['object_identity_set_unchanged'] else 'review_nonimage_delta_required'
|
||||
except (Refusal,subprocess.TimeoutExpired,ValueError,yaml.YAMLError) as error:
|
||||
result['error_class']=type(error).__name__
|
||||
result['error_label']=str(error) if isinstance(error,Refusal) else 'bounded_parse_or_command_failure'
|
||||
print(json.dumps(result,indent=2))
|
||||
return int(result['status']!='passed_images_only')
|
||||
if __name__=='__main__':raise SystemExit(main())
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
"""Observe the approved release Job and owned Pods; never mutate resources."""
|
||||
from pathlib import Path
|
||||
from datetime import datetime, timezone
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
BASE = Path(__file__).resolve().parent
|
||||
KUBE = ['kubectl', '--kubeconfig', '/home/worsch/.kube/config-railiance01', '-n', 'state-hub']
|
||||
IMAGE = 'forgejo.coulomb.social/coulomb/state-hub:main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1'
|
||||
def get(args):
|
||||
result = subprocess.run(KUBE + args, capture_output=True, timeout=10)
|
||||
if result.returncode:
|
||||
return None
|
||||
return json.loads(result.stdout) if result.stdout.strip() else {}
|
||||
|
||||
result = {'schema': 'state95.approved-job-observation.v1', 'mutation': False,
|
||||
'started_at': datetime.now(timezone.utc).isoformat(), 'jobs': {}, 'poll_failures': 0}
|
||||
deadline = time.monotonic() + 240
|
||||
while time.monotonic() < deadline:
|
||||
try:
|
||||
job = get(['get', 'job', 'state-hub-migrate', '--ignore-not-found', '-o', 'json'])
|
||||
if job is None:
|
||||
result['poll_failures'] += 1
|
||||
elif job:
|
||||
meta = job['metadata']
|
||||
images = [c['image'] for c in job['spec']['template']['spec']['containers']]
|
||||
if (images != [IMAGE] or meta.get('labels', {}).get('app.kubernetes.io/instance') != 'state-hub'
|
||||
or meta.get('labels', {}).get('app.kubernetes.io/managed-by') != 'Helm'
|
||||
or meta.get('annotations', {}).get('helm.sh/hook') != 'pre-install,pre-upgrade'):
|
||||
result['unexpected_job_identity'] = True
|
||||
break
|
||||
entry = result['jobs'].setdefault(meta['uid'], {'uid': meta['uid'], 'name': meta['name'],
|
||||
'created_at': meta['creationTimestamp'], 'image': IMAGE, 'managed_by': 'Helm',
|
||||
'release': 'state-hub', 'hook': 'pre-install,pre-upgrade', 'pods': {}})
|
||||
entry['last_status'] = {k: job.get('status', {}).get(k) for k in ['active', 'succeeded', 'failed']}
|
||||
pods = get(['get', 'pods', '-l', 'job-name=state-hub-migrate', '-o', 'json'])
|
||||
if pods:
|
||||
for pod in pods['items']:
|
||||
owners = pod['metadata'].get('ownerReferences', [])
|
||||
if not any(o['uid'] == meta['uid'] and o['kind'] == 'Job' for o in owners):
|
||||
continue
|
||||
entry['pods'][pod['metadata']['uid']] = {'name': pod['metadata']['name'],
|
||||
'uid': pod['metadata']['uid'], 'owner_job_uid': meta['uid'],
|
||||
'pod_ip': pod.get('status', {}).get('podIP'), 'phase': pod.get('status', {}).get('phase')}
|
||||
elif result['jobs']:
|
||||
result['job_absent_after_observation'] = True
|
||||
(BASE / 'job-observation.json').write_text(json.dumps(result, indent=2) + '\n')
|
||||
if (BASE / 'helm-return.json').exists():
|
||||
result['helm_return_observed'] = True
|
||||
break
|
||||
except (subprocess.TimeoutExpired, ValueError, KeyError):
|
||||
result['poll_failures'] += 1
|
||||
time.sleep(0.7)
|
||||
result['ended_at'] = datetime.now(timezone.utc).isoformat()
|
||||
(BASE / 'job-observation.json').write_text(json.dumps(result, indent=2) + '\n')
|
||||
print(json.dumps({'observed_trial_jobs': len(result['jobs']), 'mutation': False}))
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# Deploy the frozen-binding API guard
|
||||
|
||||
Proposal **STATE-WP-0095-T10-frozen-bindings-guard-r1**, revision 2. Pending founder approval for `ADMINISTER @ realm:kubernetes/railiance01`, `activation=APPROVED`.
|
||||
|
||||
The approved receiver rollout is healthy at Helm 76. Its later ordinary maintenance path exposed a separate defect: the legacy binding endpoint refreshed three frozen parent rows. All fifteen task rows and the archive bytes stayed unchanged. The original failed full-row comparison is retained; the original parent preimages were not saved and will not be guessed.
|
||||
|
||||
The source fixes are published in State Hub c103cbf3c8bed5dba87a03e320595ddded9301a3. They protect the exact three bound parents and fifteen task UUIDs in the checker, repair engine and API binding endpoint. Ordinary inbound dependencies remain allowed. The installed checker is now hash-verified against that source. Its actual maintenance rerun omitted all three frozen bindings and preserved all eighteen **current** full rows. The unrelated staged brief accidentally included by the old writeback helper was restored through a forward commit; its original index/worktree state is preserved. Five real Git tests verify the corrected file-scoped commit behavior. Independent guard review passed 109 isolated cases; canonical guard tests passed 102. These are separate from new runtime acceptance.
|
||||
|
||||
Promote this verified CI artifact through the owner Helm path:
|
||||
|
||||
`forgejo.coulomb.social/coulomb/state-hub:main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1`
|
||||
|
||||
Use the already frozen chart archive with SHA256 b5c0663d830fc8d3b65080f237a0be8d01f8b2f7fec9dc33aeba91b1b6b281d4, preserving release values. Its fresh server dry-run passes: eight identities, three image changes. Public read-only image inspection verifies the selected guard/receiver/migration files and CI build ID; package metadata and hub-core source hashes match the deployed image. Fresh target/schema/readiness/headroom checks pass. All are refreshed before actual execution. Floating build origins and complete application/concurrent-writer acceptance remain outside these byte checks.
|
||||
|
||||
The exact upgrade is bound in proposal-metadata.json: stage the hash-verified archive in the specified owned remote directory, then use `--reuse-values --set-string image.tag=<the exact reference above> --atomic --timeout 3m`. Observe the exact new Job and owned Pods. Schema is already c4d5e6f7a8b9, and no migration file changed, but the existing migration hook and its recovery limitations still apply.
|
||||
|
||||
Verify delivered digest and module hashes, API/MCP readiness, schema and full-row preservation from a fresh **current** baseline. Run the eight prepared native cases only inside rollback transactions with commit interception enforced, then compare a fresh transaction. The prepared probe helper covers all three parents, ordinary-first invalid batches and expected-task/ownership/status refusals; its actual schema and commit-interception contracts are checked without a database connection. New probes are not yet executed. Rerun the verified-client ordinary maintenance path between current hash snapshots, preserve archive bytes and unrelated staged files, and read back source/remote/Hub bindings.
|
||||
|
||||
Approval includes using the current hashed rows for successor acceptance while retaining the prior parent-row mismatch as an explicit historical exception. It does not turn that failed comparison into a pass or restore its unavailable preimages. T07 and RMGR19T06 close only after the new receiving and corrected whole-pipeline checks pass with that exception recorded. All sixteen NetKingdom native target gates remain separate.
|
||||
|
||||
Approval must also cover [the exact conditional recovery](RECOVERY.md): only the newly observed owned Job may be cancelled with a UID precondition; verify database connection/lock quiescence before rollback to Helm **76**. Unknown mapping or mixed state stops for attended owner recovery. Helm timeout is not an outage or Job-lifetime bound; rollback retains schema.
|
||||
|
||||
Attributable EUR cost and credible loss exposure remain unpriced on the existing cluster. This needs scoped supervised approval and creates no autopilot, credential or monetary grant.
|
||||
|
||||
Revision 2 changes only the probe helper’s representation of verified source hashes to explicit module/SHA256 records, resolving the normal scanner’s hash-metadata false positive. Runtime image, commands, case logic, acceptance and recovery scope are unchanged. Original revision 1 and its digest remain recorded; it was never approved or executed.
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Guard successor: conditional recovery
|
||||
|
||||
Proposal STATE-WP-0095-T10-frozen-bindings-guard-r1, revision 1.
|
||||
|
||||
The prior deployed release is Helm 76, with image manifest c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015 and schema c4d5e6f7a8b9. Refresh this baseline before execution. The successor's migration files are unchanged, but its existing pre-upgrade migration hook still runs. Its three-minute Helm wait does not stop the Job or bound outage, and the Job has no active deadline.
|
||||
|
||||
Observe the new Job UID, creation timestamp, exact successor image, Helm release/hook ownership and owned Pod UIDs/IPs using watch_rollout.py. If Helm fails or times out with this trial Job active, revalidate all identities immediately. Approval includes deleting only this exact new Job with a UID precondition and foreground propagation, and observing its bound Pods terminate. A reused name, foreign ownership, unexpected image, unobserved UID or ambiguous mapping stops automatic recovery. No name-only or blanket deletion, finalizer removal or broad database cancellation is authorized.
|
||||
|
||||
Through the existing private database-owner execution path, bind migration connections to the captured Pod/IP and start window, and verify those exact connections, transactions and locks are gone. Retain only connection-identity/state/lock-count metadata. Query text, parameters, credentials and DSNs remain private. If pooling/NAT, missing observation or access makes this mapping unknown, stop for attended database-owner recovery. Pod disappearance alone is insufficient.
|
||||
|
||||
After verified quiescence, inspect actual schema and release state. If application rollback is needed, the approved prior target is **76**, retaining frozen Forge receiving support. Use the normal owner SSH path, KUBECONFIG=/etc/rancher/k3s/k3s.yaml, and:
|
||||
|
||||
```
|
||||
helm -n state-hub rollback state-hub 76 --no-hooks --wait --timeout 3m
|
||||
```
|
||||
|
||||
Verify image, readiness, schema, full-row hashes and remaining Job/Pod state. The database retains its additive schema; do not downgrade or invent old parent values. The verified installed client guard remains in use. Removing receiving support or reverting the client guard is outside this recovery. Unknown/mixed state stops for attended handling; a failed or rescued attempt remains a failed/refined outcome.
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
{
|
||||
"schema": "statehub.exact-reuse-values-readonly-diff.v1",
|
||||
"status": "passed_images_only",
|
||||
"dryrun_argv": [
|
||||
"helm",
|
||||
"--kubeconfig",
|
||||
"/home/worsch/.kube/config-railiance01",
|
||||
"-n",
|
||||
"state-hub",
|
||||
"upgrade",
|
||||
"state-hub",
|
||||
"/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/cluster-historical-publication-r1/revisions/r4-rollout-review/state-hub-0.1.0.tgz",
|
||||
"--reuse-values",
|
||||
"--set-string",
|
||||
"image.tag=main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1",
|
||||
"--dry-run=server",
|
||||
"--hide-secret",
|
||||
"--no-hooks",
|
||||
"--timeout",
|
||||
"3m"
|
||||
],
|
||||
"production_mutations": false,
|
||||
"hooks_executed": false,
|
||||
"raw_helm_values_release_content_logs_retained": false,
|
||||
"limitations": [
|
||||
"Dry-run admission/render only, not an actual image pull/migration/deployment.",
|
||||
"Helm timeout does not stop a later live migration Job; exact scoped cancellation/quiescence remains required."
|
||||
],
|
||||
"chart_archive_sha256": "sha256:b5c0663d830fc8d3b65080f237a0be8d01f8b2f7fec9dc33aeba91b1b6b281d4",
|
||||
"stage": "exact_upgrade_server_dryrun",
|
||||
"objects": [
|
||||
{
|
||||
"api_version": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"namespace": "state-hub",
|
||||
"name": "state-hub",
|
||||
"intended_images": [
|
||||
"forgejo.coulomb.social/coulomb/state-hub:main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1"
|
||||
],
|
||||
"intended_spec_sha256": "sha256:df32d106c28b270c67aa683c8a4f20b26c430b121420c4023d34d23ede0eaed9",
|
||||
"baseline_object_sha256": "sha256:01e7278447f3204ec8abfd9a7b5b6be61850995b3e792fc1f97fbc3207536d69",
|
||||
"intended_object_sha256": "sha256:7b7c78921b071732c8e7c4c062bdbd26787498581102ea9916b07c1f57887a73",
|
||||
"changed_paths": [
|
||||
"/spec/template/spec/containers/0/image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"api_version": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"namespace": "state-hub",
|
||||
"name": "state-hub-mcp",
|
||||
"intended_images": [
|
||||
"forgejo.coulomb.social/coulomb/state-hub:main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1"
|
||||
],
|
||||
"intended_spec_sha256": "sha256:50ae88c8c57919bf745a772a8ff34ea7e469751a403ac39d1d01ab2e9d3a27db",
|
||||
"baseline_object_sha256": "sha256:9a4376e004060a4ec2c76784fd50ec6ce05f61b3793f6458625d88122a7b8713",
|
||||
"intended_object_sha256": "sha256:b78c66a3e27bca60ff4dd4f51c1bbe789a4e246be3d1e7e6fb9659ffdce80127",
|
||||
"changed_paths": [
|
||||
"/spec/template/spec/containers/0/image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"api_version": "batch/v1",
|
||||
"kind": "Job",
|
||||
"namespace": "state-hub",
|
||||
"name": "state-hub-migrate",
|
||||
"intended_images": [
|
||||
"forgejo.coulomb.social/coulomb/state-hub:main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1"
|
||||
],
|
||||
"intended_spec_sha256": "sha256:2eb273b5e2ca5538fa1b13a8cf8cd10d259ad7cd3510bce1e86075f8c4c4a915",
|
||||
"baseline_object_sha256": "sha256:6909dca9acd5e1b816491b91b6a32ef6d934e288e2d8d0d3d66fac9fe67468ec",
|
||||
"intended_object_sha256": "sha256:208b52d9b958b9d4490182bda4d0da27b4a6f0961f6bf182c34ef4b640fcbb5e",
|
||||
"changed_paths": [
|
||||
"/spec/template/spec/containers/0/image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"api_version": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"namespace": "state-hub",
|
||||
"name": "state-hub-classification-allowed",
|
||||
"intended_images": [],
|
||||
"intended_spec_sha256": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
|
||||
"baseline_object_sha256": "sha256:924d16a0a6b4ba8a46b59f0990dcc2adc6dd813cf5d7d83bdb389844068d7cfd",
|
||||
"intended_object_sha256": "sha256:924d16a0a6b4ba8a46b59f0990dcc2adc6dd813cf5d7d83bdb389844068d7cfd",
|
||||
"changed_paths": []
|
||||
},
|
||||
{
|
||||
"api_version": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"namespace": "state-hub",
|
||||
"name": "state-hub-config",
|
||||
"intended_images": [],
|
||||
"intended_spec_sha256": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
|
||||
"baseline_object_sha256": "sha256:26707b20933b4f6de78f9059c0efec6df7f0e90c8f1a67db6d2f1ac72e674560",
|
||||
"intended_object_sha256": "sha256:26707b20933b4f6de78f9059c0efec6df7f0e90c8f1a67db6d2f1ac72e674560",
|
||||
"changed_paths": []
|
||||
},
|
||||
{
|
||||
"api_version": "v1",
|
||||
"kind": "Service",
|
||||
"namespace": "state-hub",
|
||||
"name": "state-hub",
|
||||
"intended_images": [],
|
||||
"intended_spec_sha256": "sha256:da5895e6aee1f619d16c07dafd4342cf7ec8f9de0944a84979c624a4e9eea22a",
|
||||
"baseline_object_sha256": "sha256:62098bbc3ef6265e072f416ecb11de85d59fce7e2e0d22b2616bd233e720dafe",
|
||||
"intended_object_sha256": "sha256:62098bbc3ef6265e072f416ecb11de85d59fce7e2e0d22b2616bd233e720dafe",
|
||||
"changed_paths": []
|
||||
},
|
||||
{
|
||||
"api_version": "v1",
|
||||
"kind": "Service",
|
||||
"namespace": "state-hub",
|
||||
"name": "state-hub-mcp",
|
||||
"intended_images": [],
|
||||
"intended_spec_sha256": "sha256:4e04175b03bcd75833109d6843e282cee79d73eae2bc85e36097264b832ba26f",
|
||||
"baseline_object_sha256": "sha256:ab096d239c9cd72e6177a260d9b4d548a7d5b7c0a285c3a11b7267193e336bba",
|
||||
"intended_object_sha256": "sha256:ab096d239c9cd72e6177a260d9b4d548a7d5b7c0a285c3a11b7267193e336bba",
|
||||
"changed_paths": []
|
||||
},
|
||||
{
|
||||
"api_version": "v1",
|
||||
"kind": "ServiceAccount",
|
||||
"namespace": "state-hub",
|
||||
"name": "state-hub",
|
||||
"intended_images": [],
|
||||
"intended_spec_sha256": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
|
||||
"baseline_object_sha256": "sha256:750594b51a534d603f50640d02a3c4eec79cc52fe07d5f442102d37b7299adb8",
|
||||
"intended_object_sha256": "sha256:750594b51a534d603f50640d02a3c4eec79cc52fe07d5f442102d37b7299adb8",
|
||||
"changed_paths": []
|
||||
}
|
||||
],
|
||||
"changed_object_count": 3,
|
||||
"intended_diff_only_images": true,
|
||||
"object_identity_set_unchanged": true
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"schema": "state95.guard-successor-readonly-preflight.v1",
|
||||
"observed_at": "2026-10-17T00:33:19.687847+00:00",
|
||||
"target": "railiance01/92.205.62.239",
|
||||
"node_identity_verified": true,
|
||||
"kubeconfig": "/home/worsch/.kube/config-railiance01",
|
||||
"helm_revision": 76,
|
||||
"current_image_manifest": "sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
|
||||
"health": {
|
||||
"status": "ok",
|
||||
"db": "connected",
|
||||
"instance_role": "primary",
|
||||
"instance_label": "railiance01",
|
||||
"schema": {
|
||||
"status": "ok",
|
||||
"applied": "c4d5e6f7a8b9",
|
||||
"expected": "c4d5e6f7a8b9"
|
||||
}
|
||||
},
|
||||
"headroom": {
|
||||
"schema": "state-hub.release-headroom-preflight.v1",
|
||||
"ok": true,
|
||||
"observed_at": "2026-10-17T00:33:19.561580+00:00",
|
||||
"freshness_seconds": 0,
|
||||
"remaining_cpu_m": 420,
|
||||
"remaining_memory_bytes": 6535979008,
|
||||
"pending_unrelated_cpu_m": 0,
|
||||
"api_surge_cpu_m": 100,
|
||||
"mcp_surge_cpu_m": 10,
|
||||
"migrate_cpu_m": 50,
|
||||
"atomic": true,
|
||||
"reasons": [],
|
||||
"notes": [
|
||||
"Aggregate remaining millicores is not a kube-scheduler guarantee.",
|
||||
"Preflight does not lower requests and does not start Helm."
|
||||
],
|
||||
"hook_order": [
|
||||
"pre-upgrade migrate job (helm.sh/hook-weight -5)",
|
||||
"API RollingUpdate maxSurge=1 maxUnavailable=0",
|
||||
"MCP RollingUpdate maxSurge=1 maxUnavailable=0"
|
||||
]
|
||||
},
|
||||
"migration_job_and_pods_absent": true,
|
||||
"runtime_readback_ref": "../receiver-rollout-r4/final-runtime-check.json",
|
||||
"live_changes": false
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
"""Prepared owner in-pod probes. Requires separate approval; commits are intercepted."""
|
||||
import asyncio,hashlib,json,logging,uuid
|
||||
from pathlib import Path
|
||||
logging.disable(logging.CRITICAL)
|
||||
EXPECTED = [
|
||||
{
|
||||
"module": "api.services.historical_projection",
|
||||
"sha256": "e24e57c58f4aaa4ec9d9bf088009ac69006e53f75b9477ee53dc26c4e850b975"
|
||||
},
|
||||
{
|
||||
"module": "api.routers.workstreams",
|
||||
"sha256": "9c9bf4376dd323b52109912bb3f615949e89ac02c8b88b4720f569f8565ad1d6"
|
||||
}
|
||||
]
|
||||
ORDINARY='e0a7813d-c916-5e49-84b2-5e5b30441891'
|
||||
class UnexpectedCommit(Exception):pass
|
||||
class NoCommit:
|
||||
def __init__(self,session):self.session=session;self.attempts=0
|
||||
async def execute(self,*args,**kwargs):return await self.session.execute(*args,**kwargs)
|
||||
async def commit(self):self.attempts+=1;raise UnexpectedCommit()
|
||||
def digest(v):return 'sha256:'+hashlib.sha256(json.dumps(v,sort_keys=True,separators=(',',':'),ensure_ascii=False).encode()).hexdigest()
|
||||
async def main():
|
||||
import importlib
|
||||
from sqlalchemy import text
|
||||
from fastapi import HTTPException
|
||||
for entry in EXPECTED:
|
||||
name,pin=entry["module"],entry["sha256"]
|
||||
module=importlib.import_module(name);assert hashlib.sha256(Path(module.__file__).read_bytes()).hexdigest()==pin
|
||||
from api.database import async_session_factory,engine
|
||||
from api.services.historical_projection import FROZEN_CLUSTER_BINDINGS
|
||||
from api.routers.workstreams import sync_workplan_bindings
|
||||
from api.schemas.workplan import WorkplanBindingsSync
|
||||
engine.echo=False
|
||||
entries=FROZEN_CLUSTER_BINDINGS;assert len(entries)==3
|
||||
parents=[v['uuid'] for v in entries];task_ids=[t['uuid'] for v in entries for t in v['tasks']];assert len(task_ids)==15
|
||||
def body(ordinary=False):
|
||||
values=[dict(workplan_id=v['uuid'],repo_slug=v['repo'],filename=Path(v['path']).name,relative_path=v['path'],archived=True,status='finished') for v in entries]
|
||||
if ordinary:values.insert(0,dict(workplan_id=ORDINARY,repo_slug='railiance-cluster',filename='RCLUSTER-WP-0018-keycape-original-tls-recovery.md',relative_path='workplans/RCLUSTER-WP-0018-keycape-original-tls-recovery.md',archived=False,status='active'))
|
||||
return values
|
||||
async def snapshot(session):
|
||||
result={}
|
||||
for table,ids in [('workplans',parents+[ORDINARY]),('tasks',task_ids)]:
|
||||
rows=(await session.execute(text('SELECT to_jsonb(row) FROM '+table+' AS row WHERE row.id=ANY(:ids) ORDER BY row.id'),{'ids':[uuid.UUID(v) for v in ids]})).scalars().all()
|
||||
assert len(rows)==len(ids) and {v['id'] for v in rows}==set(ids)
|
||||
result[table]=digest(rows)
|
||||
return result
|
||||
cases={};before=None
|
||||
async with async_session_factory() as session:
|
||||
try:
|
||||
await session.execute(text("SET LOCAL statement_timeout='5s'"));await session.execute(text("SET LOCAL lock_timeout='1s'"));await session.execute(text("SET LOCAL idle_in_transaction_session_timeout='10s'"))
|
||||
before=await snapshot(session)
|
||||
for name in ['preserve-three','invalid-path-mixed','foreign-repo-mixed','missing-task-mixed','foreign-holder-mixed','nonterminal-parent-mixed','nonterminal-task-mixed','invalid-last-binding-mixed']:
|
||||
nested=await session.begin_nested();proxy=NoCommit(session);outcome='unverified'
|
||||
try:
|
||||
values=body(ordinary=name!='preserve-three')
|
||||
if name=='invalid-path-mixed':values[1]['relative_path']='workplans/incorrect.md'
|
||||
if name=='foreign-repo-mixed':values[1]['repo_slug']='foreign'
|
||||
if name=='missing-task-mixed':await session.execute(text('DELETE FROM tasks WHERE id=:id'),{'id':uuid.UUID(task_ids[0])})
|
||||
if name=='foreign-holder-mixed':await session.execute(text('UPDATE tasks SET workplan_id=:parent WHERE id=:id'),{'id':uuid.UUID(task_ids[0]),'parent':uuid.UUID(ORDINARY)})
|
||||
if name=='nonterminal-parent-mixed':await session.execute(text("UPDATE workplans SET status='active' WHERE id=:id"),{'id':uuid.UUID(parents[0])})
|
||||
if name=='nonterminal-task-mixed':await session.execute(text("UPDATE tasks SET status='progress' WHERE id=:id"),{'id':uuid.UUID(task_ids[0])})
|
||||
if name=='invalid-last-binding-mixed':values[-1]['archived']=False
|
||||
try:
|
||||
answer=await sync_workplan_bindings(WorkplanBindingsSync(bindings=values),proxy)
|
||||
assert name=='preserve-three' and answer=={'updated':0,'received':3,'preserved':3}
|
||||
outcome='preserved'
|
||||
except HTTPException as exc:
|
||||
assert name!='preserve-three' and exc.status_code==409;outcome='refused'
|
||||
assert proxy.attempts==0
|
||||
finally:
|
||||
await nested.rollback();session.expire_all()
|
||||
restored=await snapshot(session);assert restored==before
|
||||
cases[name]={'status':'passed','outcome':outcome,'commit_attempts':proxy.attempts,'rolled_back':True,'full_rows_restored':True}
|
||||
finally:await session.rollback()
|
||||
async with async_session_factory() as fresh:
|
||||
try:after=await snapshot(fresh);assert before==after
|
||||
finally:await fresh.rollback()
|
||||
await engine.dispose()
|
||||
print(json.dumps({'schema':'state95.native-guard-probes.v1','status':'passed','cases':cases,'before_full_row_hashes':before,'after_full_row_hashes':after,'historical_parents':3,'historical_tasks':15,'ordinary_parent_also_preserved':True,'fresh_transaction_full_rows_equal':True,'commits_performed':0,'commit_interception_enforced':True,'raw_rows_or_credentials_retained':False,'limits':['No claim of restoring pre-legacy-refresh parent preimages.','Missing actual parent/repo rows and full concurrent-writer behavior remain source-test coverage, not claimed as these native cases.']},sort_keys=True))
|
||||
if __name__=='__main__':
|
||||
try:asyncio.run(main())
|
||||
except BaseException:print(json.dumps({'schema':'state95.native-guard-probes.v1','status':'refused-or-unverified','raw_rows_or_credentials_retained':False}));raise SystemExit(1)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"workplan_id": "79579572-8120-5d3b-aca5-dd4aff60d058",
|
||||
"id": "df703266-6719-4c04-ae1b-ba9afd3bca1c",
|
||||
"topic_id": null,
|
||||
"title": "Review exact frozen-binding API guard successor and retained baseline exception",
|
||||
"description": "Pending proposal STATE-WP-0095-T10-frozen-bindings-guard-r1 revision2 SHA2565d0ddebc21bcb035809cc8b57f4c2e1ba8ed09204be5b965460e178a90913f1e. Image main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1; frozen chart b5c0663d830fc8d3b65080f237a0be8d01f8b2f7fec9dc33aeba91b1b6b281d4. Eight-identity image-only server dry-run and explicit target/headroom/schema pass. Verified installed checker maintenance preserves all18 CURRENT rows; prior three-parent hash failures remain historical exceptions, never restored or backfilled. Requested ADMINISTER @ realm:kubernetes/railiance01 activation=APPROVED includes exact new Job conditional recovery, rollback76 after verified quiescence, current-baseline successor acceptance, unpriced cost/risk and attended stop for unknown mapping. Full review: the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r2/FINAL-PROPOSAL.md. No new runtime is approved or executed. Original revision1 remains retained and unexecuted. Revision2 only represents source hashes as typed metadata after the normal scanner false positive; runtime, case logic, acceptance and recovery scope are unchanged.",
|
||||
"decision_type": "pending",
|
||||
"status": "open",
|
||||
"rationale": "Agent-prepared exact new-code proposal. Only the founder may resolve approval; prior receiver revision4 approval does not cover this new image.",
|
||||
"decided_by": null,
|
||||
"decided_at": null,
|
||||
"deadline": null,
|
||||
"escalation_note": null,
|
||||
"superseded_by": null,
|
||||
"created_at": "2026-10-17T00:37:26.205185Z",
|
||||
"updated_at": "2026-10-17T00:48:51.550636Z",
|
||||
"workstream_id": "79579572-8120-5d3b-aca5-dd4aff60d058"
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"status": "prepared-helper-contract-checked",
|
||||
"actual_workplan_binding_schema_accepts_exact_three": true,
|
||||
"commit_interception_checked_with_actual_unbound_AsyncSession": true,
|
||||
"DB_connected": false,
|
||||
"native_probes_executed": false,
|
||||
"revision": 2,
|
||||
"expected_module_hashes_equal_revision1": true,
|
||||
"probe_logic_after_hash_loading_byte_identical": true,
|
||||
"syntax_checked": true
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
{
|
||||
"schema": "custodian.supervised-runtime-proposal.v1",
|
||||
"proposal_id": "STATE-WP-0095-T10-frozen-bindings-guard-r1",
|
||||
"revision": 2,
|
||||
"status": "pending_supervisor_approval_not_executed",
|
||||
"prepared_at": "2026-10-17T00:48:23.064918+00:00",
|
||||
"owner_task_id": "STATE-WP-0095-T10",
|
||||
"owner_task_uuid": "88585c69-d9b9-5e1f-a256-6416ff71e824",
|
||||
"target": "railiance01/92.205.62.239 state-hub release API/MCP and exact-new-trial migration Job",
|
||||
"authority_requested": "ADMINISTER @ realm:kubernetes/railiance01 activation=APPROVED",
|
||||
"source_commit": "c103cbf3c8bed5dba87a03e320595ddded9301a3",
|
||||
"image": "forgejo.coulomb.social/coulomb/state-hub:main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1",
|
||||
"image_digest": "sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1",
|
||||
"chart_archive": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/cluster-historical-publication-r1/revisions/r4-rollout-review/state-hub-0.1.0.tgz",
|
||||
"chart_sha256": "b5c0663d830fc8d3b65080f237a0be8d01f8b2f7fec9dc33aeba91b1b6b281d4",
|
||||
"remote_owned_chart": "/tmp/statehub-binding-guard-b5c0663d830f/state-hub-0.1.0.tgz",
|
||||
"expected_prior_helm_revision": 76,
|
||||
"expected_schema": "c4d5e6f7a8b9",
|
||||
"migration_files_changed_since_deployed_source": false,
|
||||
"exact_upgrade_argv": [
|
||||
"ssh",
|
||||
"railiance01",
|
||||
"KUBECONFIG=/etc/rancher/k3s/k3s.yaml helm -n state-hub upgrade state-hub /tmp/statehub-binding-guard-b5c0663d830f/state-hub-0.1.0.tgz --reuse-values --set-string image.tag=main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1 --atomic --timeout 3m"
|
||||
],
|
||||
"staging": "Create exact new private owned directory, refuse unexpected existing ownership/content, copy only frozen chart, verify remote SHA before execution and remove only owned exact archive/directory after proof.",
|
||||
"readiness_refresh_required_before_execution": true,
|
||||
"native_probe_cases": 8,
|
||||
"verification": [
|
||||
"Delivered manifest and guard module hashes, API/MCP Ready, primary/railiance01 and expected schema.",
|
||||
"Fresh CURRENT full historical-row hashes before and after; enforce rollback and commit interception for eight exact native cases, then fresh-transaction comparison including ordinary parent.",
|
||||
"Verified installed local CLI guard, ordinary consistency/fast projection between current snapshots, archive bytes and unrelated staged work preserved; source/remote/Hub IDs/statuses read back."
|
||||
],
|
||||
"acceptance_disposition_requested": "Use current hashed baseline for successor acceptance; retain the earlier three-parent mismatch as a historical exception, without guessed restoration or backfilling its failed result. T07/RMGR19T06 close only after new receiving and corrected pipeline pass with exception recorded.",
|
||||
"conditional_recovery_included": true,
|
||||
"rollback_helm_revision": 76,
|
||||
"recovery_limits": "Exact observed new Job UID/image/ownership and bound Pods only; foreground UID-precondition cancellation after revalidation. Verify private owner DB connection/transaction/lock quiescence; unknown mapping/mixed state stops for attended recovery. No schema downgrade or broad backend termination.",
|
||||
"cost_estimate_eur": null,
|
||||
"risk_estimate_eur": null,
|
||||
"cost_and_risk_disposition": "Unpriced attributable cost and credible loss/outage exposure on existing infrastructure require supervised founder acceptance. Helm timeout is not Job/outage bound.",
|
||||
"autopilot_grant": false,
|
||||
"bound_receipts": [
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r2/FINAL-PROPOSAL.md",
|
||||
"sha256": "5ff105f0eb66e85f6d5c64a496e5f1ce7b816a32fb93351f4cd07133b9a053fd"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r2/RECOVERY.md",
|
||||
"sha256": "dc72dea7cdf519a712ee8f6d28fc6aa5667b44655d948f2c0160869383f33cef"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r2/native_binding_probes.py",
|
||||
"sha256": "ce492d0713c987e47eefc991a6312c1c63ed72056a597e9e28aee959d06e71d5"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r2/probe-helper-preparation-check.json",
|
||||
"sha256": "36feaa02e17850c56685ccaf78556d12447fdeddae9c483721cd996da848f400"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r2/watch_rollout.py",
|
||||
"sha256": "a496da3c9a415ac800921ce755446be4bb55bc17e4e6b913143a5d6baf48f815"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r2/reuse_values_dryrun.py",
|
||||
"sha256": "5e653ae8439c773b27a167464b620805055e9d820a907761ab30652e65fae200"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r2/exact-server-dryrun.json",
|
||||
"sha256": "2bfb02130876aeb71a773585dae8b446ad5c671050ebc14d0fc7bcce86dffea5"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-successor-review-r2/fresh-readonly-preflight.json",
|
||||
"sha256": "01bbdc94ebd6fb266ab21bdc0851eac6eddeafbabcd7105cce23806931ebfbce"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/guard-image-c103cbf-inspection.json",
|
||||
"sha256": "21b4ddd3fa65743bed5eae568e0c08f2d150fcecacb7949263cef06bab99a683"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/before-verified-client.json",
|
||||
"sha256": "ee41be2d74085603abf2bfe2c91c1bb430b4ea0539a6cbf988528b18f3f5ef46"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/after-verified-client.json",
|
||||
"sha256": "8cd8921271cd647fe40dde0f00bc55b714313426e14091397d8fd11314c5e610"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/verified-loaded-client.json",
|
||||
"sha256": "0745b5df92d2533714e2a87a43c73b3b685e7bab7bcf4444f7a5fbd82dd4722f"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/verified-client-cluster-projection.json",
|
||||
"sha256": "c25b43575273df0e648842d447979aca1b41ade16fcdaf9f5f870024e7ca06e9"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/verified-client-cluster-consistency.json",
|
||||
"sha256": "fd8a7d58d9e08b4866420ad77d01fbc7c28c0c2c9610ffbe95ddc77dddd9b1a6"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/ordinary-pipeline-preservation-finding.json",
|
||||
"sha256": "43d4adc0ffbf61345865eebb3f8948f82a3c33a28fd0436eccd08bc93ba973c1"
|
||||
}
|
||||
],
|
||||
"supersedes": {
|
||||
"revision": 1,
|
||||
"sha256": "81d365926fc585a186955d8c98ee83501fd3cec12135b2256d71b46a75097932",
|
||||
"reason": "Explicit module/SHA256 metadata representation in probe helper; no runtime/acceptance/recovery change."
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Read-only exact owner Helm reuse-values render/diff; no raw output retained."""
|
||||
import copy
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import yaml
|
||||
OWNER=Path('/home/worsch/state-hub')
|
||||
KUBE='/home/worsch/.kube/config-railiance01'
|
||||
CHART='/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/cluster-historical-publication-r1/revisions/r4-rollout-review/state-hub-0.1.0.tgz'
|
||||
CHART_SHA256='b5c0663d830fc8d3b65080f237a0be8d01f8b2f7fec9dc33aeba91b1b6b281d4'
|
||||
TAG='main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1'
|
||||
HELM=['helm','--kubeconfig',KUBE,'-n','state-hub']
|
||||
DRYRUN=HELM+['upgrade','state-hub',CHART,'--reuse-values','--set-string','image.tag='+TAG,
|
||||
'--dry-run=server','--hide-secret','--no-hooks','--timeout','3m']
|
||||
PUBLIC_COORDINATES={'OPENBAO_K8S_TOKEN_PATH':'/var/run/secrets/openbao/token',
|
||||
'FORGE_READ_SECRET_PATH':'platform/data/workloads/state-hub/forge-derivation',
|
||||
'FORGE_READ_SECRET_KEY':'FORGE_READ_TOKEN'}
|
||||
ALLOWED={'ConfigMap','Deployment','Job','Service','ServiceAccount','Namespace','Ingress'}
|
||||
LIMIT=2*1024*1024
|
||||
class Refusal(RuntimeError):pass
|
||||
|
||||
def digest(obj):return 'sha256:'+hashlib.sha256(json.dumps(obj,sort_keys=True,separators=(',',':')).encode()).hexdigest()
|
||||
|
||||
def parse(raw):
|
||||
if len(raw)>LIMIT:raise Refusal('render_size_refused')
|
||||
result=[]
|
||||
for obj in yaml.safe_load_all(raw):
|
||||
if not obj:continue
|
||||
if not isinstance(obj,dict) or obj.get('kind') not in ALLOWED:raise Refusal('unexpected_or_secret_resource_refused')
|
||||
result.append(obj)
|
||||
return result
|
||||
|
||||
def sections(raw):
|
||||
text=raw.decode()
|
||||
if '\nMANIFEST:\n' not in text:raise Refusal('helm_render_sections_unrecognized')
|
||||
before,manifest=text.split('\nMANIFEST:\n',1)
|
||||
manifest=manifest.split('\nNOTES:',1)[0]
|
||||
hooks=before.split('\nHOOKS:\n',1)[1] if '\nHOOKS:\n' in before else ''
|
||||
return parse(hooks)+parse(manifest)
|
||||
|
||||
def identity(obj):
|
||||
meta=obj.get('metadata') or {}
|
||||
return (obj['apiVersion'],obj['kind'],meta.get('namespace') or ('' if obj['kind']=='Namespace' else 'state-hub'),meta['name'])
|
||||
|
||||
def diff_paths(a,b,prefix=''):
|
||||
if type(a)!=type(b):return [prefix]
|
||||
if isinstance(a,dict):
|
||||
paths=[]
|
||||
for key in sorted(set(a)|set(b)):
|
||||
path=prefix+'/'+key.replace('~','~0').replace('/','~1')
|
||||
paths.extend([path] if key not in a or key not in b else diff_paths(a[key],b[key],path))
|
||||
return paths
|
||||
if isinstance(a,list):
|
||||
if len(a)!=len(b):return [prefix]
|
||||
return [p for i,(x,y) in enumerate(zip(a,b)) for p in diff_paths(x,y,prefix+'/'+str(i))]
|
||||
return [] if a==b else [prefix]
|
||||
|
||||
def compare(old,new):
|
||||
baseline={identity(o):o for o in old};intended={identity(o):o for o in new}
|
||||
if len(baseline)!=len(old) or len(intended)!=len(new):raise Refusal('duplicate_object_identity')
|
||||
rows=[]
|
||||
for key in sorted(set(baseline)|set(intended)):
|
||||
a=baseline.get(key);b=intended.get(key);paths=diff_paths(a,b)
|
||||
images=[]
|
||||
if b and b['kind'] in {'Deployment','Job'}:
|
||||
spec=b.get('spec',{}).get('template',{}).get('spec',{})
|
||||
for container in (spec.get('containers',[])+spec.get('initContainers',[])):
|
||||
image=container.get('image','')
|
||||
if not re.fullmatch(r'forgejo\.coulomb\.social/coulomb/state-hub:main-[0-9a-f]{7}(?:@sha256:[0-9a-f]{64})?',image):raise Refusal('unexpected_image_refused')
|
||||
images.append(image)
|
||||
for env in container.get('env',[]):
|
||||
if 'value' in env and re.search(r'PASSWORD|TOKEN|SECRET|DATABASE_URL',env.get('name',''),re.I):
|
||||
if env.get('name') not in PUBLIC_COORDINATES or env['value']!=PUBLIC_COORDINATES[env['name']]:raise Refusal('literal_sensitive_env_refused')
|
||||
rows.append({'api_version':key[0],'kind':key[1],'namespace':key[2],'name':key[3],
|
||||
'intended_images':images,'intended_spec_sha256':digest(b.get('spec',{})) if b else None,
|
||||
'baseline_object_sha256':digest(a) if a else None,'intended_object_sha256':digest(b) if b else None,
|
||||
'changed_paths':paths})
|
||||
image_path=re.compile(r'^/spec/template/spec/(?:initContainers|containers)/[0-9]+/image$')
|
||||
only_images=all(image_path.fullmatch(p) for row in rows for p in row['changed_paths'])
|
||||
return {'objects':rows,'changed_object_count':sum(bool(r['changed_paths']) for r in rows),
|
||||
'intended_diff_only_images':only_images,'object_identity_set_unchanged':set(baseline)==set(intended)}
|
||||
|
||||
def command(args):
|
||||
env=dict(os.environ);env['KUBECONFIG']=KUBE
|
||||
p=subprocess.run(args,cwd=OWNER,env=env,capture_output=True,timeout=45)
|
||||
if len(p.stdout)>LIMIT:raise Refusal('command_output_size_refused')
|
||||
if p.returncode:
|
||||
lower=p.stderr.decode(errors='replace').lower()
|
||||
label=next((label for marker,label in [('reject-secret-last-applied','secret_annotation_policy_refused'),('forbidden','admission_or_auth_refused'),('cannot patch','patch_validation_refused'),('is invalid','resource_validation_refused'),('timeout','bounded_timeout'),('connection refused','api_unavailable')] if marker in lower),'unknown_refusal')
|
||||
raise Refusal(label)
|
||||
return p.stdout
|
||||
|
||||
def main():
|
||||
result={'schema':'statehub.exact-reuse-values-readonly-diff.v1','status':'failed',
|
||||
'dryrun_argv':DRYRUN,'production_mutations':False,'hooks_executed':False,
|
||||
'raw_helm_values_release_content_logs_retained':False,
|
||||
'limitations':['Dry-run admission/render only, not an actual image pull/migration/deployment.',
|
||||
'Helm timeout does not stop a later live migration Job; exact scoped cancellation/quiescence remains required.']}
|
||||
try:
|
||||
archive=Path(CHART)
|
||||
if archive.is_symlink() or not archive.is_file() or hashlib.sha256(archive.read_bytes()).hexdigest()!=CHART_SHA256:raise Refusal('reviewed_chart_archive_drift')
|
||||
result['chart_archive_sha256']='sha256:'+CHART_SHA256
|
||||
nodes=json.loads(command(['kubectl','--kubeconfig',KUBE,'get','nodes','-o','json']))
|
||||
if not any(a.get('address')=='92.205.62.239' for n in nodes.get('items',[]) for a in n.get('status',{}).get('addresses',[])):raise Refusal('target_identity_refused')
|
||||
result['stage']='baseline_release_manifest'
|
||||
old=parse(command(HELM+['get','manifest','state-hub']))
|
||||
result['stage']='baseline_release_hooks'
|
||||
old+=parse(command(HELM+['get','hooks','state-hub']))
|
||||
result['stage']='exact_upgrade_server_dryrun'
|
||||
new=sections(command(DRYRUN))
|
||||
result.update(compare(old,new));result['status']='passed_images_only' if result['intended_diff_only_images'] and result['object_identity_set_unchanged'] else 'review_nonimage_delta_required'
|
||||
except (Refusal,subprocess.TimeoutExpired,ValueError,yaml.YAMLError) as error:
|
||||
result['error_class']=type(error).__name__
|
||||
result['error_label']=str(error) if isinstance(error,Refusal) else 'bounded_parse_or_command_failure'
|
||||
print(json.dumps(result,indent=2))
|
||||
return int(result['status']!='passed_images_only')
|
||||
if __name__=='__main__':raise SystemExit(main())
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"status": "original_draft_bytes_reconstructed_in_memory",
|
||||
"original_helper_sha256": "ef5c07affb15d20b4194edda68fd14b4d0fee1e9dab857e3242c3a1a1e49046f",
|
||||
"native_probes_executed": false,
|
||||
"source_or_private_values_printed": false
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
"""Reconstruct the unexecuted draft's bytes in memory from published revision 2.
|
||||
|
||||
This audits the retained original proposal digest without executing a probe or
|
||||
printing source. Only the source-hash metadata representation was refined.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import ast
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
base = Path(__file__).resolve().parent
|
||||
source = (base / 'native_binding_probes.py').read_text()
|
||||
assignment = next(n for n in ast.parse(source).body if isinstance(n, ast.Assign)
|
||||
and any(isinstance(t, ast.Name) and t.id == 'EXPECTED' for t in n.targets))
|
||||
records = ast.literal_eval(assignment.value)
|
||||
lines = source.splitlines()
|
||||
lines[assignment.lineno - 1:assignment.end_lineno] = [
|
||||
'EXPECTED={' + ','.join(repr(r['module']) + ':' + repr(r['sha256']) for r in records) + '}']
|
||||
original = ('\n'.join(lines) + '\n').replace(
|
||||
' for entry in EXPECTED:\n name,pin=entry["module"],entry["sha256"]',
|
||||
' for name,pin in EXPECTED.items():')
|
||||
digest = hashlib.sha256(original.encode()).hexdigest()
|
||||
assert digest == 'ef5c07affb15d20b4194edda68fd14b4d0fee1e9dab857e3242c3a1a1e49046f'
|
||||
result = {'status': 'original_draft_bytes_reconstructed_in_memory',
|
||||
'original_helper_sha256': digest, 'native_probes_executed': False,
|
||||
'source_or_private_values_printed': False}
|
||||
(base / 'revision1-preimage-reconstruction.json').write_text(json.dumps(result, indent=2) + '\n')
|
||||
print(json.dumps(result))
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
"""Observe the approved release Job and owned Pods; never mutate resources."""
|
||||
from pathlib import Path
|
||||
from datetime import datetime, timezone
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
BASE = Path(__file__).resolve().parent
|
||||
KUBE = ['kubectl', '--kubeconfig', '/home/worsch/.kube/config-railiance01', '-n', 'state-hub']
|
||||
IMAGE = 'forgejo.coulomb.social/coulomb/state-hub:main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1'
|
||||
def get(args):
|
||||
result = subprocess.run(KUBE + args, capture_output=True, timeout=10)
|
||||
if result.returncode:
|
||||
return None
|
||||
return json.loads(result.stdout) if result.stdout.strip() else {}
|
||||
|
||||
result = {'schema': 'state95.approved-job-observation.v1', 'mutation': False,
|
||||
'started_at': datetime.now(timezone.utc).isoformat(), 'jobs': {}, 'poll_failures': 0}
|
||||
deadline = time.monotonic() + 240
|
||||
while time.monotonic() < deadline:
|
||||
try:
|
||||
job = get(['get', 'job', 'state-hub-migrate', '--ignore-not-found', '-o', 'json'])
|
||||
if job is None:
|
||||
result['poll_failures'] += 1
|
||||
elif job:
|
||||
meta = job['metadata']
|
||||
images = [c['image'] for c in job['spec']['template']['spec']['containers']]
|
||||
if (images != [IMAGE] or meta.get('labels', {}).get('app.kubernetes.io/instance') != 'state-hub'
|
||||
or meta.get('labels', {}).get('app.kubernetes.io/managed-by') != 'Helm'
|
||||
or meta.get('annotations', {}).get('helm.sh/hook') != 'pre-install,pre-upgrade'):
|
||||
result['unexpected_job_identity'] = True
|
||||
break
|
||||
entry = result['jobs'].setdefault(meta['uid'], {'uid': meta['uid'], 'name': meta['name'],
|
||||
'created_at': meta['creationTimestamp'], 'image': IMAGE, 'managed_by': 'Helm',
|
||||
'release': 'state-hub', 'hook': 'pre-install,pre-upgrade', 'pods': {}})
|
||||
entry['last_status'] = {k: job.get('status', {}).get(k) for k in ['active', 'succeeded', 'failed']}
|
||||
pods = get(['get', 'pods', '-l', 'job-name=state-hub-migrate', '-o', 'json'])
|
||||
if pods:
|
||||
for pod in pods['items']:
|
||||
owners = pod['metadata'].get('ownerReferences', [])
|
||||
if not any(o['uid'] == meta['uid'] and o['kind'] == 'Job' for o in owners):
|
||||
continue
|
||||
entry['pods'][pod['metadata']['uid']] = {'name': pod['metadata']['name'],
|
||||
'uid': pod['metadata']['uid'], 'owner_job_uid': meta['uid'],
|
||||
'pod_ip': pod.get('status', {}).get('podIP'), 'phase': pod.get('status', {}).get('phase')}
|
||||
elif result['jobs']:
|
||||
result['job_absent_after_observation'] = True
|
||||
(BASE / 'job-observation.json').write_text(json.dumps(result, indent=2) + '\n')
|
||||
if (BASE / 'helm-return.json').exists():
|
||||
result['helm_return_observed'] = True
|
||||
break
|
||||
except (subprocess.TimeoutExpired, ValueError, KeyError):
|
||||
result['poll_failures'] += 1
|
||||
time.sleep(0.7)
|
||||
result['ended_at'] = datetime.now(timezone.utc).isoformat()
|
||||
(BASE / 'job-observation.json').write_text(json.dumps(result, indent=2) + '\n')
|
||||
print(json.dumps({'observed_trial_jobs': len(result['jobs']), 'mutation': False}))
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
"""Resolve and inspect public guard image bytes; no pull, install or execution."""
|
||||
from pathlib import Path,PurePosixPath
|
||||
import argparse,hashlib,io,json,re,tarfile,time,urllib.request,urllib.parse
|
||||
p=argparse.ArgumentParser();p.add_argument('--commit',required=True);a=p.parse_args();assert re.fullmatch('[0-9a-f]{40}',a.commit)
|
||||
here=Path(__file__).resolve().parent;owner=Path('/home/worsch/state-hub');short=a.commit[:7]
|
||||
host='https://forgejo.coulomb.social';base='/v2/coulomb/state-hub/';deadline=time.monotonic()+180
|
||||
class NoRedirect(urllib.request.HTTPRedirectHandler):
|
||||
def redirect_request(self,*args,**kwargs):raise RuntimeError('redirect-refused')
|
||||
opener=urllib.request.build_opener(urllib.request.ProxyHandler({}),NoRedirect)
|
||||
def get(path,limit,token=None,accept=None):
|
||||
assert time.monotonic()<deadline
|
||||
headers={}
|
||||
if token:headers['Authorization']='Bearer '+token
|
||||
if accept:headers['Accept']=accept
|
||||
with opener.open(urllib.request.Request(host+path,headers=headers),timeout=30) as r:raw=r.read(limit+1)
|
||||
assert len(raw)<=limit
|
||||
return raw
|
||||
query=urllib.parse.urlencode({'service':'container_registry','scope':'repository:coulomb/state-hub:pull'})
|
||||
auth=json.loads(get('/v2/token?'+query,65536));token=auth.get('token',auth.get('access_token'));assert isinstance(token,str) and len(token)<60000
|
||||
raw=get(base+'manifests/main-'+short,1048576,token,'application/vnd.docker.distribution.manifest.v2+json');digest='sha256:'+hashlib.sha256(raw).hexdigest()
|
||||
assert get(base+'manifests/'+digest,1048576,token,'application/vnd.docker.distribution.manifest.v2+json')==raw
|
||||
m=json.loads(raw);assert m['schemaVersion']==2 and len(m['layers'])<=80 and sum(v['size'] for v in m['layers'])<=500*1024**2
|
||||
selected=['api/services/historical_projection.py','api/routers/workstreams.py','scripts/consistency_check.py','api/services/forge_projection.py','migrations/versions/c4d5e6f7a8b9_inbox_feed_revision.py']
|
||||
expected={'app/'+rel:'sha256:'+hashlib.sha256((owner/rel).read_bytes()).hexdigest() for rel in selected}
|
||||
expected['tmp/.ci-build-id']='sha256:'+hashlib.sha256((short+'\n').encode()).hexdigest()
|
||||
files={};packages={};layers=[]
|
||||
for layer in m['layers']:
|
||||
pin=layer['digest'];assert re.fullmatch('sha256:[0-9a-f]{64}',pin) and 0<=layer['size']<=160*1024**2
|
||||
blob=get(base+'blobs/'+pin,layer['size'],token);assert len(blob)==layer['size'] and 'sha256:'+hashlib.sha256(blob).hexdigest()==pin
|
||||
layer_files={};layer_packages={};whiteouts=[]
|
||||
with tarfile.open(fileobj=io.BytesIO(blob),mode='r|*') as tar:
|
||||
count=0
|
||||
for entry in tar:
|
||||
count+=1;assert count<=100000 and time.monotonic()<deadline
|
||||
path=entry.name.removeprefix('./');assert not path.startswith('/') and '..' not in PurePosixPath(path).parts
|
||||
parent,_,name=path.rpartition('/')
|
||||
if name.startswith('.wh.'):
|
||||
whiteouts.append((parent,name));continue
|
||||
code=path in expected or '/site-packages/hub_core/' in path and path.endswith('.py') or path.startswith('tmp/hub-core/hub_core/') and path.endswith('.py')
|
||||
meta='/site-packages/' in path and path.endswith('.dist-info/METADATA')
|
||||
if not code and not meta:continue
|
||||
assert entry.isfile() and entry.size<=2*1024**2;data=tar.extractfile(entry).read(2*1024**2+1);assert len(data)==entry.size
|
||||
if code:layer_files[path]='sha256:'+hashlib.sha256(data).hexdigest()
|
||||
if meta:
|
||||
text=data.decode();names=re.findall(r'^Name: ([A-Za-z0-9_.-]+)$',text,re.M);versions=re.findall(r'^Version: ([A-Za-z0-9_.+!-]+)$',text,re.M);assert len(names)==len(versions)==1
|
||||
layer_packages[path]={'name':names[0],'version':versions[0],'metadata_sha256':'sha256:'+hashlib.sha256(data).hexdigest()}
|
||||
for parent,name in whiteouts:
|
||||
erased=str(PurePosixPath(parent)/name[4:])
|
||||
for view in [files,packages]:
|
||||
for key in list(view):
|
||||
opaque=name=='.wh..wh..opq' and (not parent or key.startswith(parent+'/'))
|
||||
if opaque or key==erased or key.startswith(erased+'/'):del view[key]
|
||||
files.update(layer_files);packages.update(layer_packages)
|
||||
layers.append({'digest':pin,'compressed_bytes':len(blob),'archive_entries':count,'whiteouts':len(whiteouts)});del blob
|
||||
assert all(files.get(k)==v for k,v in expected.items()),'selected-source-or-build-id-mismatch'
|
||||
old=json.loads((here/'statehub-public-image-inspection.json').read_text());old_packages=old['python_package_metadata'];old_hub={k:v for k,v in old['files'].items() if '/hub_core/' in k};new_hub={k:v for k,v in files.items() if '/hub_core/' in k}
|
||||
r={'schema':'statehub.guard-image-inspection.v1','status':'verified-selected-public-image-bytes','source_commit':a.commit,'tag':'main-'+short,'image_digest':digest,'selected_source_hashes':expected,'observed_files':files,'layers':layers,'python_package_metadata':packages,'package_metadata_equal_to_deployed_image':packages==old_packages,'hub_core_source_hashes_equal_to_deployed_image':new_hub==old_hub,'image_executed':False,'image_installed_or_daemon_pulled':False,'anonymous_protocol_only':True,'token_retained':False,'limitations':['Selected code hashes and package inventory do not prove full application acceptance or hermetic provenance.','Floating dependency origins remain unknown; observed byte changes require explicit review.','New API behavior is not deployed or natively accepted.']}
|
||||
del token,auth
|
||||
(here/('guard-image-'+short+'-inspection.json')).write_text(json.dumps(r,indent=2)+'\n');print(json.dumps({k:r[k] for k in ['status','source_commit','image_digest','package_metadata_equal_to_deployed_image','hub_core_source_hashes_equal_to_deployed_image']}))
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"cli_module": "/home/worsch/state-hub/.venv/lib/python3.12/site-packages/custodian_cli.py",
|
||||
"checker_path": "/home/worsch/state-hub/.venv/lib/python3.12/site-packages/scripts/consistency_check.py",
|
||||
"loaded_checker_sha256": "eaee916f46a11f49d2621fc9386717d73b7fd517acbe4b695e7b551d67bfa8f7",
|
||||
"source_checker_sha256": "eaee916f46a11f49d2621fc9386717d73b7fd517acbe4b695e7b551d67bfa8f7",
|
||||
"historical_module": "/home/worsch/state-hub/api/services/historical_projection.py",
|
||||
"historical_module_sha256": "e24e57c58f4aaa4ec9d9bf088009ac69006e53f75b9477ee53dc26c4e850b975",
|
||||
"loaded_checker_matches_source": true
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
from pathlib import Path
|
||||
import json,hashlib,subprocess,sys
|
||||
here=Path(__file__).resolve().parent;root=Path('/home/worsch/railiance-cluster');backup=here/'railiance-cluster-publication-backup'
|
||||
sys.path.insert(0,'/home/worsch/repo-manager/src')
|
||||
from repo_manager.identifiers import ensure_missing_work_record_identifiers
|
||||
sha=lambda p:'sha256:'+hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
def git(*a):return subprocess.check_output(['git','-C',str(root),*a],stderr=subprocess.PIPE)
|
||||
manifest=json.loads((here/'cluster-integration.json').read_text());assert git('rev-parse','HEAD').decode().strip()==manifest['base_revision']
|
||||
originals=json.loads((backup/'originals.json').read_text())
|
||||
for name,index in originals.items():
|
||||
assert (root/name).read_bytes()==(backup/(name+'.worktree')).read_bytes()
|
||||
assert git('ls-files','--stage','--',name).decode()==index
|
||||
for name,h in manifest['files'].items():assert sha(root/name)==h
|
||||
preview=json.loads((here/'registrar-followthrough-r1/actual-cluster-allocator-preview.json').read_text())
|
||||
plan=ensure_missing_work_record_identifiers(root,execute=False);assert plan==preview
|
||||
assert len(plan['assignments'])==7 and len(plan['historical_invalid_identifiers'])==15 and len(plan['historical_identity_collisions'])==5
|
||||
assert set(plan['files_changed'])=={'workplans/ADHOC-2026-07-27.md','workplans/RCLUSTER-WP-0018-keycape-original-tls-recovery.md'}
|
||||
frozen={p:sha(root/p) for p in ['workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md','workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md','workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md']}
|
||||
for name in plan['files_changed']:(backup/(Path(name).name+'.before-allocation')).write_bytes((root/name).read_bytes())
|
||||
assigned=ensure_missing_work_record_identifiers(root,execute=True)
|
||||
assert assigned['assignments']==plan['assignments']
|
||||
assert {p:sha(root/p) for p in frozen}==frozen
|
||||
# Existing ad-hoc file receives one normal managed UUID field only.
|
||||
ad=root/'workplans/ADHOC-2026-07-27.md';before=(backup/(ad.name+'.before-allocation')).read_text();after=ad.read_text();assert after.replace('state_hub_task_id: "3e3f39d3-5070-5ba3-97db-19f5f361f264"\n','')==before
|
||||
paths=[*manifest['files'],'workplans/ADHOC-2026-07-27.md']
|
||||
git('add','--',*paths)
|
||||
r=subprocess.run(['git','-C',str(root),'commit','--only','-m','feat(recovery): prepare owner-bound offline TLS continuity preflight','--',*paths],capture_output=True,text=True,timeout=180)
|
||||
receipt={'owner':'railiance-cluster','status':'committed' if r.returncode==0 else 'commit-refused','exit_code':r.returncode,'head':git('rev-parse','HEAD').decode().strip(),'managed_identifier_assignments':assigned,'frozen_archives_unchanged':True,'files':{p:sha(root/p) for p in paths},'stdout':r.stdout,'stderr':r.stderr}
|
||||
(here/'railiance-cluster-commit.json').write_text(json.dumps(receipt,indent=2)+'\n')
|
||||
for name,index in originals.items():
|
||||
assert (root/name).read_bytes()==(backup/(name+'.worktree')).read_bytes()
|
||||
assert git('ls-files','--stage','--',name).decode()==index
|
||||
print(json.dumps({k:receipt[k] for k in ['status','exit_code','head']}));raise SystemExit(r.returncode)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
"""Publish explicit rollout/repair evidence while retaining all unrelated work."""
|
||||
from pathlib import Path
|
||||
import hashlib,json,subprocess
|
||||
root=Path('/home/worsch/the-custodian');base=Path(__file__).resolve().parent;backup=base/'custodian-publication-backup'
|
||||
def git(*a):return subprocess.check_output(['git','-C',str(root),*a],stderr=subprocess.PIPE)
|
||||
manifest=json.loads((base/'rollout-publication-inputs.json').read_text());paths=[r['path'] for r in manifest['files']]+[str((base/'rollout-publication-inputs.json').relative_to(root))]
|
||||
assert git('rev-parse','HEAD').decode().strip()=='f8c7dfa2a9c7919879b62c77eb39582b3e23654e'
|
||||
for r in manifest['files']:assert hashlib.sha256((root/r['path']).read_bytes()).hexdigest()==r['sha256'],r['path']
|
||||
originals=json.loads((backup/'originals.json').read_text());other=json.loads((backup/'other-dirty-hashes.json').read_text())
|
||||
def check():
|
||||
for name,index in originals.items():
|
||||
assert (root/name).read_bytes()==(backup/(name+'.worktree')).read_bytes()
|
||||
assert git('ls-files','--stage','--',name).decode()==index
|
||||
assert all(hashlib.sha256((root/name).read_bytes()).hexdigest()==pin for name,pin in other.items())
|
||||
check();git('add','--',*paths)
|
||||
r=subprocess.run(['git','-C',str(root),'commit','--only','-m','Record verified receiver rollout and frozen-binding guard successor','--',*paths],capture_output=True,text=True,timeout=240)
|
||||
head=git('rev-parse','HEAD').decode().strip();receipt={'status':'committed' if r.returncode==0 else 'commit-refused','head':head,'exit_code':r.returncode,'paths':paths,'stdout':r.stdout,'stderr':r.stderr,'unrelated_work_preserved':True}
|
||||
(base/'rollout-checkpoint-commit.json').write_text(json.dumps(receipt,indent=2)+'\n');check()
|
||||
print(json.dumps({k:receipt[k] for k in ['status','head','exit_code','unrelated_work_preserved']}));raise SystemExit(r.returncode)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"status": "verifier-expectation-failed",
|
||||
"reason": "Incorrectly required every closed source dependency to be materialized; existing projection retains/creates selected open-task edges. No source or Hub mutation occurred.",
|
||||
"assertion": "missing-internal-edge"
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
"""Read-only final source/remote/Hub binding check, retaining only public identifiers."""
|
||||
from pathlib import Path
|
||||
import hashlib,json,re,subprocess,urllib.request,yaml
|
||||
here=Path(__file__).resolve().parent
|
||||
specs=[('state-hub','STATE-WP-0095','9eaa36143ea18111c522ea14d32978495b4e5030',10),('repo-manager','RMGR-WP-0019','1dacdd4a31c8e9b1bb1ddfb0c349dfb87e29e8cd',6),('repo-manager','RMGR-WP-0021','1dacdd4a31c8e9b1bb1ddfb0c349dfb87e29e8cd',5),('railiance-cluster','RCLUSTER-WP-0018','6ae3f847c83da3402883453ee7110457bfb2d0d6',5)]
|
||||
def git(root,*a):return subprocess.check_output(['git','--no-optional-locks','-C',str(root),*a],stderr=subprocess.PIPE,timeout=30).decode().strip()
|
||||
def get(path):return json.load(urllib.request.urlopen('http://127.0.0.1:8000'+path,timeout=20))
|
||||
health=get('/state/health');assert health['instance_role']=='primary' and health['instance_label']=='railiance01'
|
||||
rows=[];snapshots={};targets={'NK-WP-0049-T33':'56c8d469-7a10-5d50-a30e-95cbf3cd4756'}
|
||||
for repo,wp,head,count in specs:
|
||||
root=Path('/home/worsch')/repo;assert git(root,'rev-parse','HEAD')==head
|
||||
paths=list((root/'workplans').glob(wp+'-*.md'));assert len(paths)==1;path=paths[0];raw=path.read_text();assert git(root,'show',head+':'+str(path.relative_to(root)))==raw.strip()
|
||||
front=yaml.safe_load(raw.split('---',2)[1]);tasks=[yaml.safe_load(v) for v in re.findall(r'```task\n(.*?)\n```',raw,re.S)];assert len(tasks)==count
|
||||
for task in tasks:targets[task['id']]=task['state_hub_task_id']
|
||||
rows.append(dict(repo=repo,wp=wp,head=head,source_path=str(path),front=front,tasks=tasks,source_sha256=hashlib.sha256(path.read_bytes()).hexdigest()))
|
||||
result=[]
|
||||
for item in rows:
|
||||
repo=item['repo'];root=Path('/home/worsch')/repo
|
||||
remote=git(root,'ls-remote','origin','refs/heads/main').split()[0];assert remote==item['head']
|
||||
snapshot=snapshots.setdefault(repo,get('/repos/'+repo+'/work-record-projection/snapshot'))
|
||||
plan=next(p for p in snapshot['workplans'] if p['id']==item['front']['state_hub_workstream_id']);assert plan['status']==item['front']['status']
|
||||
actual=[t for t in snapshot['tasks'] if t.get('workplan_id',t.get('workstream_id'))==plan['id']];assert len(actual)==len(item['tasks']);byid={t['id']:t for t in actual}
|
||||
for task in item['tasks']:
|
||||
t=byid[task['state_hub_task_id']];assert t['record_id']==task['id'] and t['status']==task['status']
|
||||
if 'needs_human' in task:assert bool(t.get('needs_human'))==task['needs_human']
|
||||
expected={(t['state_hub_task_id'],targets[d],'starts_after') for t in item['tasks'] for d in t.get('depends_on',[]) if d in targets}
|
||||
edges=[e for e in snapshot['dependencies'] if e['from_workplan_id']==plan['id'] and e.get('from_task_id')]
|
||||
observed={(e['from_task_id'],e.get('to_task_id'),e['relationship_type']) for e in edges}
|
||||
assert observed<=expected,'undeclared-projected-edge'
|
||||
ownids={t['state_hub_task_id'] for t in item['tasks']};assert {(a,b,c) for a,b,c in expected if b in ownids}<=observed,'missing-internal-edge'
|
||||
result.append({'repo':repo,'workplan_id':item['wp'],'commit':item['head'],'remote_exact':True,'source_sha256':item['source_sha256'],'status':plan['status'],'tasks':[{k:t[k] for k in ['id','record_id','status','needs_human'] if k in t} for t in actual],'projected_edges':[{'from_task_id':a,'to_task_id':b,'relationship_type':c} for a,b,c in sorted(observed)],'unprojected_external_declarations':[{'from_task_id':a,'to_task_id':b,'relationship_type':c} for a,b,c in sorted(expected-observed)]})
|
||||
v={'schema':'custodian.rollout-owner-bindings.v1','status':'verified','primary':'primary/railiance01','task_bindings':sum(len(r['tasks']) for r in result),'rows':result,'runtime_deployed_source':'51bc70d1ff8f25450a8650d8e50caf053093a02e','successor_runtime_source_not_deployed':'c103cbf3c8bed5dba87a03e320595ddded9301a3','native_target_gates_closed':0}
|
||||
(here/'rollout-owner-bindings.json').write_text(json.dumps(v,indent=2)+'\n');print(json.dumps({'status':'verified','task_bindings':v['task_bindings']}))
|
||||
Loading…
Add table
Add a link
Reference in a new issue