Record verified production Clock custody and authority acceptance
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
This commit is contained in:
codex 2026-09-15 23:13:13 +02:00
parent e70ef2f32a
commit 3bd3a2e87b
5 changed files with 102 additions and 8 deletions

View file

@ -3,7 +3,7 @@ kind: credential-change-request
schema_version: 1 schema_version: 1
request_type: attended-host-key-delivery request_type: attended-host-key-delivery
title: Railiance Clock authority signing key custody and host delivery title: Railiance Clock authority signing key custody and host delivery
status: approved status: verified
created: '2026-09-15' created: '2026-09-15'
updated: '2026-09-15' updated: '2026-09-15'
requester: requester:
@ -53,7 +53,7 @@ openbao:
access_frontdoor: access_frontdoor:
type: attended-host-file type: attended-host-file
catalog_id: openbao-platform-admin-login catalog_id: openbao-platform-admin-login
readiness: approved-pending-apply readiness: ready
resolvable: false resolvable: false
delivery: delivery:
surface: attended-host-file surface: attended-host-file
@ -93,7 +93,18 @@ verification:
host key verification is required. host key verification is required.
- Fresh public key, artifact SHA256, readiness and signed sample acceptance are - Fresh public key, artifact SHA256, readiness and signed sample acceptance are
recorded. recorded.
evidence: [] evidence:
- kind: attended_host_custody
result: passed
receipt: docs/evidence/2026-09-15-railiance-clock-host-custody.json
details: OpenBao KV version 1 and matching public-key fingerprint on private host
file; contained login, child operation and self-revocation succeeded.
- kind: production_authority_acceptance
result: passed
receipt: docs/evidence/2026-09-15-railiance-clock-production.json
details: Active/enabled private authority; pinned wheel; bounded host health;
three signed workstation samples cross-checked through SSH, wrong key id refused.
No standing workload read grant.
lifecycle: lifecycle:
deactivate: Stop authority and withdraw consumer trust. Retain custody and host deactivate: Stop authority and withdraw consumer trust. Retain custody and host
key until owner retention decision. key until owner retention decision.

View file

@ -34,3 +34,23 @@ Rotation is a separate explicit operation: stop the authority, CAS against the
known current custody version, replace the host key atomically, and distribute known current custody version, replace the host key atomically, and distribute
new public key/epoch trust before resuming clients. This initial-admission helper new public key/epoch trust before resuming clients. This initial-admission helper
deliberately does not implement rotation or overwrite keys. deliberately does not implement rotation or overwrite keys.
## Accepted production deployment
Version 1 custody and native workstation samples passed on 2026-09-15. See
`docs/evidence/2026-09-15-railiance-clock-host-custody.json` and
`docs/evidence/2026-09-15-railiance-clock-production.json`.
Use the documented private OpenBao endpoint `http://127.0.0.1:18200` for the
attended envelope: the configured public hostname presented an untrusted
certificate during deployment. TLS verification was not disabled.
Authority endpoint on Railiance01: `http://127.0.0.1:8787/v1/time-samples`;
readiness: `http://127.0.0.1:8787/readyz`. Workstations use an authenticated SSH
loopback connection and separately admitted public key/epoch. Trust files last
at most 15 minutes and do not survive a workstation boot. Optional integrations
are not globally enabled by this deployment; operational trust refresh remains
in RCLK-WP-0005-T06 and consumer adoption in RCLK-WP-0004.
The health export sandbox allows read-only adjtimex, which ProtectClock would
block even with modes=0. Empty bounding/ambient capability sets deny clock writes.
The authority keeps ProtectClock enabled.

View file

@ -0,0 +1,15 @@
{
"schema": "platform.railiance-clock-host-custody.v1",
"status": "delivered_pending_authority_acceptance",
"stage": "complete",
"ccr": "CCR-2026-0028",
"kv_version": 1,
"kid": "railiance01-clock-20260915-v1",
"public_key_pem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6dgMrD13yE5PKBxDSl7ledal2vwd\nDmPZEjdYW8UK48Abto6dfuJfjCHeC+Jl3w1D5VZLfbbzD114ymKTszE80A==\n-----END PUBLIC KEY-----\n",
"public_key_sha256": "bd583446b5ed61d086806b2a0c5aaf33a875b751e45599e75335d1f415be609a",
"host": {
"public_key_sha256": "bd583446b5ed61d086806b2a0c5aaf33a875b751e45599e75335d1f415be609a",
"owner": "railiance-clock",
"mode": "0600"
}
}

View file

@ -0,0 +1,42 @@
{
"scope": "production authority acceptance",
"host": "railiance01",
"client": "Linux/WSL via SSH loopback",
"os_clocks_changed": false,
"samples": [
{
"width_ms": 101.667802,
"railiance_minus_local_midpoint_ms": 253.602519,
"independent_host_cross_check": true
},
{
"width_ms": 108.062875,
"railiance_minus_local_midpoint_ms": 325.159405,
"independent_host_cross_check": true
},
{
"width_ms": 160.436282,
"railiance_minus_local_midpoint_ms": 371.73663,
"independent_host_cross_check": true
}
],
"wrong_kid_refused": true,
"epoch": "b1164ccb-a4c2-4cc8-adf8-1d5597de697b",
"kid": "railiance01-clock-20260915-v1",
"public_key_sha256": "bd583446b5ed61d086806b2a0c5aaf33a875b751e45599e75335d1f415be609a",
"trust_lifetime_seconds": 900,
"wheel_sha256": "89d1419f038eff485a27a4206a19101ce7ca2976092971bbc796a77188fc93ad",
"listener": "127.0.0.1:8787",
"service_active": true,
"service_enabled": true,
"authority_clock_write_capabilities": [],
"health_clock_write_capabilities": [],
"readiness": "usable",
"key_custody": "CCR-2026-0028 version 1",
"final_ansible_convergence": {
"ok": 29,
"changed": 0,
"failed": 0,
"unreachable": 0
}
}

View file

@ -493,7 +493,7 @@ def render_summary(ccr: dict[str, Any], warnings: list[str]) -> str:
f" {openbao['kv_path']}", f" {openbao['kv_path']}",
f" {fields}", f" {fields}",
"Policy:", "Policy:",
f" {openbao['policy_name']}", f" {openbao.get('policy_name', 'none (attended host delivery)')}",
"Auth binding:", "Auth binding:",
] ]
if auth.get("method") == "token": if auth.get("method") == "token":
@ -664,6 +664,8 @@ def auth_payload(ccr: dict[str, Any]) -> dict[str, Any]:
def render_plan(ccr: dict[str, Any]) -> str: def render_plan(ccr: dict[str, Any]) -> str:
if ccr.get("request_type") == "attended-host-key-delivery":
return "Use docs/credential-lane-designs/railiance-clock-host-delivery.md through the attended owner procedure. No workload policy or auth role is created."
openbao = ccr["openbao"] openbao = ccr["openbao"]
auth = openbao["auth"] auth = openbao["auth"]
payload = auth_payload(ccr) payload = auth_payload(ccr)
@ -693,6 +695,8 @@ def render_plan(ccr: dict[str, Any]) -> str:
def render_operator_commands(ccr: dict[str, Any]) -> str: def render_operator_commands(ccr: dict[str, Any]) -> str:
if ccr.get("request_type") == "attended-host-key-delivery":
return "Use docs/credential-lane-designs/railiance-clock-host-delivery.md through the attended owner procedure. No workload policy or auth role is created."
openbao = ccr["openbao"] openbao = ccr["openbao"]
auth = openbao["auth"] auth = openbao["auth"]
auth_path = f"auth/{auth['mount']}/role/{auth['role']}" auth_path = f"auth/{auth['mount']}/role/{auth['role']}"
@ -898,7 +902,7 @@ def applier_dry_run_payload(ccr: dict[str, Any], warnings: list[str]) -> dict[st
{ {
"kind": "policy_write", "kind": "policy_write",
"openbao_path": f"sys/policies/acl/{openbao['policy_name']}", "openbao_path": f"sys/policies/acl/{openbao['policy_name']}",
"policy_name": openbao["policy_name"], "policy_name": openbao.get("policy_name"),
"source": openbao["policy_file"], "source": openbao["policy_file"],
"body": generated_policy_hcl(ccr).rstrip(), "body": generated_policy_hcl(ccr).rstrip(),
}, },
@ -998,6 +1002,8 @@ def render_applier_apply_plan(ccr: dict[str, Any], warnings: list[str]) -> str:
def runbook_readiness_blockers(ccr: dict[str, Any]) -> list[str]: def runbook_readiness_blockers(ccr: dict[str, Any]) -> list[str]:
if ccr.get("request_type") == "attended-host-key-delivery":
return ["use the attended owner procedure in docs/credential-lane-designs/railiance-clock-host-delivery.md"]
blockers: list[str] = [] blockers: list[str] = []
status = ccr.get("status") status = ccr.get("status")
if status not in RUNBOOK_ALLOWED_STATUSES: if status not in RUNBOOK_ALLOWED_STATUSES:
@ -1281,7 +1287,7 @@ def lifecycle_payload(ccr: dict[str, Any], action: str) -> dict[str, Any]:
"openbao": { "openbao": {
"secret_path": openbao["kv_path"], "secret_path": openbao["kv_path"],
"fields": openbao["fields"], "fields": openbao["fields"],
"policy_name": openbao["policy_name"], "policy_name": openbao.get("policy_name"),
"auth_role_path": auth_role_path, "auth_role_path": auth_role_path,
"disable_commands": disable_commands, "disable_commands": disable_commands,
}, },
@ -1627,7 +1633,7 @@ def status_payload(ccr: dict[str, Any], warnings: list[str]) -> dict[str, Any]:
"mount": openbao["mount"], "mount": openbao["mount"],
"kv_path": openbao["kv_path"], "kv_path": openbao["kv_path"],
"fields": openbao["fields"], "fields": openbao["fields"],
"policy_name": openbao["policy_name"], "policy_name": openbao.get("policy_name"),
"auth_mount": auth.get("mount"), "auth_mount": auth.get("mount"),
"auth_method": auth.get("method"), "auth_method": auth.get("method"),
"auth_role": auth.get("role"), "auth_role": auth.get("role"),
@ -1783,7 +1789,7 @@ def decision_template_context(ccr: dict[str, Any]) -> dict[str, str]:
return { return {
"id": ccr["id"], "id": ccr["id"],
"kv_path": openbao["kv_path"], "kv_path": openbao["kv_path"],
"policy_name": openbao["policy_name"], "policy_name": openbao.get("policy_name"),
"auth_role_path": f"auth/{auth['mount']}/role/{auth['role']}", "auth_role_path": f"auth/{auth['mount']}/role/{auth['role']}",
"decision_link": state_hub.get("decision_api_url") "decision_link": state_hub.get("decision_api_url")
or state_hub.get("decision_dashboard_url") or state_hub.get("decision_dashboard_url")