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
request_type: attended-host-key-delivery
title: Railiance Clock authority signing key custody and host delivery
status: approved
status: verified
created: '2026-09-15'
updated: '2026-09-15'
requester:
@ -53,7 +53,7 @@ openbao:
access_frontdoor:
type: attended-host-file
catalog_id: openbao-platform-admin-login
readiness: approved-pending-apply
readiness: ready
resolvable: false
delivery:
surface: attended-host-file
@ -93,7 +93,18 @@ verification:
host key verification is required.
- Fresh public key, artifact SHA256, readiness and signed sample acceptance are
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:
deactivate: Stop authority and withdraw consumer trust. Retain custody and host
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
new public key/epoch trust before resuming clients. This initial-admission helper
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" {fields}",
"Policy:",
f" {openbao['policy_name']}",
f" {openbao.get('policy_name', 'none (attended host delivery)')}",
"Auth binding:",
]
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:
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"]
auth = openbao["auth"]
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:
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"]
auth = openbao["auth"]
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",
"openbao_path": f"sys/policies/acl/{openbao['policy_name']}",
"policy_name": openbao["policy_name"],
"policy_name": openbao.get("policy_name"),
"source": openbao["policy_file"],
"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]:
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] = []
status = ccr.get("status")
if status not in RUNBOOK_ALLOWED_STATUSES:
@ -1281,7 +1287,7 @@ def lifecycle_payload(ccr: dict[str, Any], action: str) -> dict[str, Any]:
"openbao": {
"secret_path": openbao["kv_path"],
"fields": openbao["fields"],
"policy_name": openbao["policy_name"],
"policy_name": openbao.get("policy_name"),
"auth_role_path": auth_role_path,
"disable_commands": disable_commands,
},
@ -1627,7 +1633,7 @@ def status_payload(ccr: dict[str, Any], warnings: list[str]) -> dict[str, Any]:
"mount": openbao["mount"],
"kv_path": openbao["kv_path"],
"fields": openbao["fields"],
"policy_name": openbao["policy_name"],
"policy_name": openbao.get("policy_name"),
"auth_mount": auth.get("mount"),
"auth_method": auth.get("method"),
"auth_role": auth.get("role"),
@ -1783,7 +1789,7 @@ def decision_template_context(ccr: dict[str, Any]) -> dict[str, str]:
return {
"id": ccr["id"],
"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']}",
"decision_link": state_hub.get("decision_api_url")
or state_hub.get("decision_dashboard_url")