Add OpenBao emergency lockdown runbook

This commit is contained in:
2026-05-25 18:31:48 +02:00
parent b9bad47a21
commit e2540529f0
2 changed files with 153 additions and 15 deletions

View File

@@ -594,6 +594,7 @@ def merged_approval_metadata(
"openbao_trial_material_exposed", "openbao_trial_material_exposed",
"openbao_compromise_response_complete", "openbao_compromise_response_complete",
"openbao_unseal_keys_rotated", "openbao_unseal_keys_rotated",
"openbao_emergency_lockdown_drilled",
"restore_drill_passed", "restore_drill_passed",
): ):
if field in payload: if field in payload:
@@ -811,6 +812,7 @@ def metadata_template() -> dict[str, Any]:
"openbao_trial_material_exposed": False, "openbao_trial_material_exposed": False,
"openbao_compromise_response_complete": False, "openbao_compromise_response_complete": False,
"openbao_unseal_keys_rotated": False, "openbao_unseal_keys_rotated": False,
"openbao_emergency_lockdown_drilled": False,
"root_token_disposition": "", "root_token_disposition": "",
"restore_drill_passed": False, "restore_drill_passed": False,
"cleanup_complete": False, "cleanup_complete": False,
@@ -1339,6 +1341,7 @@ def runbook_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
trial_exposed = yes(data, "openbao_trial_material_exposed") trial_exposed = yes(data, "openbao_trial_material_exposed")
response_complete = yes(data, "openbao_compromise_response_complete") response_complete = yes(data, "openbao_compromise_response_complete")
keys_rotated = yes(data, "openbao_unseal_keys_rotated") keys_rotated = yes(data, "openbao_unseal_keys_rotated")
lockdown_drilled = yes(data, "openbao_emergency_lockdown_drilled")
openbao_direct_taint = openbao_trial_taint(data, "direct") openbao_direct_taint = openbao_trial_taint(data, "direct")
openbao_downstream_taint = openbao_trial_taint(data, "downstream") openbao_downstream_taint = openbao_trial_taint(data, "downstream")
@@ -1357,6 +1360,12 @@ def runbook_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
rotate_status = "blocked" rotate_status = "blocked"
rotate_location = "Record the key-compromise condition or schedule a normal rotation first." rotate_location = "Record the key-compromise condition or schedule a normal rotation first."
lockdown_status = "done" if lockdown_drilled else "todo"
lockdown_location = "Requires an unsealed OpenBao instance and a token with root or sudo on sys/seal."
if not initialized:
lockdown_status = "blocked"
lockdown_location = "OpenBao is not recorded as unsealed; sealing is only meaningful while it is serving requests."
return [ return [
add_taint( add_taint(
{ {
@@ -1382,6 +1391,18 @@ def runbook_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
}, },
openbao_downstream_taint if trial_exposed and not response_complete else {}, openbao_downstream_taint if trial_exposed and not response_complete else {},
), ),
add_taint(
{
"name": "Emergency lock-down",
"description": "Seal Railiance OpenBao to stop access to stored OpenBao assets until a later unseal quorum reopens it.",
"subsystem": "Railiance OpenBao",
"responsibility": "openbao-ceremony-operator",
"email": role_email(data, "role_openbao_operator_email"),
"location": lockdown_location,
"state": lockdown_status,
},
openbao_downstream_taint if initialized else {},
),
] ]
@@ -1391,6 +1412,7 @@ def runbook_command_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
trial_exposed = yes(data, "openbao_trial_material_exposed") trial_exposed = yes(data, "openbao_trial_material_exposed")
response_complete = yes(data, "openbao_compromise_response_complete") response_complete = yes(data, "openbao_compromise_response_complete")
keys_rotated = yes(data, "openbao_unseal_keys_rotated") keys_rotated = yes(data, "openbao_unseal_keys_rotated")
lockdown_drilled = yes(data, "openbao_emergency_lockdown_drilled")
openbao_direct_taint = openbao_trial_taint(data, "direct") openbao_direct_taint = openbao_trial_taint(data, "direct")
openbao_downstream_taint = openbao_trial_taint(data, "downstream") openbao_downstream_taint = openbao_trial_taint(data, "downstream")
@@ -1418,6 +1440,20 @@ def runbook_command_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
rotate_status = "blocked" rotate_status = "blocked"
rotate_reason = "Record exposure or schedule a normal rotation before generating new shares." rotate_reason = "Record exposure or schedule a normal rotation before generating new shares."
lockdown_status = "done" if lockdown_drilled else "todo"
lockdown_reason = "Emergency lock-down drill is recorded." if lockdown_drilled else "Seals OpenBao immediately. Use only during an emergency or scheduled drill."
if not initialized:
lockdown_status = "blocked"
lockdown_reason = "OpenBao must be unsealed before emergency seal changes availability."
seal_command = (
"printf 'OpenBao token: ' >&2\n"
"read -rs OPENBAO_TOKEN\n"
"printf '\\n' >&2\n"
"printf '%s\\n' \"$OPENBAO_TOKEN\" | kubectl exec -i -n openbao openbao-0 -- "
"sh -c 'read -r BAO_TOKEN; export BAO_TOKEN; bao operator seal'\n"
"unset OPENBAO_TOKEN"
)
return [ return [
add_taint( add_taint(
{ {
@@ -1479,6 +1515,36 @@ def runbook_command_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
}, },
openbao_downstream_taint if trial_exposed and not response_complete else {}, openbao_downstream_taint if trial_exposed and not response_complete else {},
), ),
add_taint(
{
"name": "Emergency seal OpenBao",
"description": "Prompt locally for an OpenBao token and seal Railiance OpenBao without placing the token on the command line.",
"status": lockdown_status,
"status_reason": lockdown_reason,
"command": seal_command,
},
openbao_downstream_taint if initialized else {},
),
add_taint(
{
"name": "Confirm sealed status",
"description": "Check that Railiance OpenBao reports Sealed true after an emergency seal.",
"status": "todo" if initialized else "blocked",
"status_reason": "Run after emergency seal; expect Sealed true." if initialized else "OpenBao must be initialized before status confirms lock-down.",
"command": "kubectl exec -n openbao openbao-0 -- bao status",
},
openbao_downstream_taint if initialized else {},
),
add_taint(
{
"name": "Record emergency lock-down drill",
"description": "Non-secret metadata checkbox after an emergency seal drill or real lock-down is confirmed.",
"status": lockdown_status,
"status_reason": lockdown_reason,
"command": "Use the checkbox: Emergency lock-down drill recorded",
},
openbao_downstream_taint if initialized else {},
),
] ]
@@ -1490,24 +1556,36 @@ def section_gate_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
runbook_rows = runbook_payloads(data) runbook_rows = runbook_payloads(data)
artifact_rows = artifact_payloads(data) artifact_rows = artifact_payloads(data)
return [ return [
{
"key": "intro",
"name": "Introduction & Actors",
"status": "ok",
"reason": "NetKingdom purpose, global actors, and supported custody shapes are visible.",
},
{
"key": "subsystems",
"name": "Subsystems & Scopes",
"status": "ok" if all(row["state"] in {"ok", "set"} for row in subsystem_rows) else "err",
"reason": "Subsystems have install/access evidence." if all(row["state"] != "nil" for row in subsystem_rows) else "Complete subsystem setup fields and confirmations.",
},
{ {
"key": "roles", "key": "roles",
"name": "Roles & Responsibilities", "name": "Roles & Responsibilities",
"status": "ok" if role_ok else "set", "status": "ok" if role_ok else "set",
"reason": "All active bootstrap roles have a designated email." if role_ok else "Assign an email to each active bootstrap role.", "reason": "All active bootstrap roles have a designated email." if role_ok else "Assign an email to each active bootstrap role.",
}, },
{
"key": "subsystems",
"name": "Subsystems & Scope",
"status": "ok" if all(row["state"] in {"ok", "set"} for row in subsystem_rows) else "err",
"reason": "Subsystems have install/access evidence." if all(row["state"] != "nil" for row in subsystem_rows) else "Complete subsystem setup fields and confirmations.",
},
{ {
"key": "integrations", "key": "integrations",
"name": "Integration & Tests", "name": "Integration & Tests",
"status": "ok" if all(row["state"] == "ok" for row in integration_rows[:4]) else "set", "status": "ok" if all(row["state"] == "ok" for row in integration_rows[:4]) else "set",
"reason": "Identity and OpenBao preflight checks are done." if all(row["state"] == "ok" for row in integration_rows[:4]) else "Run or confirm the remaining integration checks.", "reason": "Identity and OpenBao preflight checks are done." if all(row["state"] == "ok" for row in integration_rows[:4]) else "Run or confirm the remaining integration checks.",
}, },
{
"key": "artifacts",
"name": "Artefacts & Locations",
"status": "ok" if all(row["state"] != "nil" for row in artifact_rows[:10]) else "set",
"reason": "Core custody artefacts have locations or confirmations." if all(row["state"] != "nil" for row in artifact_rows[:10]) else "Record missing artefact locations and confirmations.",
},
{ {
"key": "runbooks", "key": "runbooks",
"name": "Usecases & Runbooks", "name": "Usecases & Runbooks",
@@ -1515,10 +1593,10 @@ def section_gate_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
"reason": "Runbook states are recorded." if all(row["state"] in {"done", "blocked"} for row in runbook_rows) else "Review active runbooks and record non-secret outcomes.", "reason": "Runbook states are recorded." if all(row["state"] in {"done", "blocked"} for row in runbook_rows) else "Review active runbooks and record non-secret outcomes.",
}, },
{ {
"key": "artifacts", "key": "terminology",
"name": "Artefacts & Locations", "name": "Terminology & Patterns",
"status": "ok" if all(row["state"] != "nil" for row in artifact_rows[:10]) else "set", "status": "ok",
"reason": "Core custody artefacts have locations or confirmations." if all(row["state"] != "nil" for row in artifact_rows[:10]) else "Record missing artefact locations and confirmations.", "reason": "Shared NetKingdom security terms and patterns are documented for operators.",
}, },
] ]
@@ -1815,6 +1893,18 @@ def ui_html() -> str:
background: var(--paper); background: var(--paper);
padding: 18px; padding: 18px;
} }
#approval-form {
display: flex;
flex-direction: column;
}
.workflow-section[data-section="intro"] { order: 1; }
.workflow-section[data-section="subsystems"] { order: 2; }
.workflow-section[data-section="roles"] { order: 3; }
.workflow-section[data-section="integrations"] { order: 4; }
.workflow-section[data-section="artifacts"] { order: 5; }
.workflow-section[data-section="runbooks"] { order: 6; }
.workflow-section[data-section="terminology"] { order: 7; }
.workflow-actions { order: 8; }
.panel + .panel { margin-top: 18px; } .panel + .panel { margin-top: 18px; }
h2 { h2 {
margin: 0 0 14px; margin: 0 0 14px;
@@ -2179,8 +2269,26 @@ def ui_html() -> str:
<div class="layout"> <div class="layout">
<form id="approval-form"> <form id="approval-form">
<details class="panel workflow-section" data-section="intro" open>
<summary><span class="summary-title">1. Introduction & Actors</span><span class="state nil" data-section-state="intro">nil</span></summary>
<div class="section-gate" data-section-gate="intro">Loading introduction gate.</div>
<p class="notice">NetKingdom is the operating frame for accountable digital sovereignty: identity, custody, secrets, approvals, recovery, and emergency control are made explicit before subsystems are trusted with live assets.</p>
<div class="record-list">
<div class="record-row"><span class="state set">role</span><div><div class="record-name">King / CEO</div><div class="record-description">Ultimate accountable authority for the kingdom, final custody intent, and existential risk decisions.</div></div><div class="record-meta">Global actor</div><div class="record-meta">Always present</div></div>
<div class="record-row"><span class="state set">role</span><div><div class="record-name">Guardian / CSO</div><div class="record-description">Owns security posture, emergency lock-down readiness, incident response, and custody-risk review.</div></div><div class="record-meta">Global actor</div><div class="record-meta">Required for quorum setups</div></div>
<div class="record-row"><span class="state set">role</span><div><div class="record-name">Armorer / CTO</div><div class="record-description">Owns technical implementation, platform architecture, automation, and operational correctness.</div></div><div class="record-meta">Global actor</div><div class="record-meta">Required for quorum setups</div></div>
<div class="record-row"><span class="state set">role</span><div><div class="record-name">Steward / COO</div><div class="record-description">Owns process reliability, onboarding, offboarding, runbook hygiene, and day-to-day operating cadence.</div></div><div class="record-meta">Global actor</div><div class="record-meta">Five-person setup</div></div>
<div class="record-row"><span class="state set">role</span><div><div class="record-name">Treasurer / CFO</div><div class="record-description">Owns financial authorization, asset inventory, economic risk, and continuity of valuable holdings.</div></div><div class="record-meta">Global actor</div><div class="record-meta">Five-person setup</div></div>
</div>
<div class="choice-list">
<div class="choice"><span class="step-number">A</span><span><strong>One Person Kingdom</strong><span>King / CEO only. Best for early bootstrap and solo custody; fast but has no human quorum.</span></span></div>
<div class="choice"><span class="step-number">B</span><span><strong>Three Person Kingdom</strong><span>King / CEO plus two others. Decisions and access control target a two-of-three quorum.</span></span></div>
<div class="choice"><span class="step-number">C</span><span><strong>Five Person Kingdom</strong><span>All global actors active. Decisions and access control target a three-of-five quorum.</span></span></div>
</div>
</details>
<details class="panel workflow-section" data-section="roles" open> <details class="panel workflow-section" data-section="roles" open>
<summary><span class="summary-title">1. Roles & Responsibilities</span><span class="state nil" data-section-state="roles">nil</span></summary> <summary><span class="summary-title">3. Roles & Responsibilities</span><span class="state nil" data-section-state="roles">nil</span></summary>
<div class="section-gate" data-section-gate="roles">Loading role gate.</div> <div class="section-gate" data-section-gate="roles">Loading role gate.</div>
<p class="notice">Define who is accountable for each bootstrap role before touching subsystem-specific controls. Role chips in every record show the role name; hover them to see the designated email.</p> <p class="notice">Define who is accountable for each bootstrap role before touching subsystem-specific controls. Role chips in every record show the role name; hover them to see the designated email.</p>
<div id="roles-records" class="record-list"></div> <div id="roles-records" class="record-list"></div>
@@ -2228,7 +2336,7 @@ def ui_html() -> str:
</details> </details>
<details class="panel workflow-section" data-section="subsystems" open> <details class="panel workflow-section" data-section="subsystems" open>
<summary><span class="summary-title">2. Subsystems & Scope</span><span class="state nil" data-section-state="subsystems">nil</span></summary> <summary><span class="summary-title">2. Subsystems & Scopes</span><span class="state nil" data-section-state="subsystems">nil</span></summary>
<div class="section-gate" data-section-gate="subsystems">Loading subsystem gate.</div> <div class="section-gate" data-section-gate="subsystems">Loading subsystem gate.</div>
<p class="notice">This section is about installing each subsystem and establishing initial user access. Integration checks come later.</p> <p class="notice">This section is about installing each subsystem and establishing initial user access. Integration checks come later.</p>
<div id="subsystems-records" class="record-list"></div> <div id="subsystems-records" class="record-list"></div>
@@ -2307,7 +2415,7 @@ def ui_html() -> str:
</details> </details>
<details class="panel workflow-section" data-section="integrations" open> <details class="panel workflow-section" data-section="integrations" open>
<summary><span class="summary-title">3. Integration & Tests</span><span class="state nil" data-section-state="integrations">nil</span></summary> <summary><span class="summary-title">4. Integration & Tests</span><span class="state nil" data-section-state="integrations">nil</span></summary>
<div class="section-gate" data-section-gate="integrations">Loading integration gate.</div> <div class="section-gate" data-section-gate="integrations">Loading integration gate.</div>
<p class="notice">This section connects the subsystems and shows every console command as a copyable block. Commands still run outside this browser so secret output never enters the control surface.</p> <p class="notice">This section connects the subsystems and shows every console command as a copyable block. Commands still run outside this browser so secret output never enters the control surface.</p>
<div id="integrations-records" class="record-list"></div> <div id="integrations-records" class="record-list"></div>
@@ -2337,7 +2445,7 @@ def ui_html() -> str:
</details> </details>
<details class="panel workflow-section" data-section="runbooks" open> <details class="panel workflow-section" data-section="runbooks" open>
<summary><span class="summary-title">4. Usecases & Runbooks</span><span class="state nil" data-section-state="runbooks">nil</span></summary> <summary><span class="summary-title">6. Usecases & Runbooks</span><span class="state nil" data-section-state="runbooks">nil</span></summary>
<div class="section-gate" data-section-gate="runbooks">Loading runbook gate.</div> <div class="section-gate" data-section-gate="runbooks">Loading runbook gate.</div>
<p class="notice">Use these routines when the ceremony path changes, trial secrets are exposed, or custody material must be regenerated. The UI records only non-secret outcomes.</p> <p class="notice">Use these routines when the ceremony path changes, trial secrets are exposed, or custody material must be regenerated. The UI records only non-secret outcomes.</p>
<div id="runbooks-records" class="record-list"></div> <div id="runbooks-records" class="record-list"></div>
@@ -2345,6 +2453,7 @@ def ui_html() -> str:
<label class="choice"><input id="openbao_trial_material_exposed" type="checkbox"><span><strong>Trial key material exposed</strong><span>Init output, unseal shares, or root-token material escaped the custody boundary during a trial.</span></span></label> <label class="choice"><input id="openbao_trial_material_exposed" type="checkbox"><span><strong>Trial key material exposed</strong><span>Init output, unseal shares, or root-token material escaped the custody boundary during a trial.</span></span></label>
<label class="choice"><input id="openbao_compromise_response_complete" type="checkbox"><span><strong>Compromise response complete</strong><span>Exposed material was rotated or the trial environment was reset. No secret values are recorded here.</span></span></label> <label class="choice"><input id="openbao_compromise_response_complete" type="checkbox"><span><strong>Compromise response complete</strong><span>Exposed material was rotated or the trial environment was reset. No secret values are recorded here.</span></span></label>
<label class="choice"><input id="openbao_unseal_keys_rotated" type="checkbox"><span><strong>New unseal keys generated</strong><span>OpenBao generated replacement unseal shares under the current runbook.</span></span></label> <label class="choice"><input id="openbao_unseal_keys_rotated" type="checkbox"><span><strong>New unseal keys generated</strong><span>OpenBao generated replacement unseal shares under the current runbook.</span></span></label>
<label class="choice"><input id="openbao_emergency_lockdown_drilled" type="checkbox"><span><strong>Emergency lock-down drill recorded</strong><span>Railiance OpenBao was sealed and status-confirmed during a drill or real lock-down. No token or share is recorded here.</span></span></label>
</div> </div>
<div id="runbook-command-list" class="command-list"></div> <div id="runbook-command-list" class="command-list"></div>
</details> </details>
@@ -2375,13 +2484,34 @@ def ui_html() -> str:
<span class="label">Approval phrase for selected strategy</span> <span class="label">Approval phrase for selected strategy</span>
<input id="approval_phrase" type="text" autocomplete="off" placeholder="approve custody mode" title="Type the approval phrase only after the selected strategy, recovery material, and custody packet are ready."> <input id="approval_phrase" type="text" autocomplete="off" placeholder="approve custody mode" title="Type the approval phrase only after the selected strategy, recovery material, and custody packet are ready.">
</div> </div>
</details>
<details class="panel workflow-section" data-section="terminology" open>
<summary><span class="summary-title">7. Terminology & Patterns</span><span class="state nil" data-section-state="terminology">nil</span></summary>
<div class="section-gate" data-section-gate="terminology">Loading terminology gate.</div>
<p class="notice">These terms apply across NetKingdom. Subsystems may have their own names, but the control surface keeps the cross-subsystem security pattern visible.</p>
<div class="record-list">
<div class="record-row"><span class="state set">term</span><div><div class="record-name">Subsystem</div><div class="record-description">A bounded tool or service with its own admin surface, state, and security model.</div></div><div class="record-meta">Pattern</div><div class="record-meta">LLDAP, privacyIDEA, KeyCape, OpenBao</div></div>
<div class="record-row"><span class="state set">term</span><div><div class="record-name">Artefact</div><div class="record-description">A named credential, key, token, group, policy, packet, bundle, or evidence item with an owner and location.</div></div><div class="record-meta">Pattern</div><div class="record-meta">Track name, subsystem, role, location, state</div></div>
<div class="record-row"><span class="state set">term</span><div><div class="record-name">Custody</div><div class="record-description">The human and technical arrangement that controls who can access or recover critical material.</div></div><div class="record-meta">Pattern</div><div class="record-meta">Single king, two-of-three, three-of-five</div></div>
<div class="record-row"><span class="state set">term</span><div><div class="record-name">Quorum</div><div class="record-description">A threshold of actors or shares required before sensitive decisions or recovery actions can proceed.</div></div><div class="record-meta">Pattern</div><div class="record-meta">2 of 3, 3 of 5</div></div>
<div class="record-row"><span class="state set">term</span><div><div class="record-name">Taint</div><div class="record-description">A warning that work is downstream of a compromised, exposed, or trial-only artefact. Taint informs; it does not hard-block the operator.</div></div><div class="record-meta">Pattern</div><div class="record-meta">Keeps source reference visible</div></div>
<div class="record-row"><span class="state set">term</span><div><div class="record-name">Break-glass</div><div class="record-description">Emergency material that can restore control but is not part of normal daily operation.</div></div><div class="record-meta">Pattern</div><div class="record-meta">Retrieve, use, review, rotate if needed</div></div>
<div class="record-row"><span class="state set">term</span><div><div class="record-name">Seal and unseal</div><div class="record-description">For Railiance OpenBao, sealing stops access to stored OpenBao assets; unsealing requires the configured threshold of unseal shares.</div></div><div class="record-meta">Pattern</div><div class="record-meta">Emergency lock-down and reopening</div></div>
<div class="record-row"><span class="state set">term</span><div><div class="record-name">Root token vs unseal shares</div><div class="record-description">The Railiance OpenBao initial root token is a superuser API credential after unseal. Unseal shares control whether OpenBao can decrypt and serve requests at all.</div></div><div class="record-meta">Pattern</div><div class="record-meta">Different layers, different custody</div></div>
<div class="record-row"><span class="state set">term</span><div><div class="record-name">Least privilege</div><div class="record-description">Daily access should use scoped credentials; root, quorum, and break-glass paths remain exceptional and reviewable.</div></div><div class="record-meta">Pattern</div><div class="record-meta">Delegate, expire, audit</div></div>
</div>
</details>
<section class="panel workflow-actions">
<h2>Actions</h2>
<div class="actions"> <div class="actions">
<button class="secondary" id="save-button" type="button" title="Save the visible non-secret progress fields to local metadata.">Save progress</button> <button class="secondary" id="save-button" type="button" title="Save the visible non-secret progress fields to local metadata.">Save progress</button>
<button id="approve-button" type="submit" title="Approve the selected custody strategy only after all kit gates are satisfied.">Approve selected strategy</button> <button id="approve-button" type="submit" title="Approve the selected custody strategy only after all kit gates are satisfied.">Approve selected strategy</button>
<button class="secondary" id="refresh-button" type="button" title="Reload the local metadata and gate status from disk.">Refresh</button> <button class="secondary" id="refresh-button" type="button" title="Reload the local metadata and gate status from disk.">Refresh</button>
</div> </div>
<div id="message" class="message" role="status">Waiting for local approval.</div> <div id="message" class="message" role="status">Waiting for local approval.</div>
</details> </section>
</form> </form>
<aside> <aside>
@@ -2438,6 +2568,7 @@ def ui_html() -> str:
"openbao_trial_material_exposed", "openbao_trial_material_exposed",
"openbao_compromise_response_complete", "openbao_compromise_response_complete",
"openbao_unseal_keys_rotated", "openbao_unseal_keys_rotated",
"openbao_emergency_lockdown_drilled",
"root_token_disposition", "root_token_disposition",
"restore_drill_passed" "restore_drill_passed"
]; ];
@@ -2720,6 +2851,7 @@ def ui_html() -> str:
openbao_trial_material_exposed: document.getElementById("openbao_trial_material_exposed").checked, openbao_trial_material_exposed: document.getElementById("openbao_trial_material_exposed").checked,
openbao_compromise_response_complete: document.getElementById("openbao_compromise_response_complete").checked, openbao_compromise_response_complete: document.getElementById("openbao_compromise_response_complete").checked,
openbao_unseal_keys_rotated: document.getElementById("openbao_unseal_keys_rotated").checked, openbao_unseal_keys_rotated: document.getElementById("openbao_unseal_keys_rotated").checked,
openbao_emergency_lockdown_drilled: document.getElementById("openbao_emergency_lockdown_drilled").checked,
root_token_disposition: document.getElementById("root_token_disposition").value, root_token_disposition: document.getElementById("root_token_disposition").value,
restore_drill_passed: document.getElementById("restore_drill_passed").checked, restore_drill_passed: document.getElementById("restore_drill_passed").checked,
approval_phrase: document.getElementById("approval_phrase").value, approval_phrase: document.getElementById("approval_phrase").value,

View File

@@ -257,6 +257,12 @@ the operator can still proceed deliberately on a tainted workpath.
in the bootstrap console. The initial config command can now be recorded as in the bootstrap console. The initial config command can now be recorded as
applied while root-token revocation/escrow remains a separate gate. applied while root-token revocation/escrow remains a separate gate.
**2026-05-25:** Added an Emergency lock-down runbook for sealing Railiance
OpenBao without placing tokens on the command line. Reordered the console into
Introduction & Actors, Subsystems & Scopes, Roles & Responsibilities,
Integration & Tests, Artefacts & Locations, Usecases & Runbooks, and
Terminology & Patterns.
**2026-05-24:** Stepped back from ad hoc secret rollout and added the **2026-05-24:** Stepped back from ad hoc secret rollout and added the
custodian age-key bootstrap model to the control surface. The UI now records custodian age-key bootstrap model to the control surface. The UI now records
the custodian public age recipient, a derived fingerprint, and a non-secret the custodian public age recipient, a derived fingerprint, and a non-secret