net-kingdom/docs/platform-root-custody.md
tegwick e7d326458c
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
docs(custody): every credential says what it is
Five custom_metadata fields on every path in operators/ and
platform/workloads/: description, owner, used_by, rotation, on_loss.
Non-secret, so anyone orienting themselves can read all of them without
being granted a value.

on_loss is the field the section exists for. When the LLDAP admin
predecessor was destroyed on 2026-08-27 the answer had to be
reconstructed from first principles under pressure; written down in
advance it is one line.

used_by is the second lesson: rotation without a consumer list is how
privacyIDEA's resolver was left holding a superseded bind credential for
four days while everything else had been reconciled.

Inventory with ops-mason/scripts/custody-inventory.py. It reports 21
paths today, 17 undescribed — the four described are the ones this
session touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 3377672@bnt-lap001
Assistant-Session: 15463ccf-238f-4e13-b163-93aa25c6d166
2026-08-28 11:42:51 +02:00

18 KiB
Raw Permalink Blame History

Platform Root Custody

Status: active bootstrap policy Date: 2026-05-24

Purpose

This document defines the bootstrap trust model for NetKingdom platform-root authority and the Railiance OpenBao bootstrap. It deliberately stores only identity, policy, and procedure. It must never contain passwords, OpenBao unseal keys, root tokens, recovery keys, one-time codes, private keys, or screenshots of secret output.

The design goal is practical security: early infrastructure can be assembled by a low-trust setup operator, then handed over to a dedicated king credential when the control plane is ready. After handover, bootstrap-era material is rotated, reset, or retired before live workloads depend on it.

Bootstrap Roles

Role Purpose Initial assignment
Setup operator Runs early MVP/prototype setup, receives notifications, operates Git/Gitea and day-to-day tooling tegwick / bernd.worsch@gmail.com / Gitea tegwick
King credential Dedicated platform-root break-glass and final custody credential, independent from day-to-day accounts To be created before OpenBao live bootstrap
Future escrow holders Later independent recovery control, preferably two-of-three custody To be named after the first safe bootstrap path exists

tegwick is the initial accountable setup operator and contact. That identity is useful for notifications, work tracking, Git access, and early operations, but it is not the desired long-term platform root of trust.

The king credential must be independent from ordinary Gitea and email access. Email may receive notifications, but secrets, reset links, root tokens, unseal shares, private keys, OTP seeds, and recovery codes must not travel through email.

King Credential

The king credential is the rare platform-root credential used to accept final custody after the bootstrap substrate exists. It should be represented as a dedicated identity such as platform-root or king, not as a personal day-to-day account.

Minimum first version:

  • stored in an offline or local password safe controlled by the human operator;
  • protected by a separate strong passphrase;
  • protected by an OTP factor or hardware-backed factor where the selected IAM implementation supports it; the QR code or setup key must come from that verifier, not from the local metadata console;
  • not used for normal Git, browsing, chat, or daily administration;
  • not recoverable through the day-to-day email account alone; and
  • documented only by non-secret metadata in Git/State Hub.

Preferred later version:

  • two-of-three independent custody or equivalent multisig-style approval;
  • at least one independent human or institutional escrow holder;
  • documented rotation and recovery drills; and
  • break-glass use logged for review without exposing secret values.

Custody Meaning

For this bootstrap, "master credential" means the offline recovery authority needed to recover or administer the platform before normal identity, authorization, and secret systems are fully online. It includes OpenBao unseal/recovery material and, only if retained, the initial OpenBao root token.

The root token is not the normal admin credential. It is one-time bootstrap or break-glass material. The preferred end state is:

  • OpenBao is initialized and unsealed.
  • Audit devices, mounts, auth methods, and policies are configured.
  • A non-root platform-admin operator path exists.
  • The initial root token is revoked, or is stored offline as sealed break-glass material under the king credential custody policy.
  • Routine access flows through NetKingdom IAM claims and scoped OpenBao policies.

Where The King Credential Lives

The king credential is an identity and custody record, not an OpenBao password. In the current lightweight NetKingdom stack, the practical placement is:

Part Current home Notes
User record LLDAP dedicated platform-root or king user, separate from tegwick
Password login Authelia over LLDAP day-to-day email is notification-only
TOTP / token enrollment privacyIDEA QR/setup key comes from privacyIDEA self-service
privacyIDEA administration pi-admin setup and repair account, not the king credential
IAM Profile / OIDC token KeyCape target issuer for normal platform identity claims
Secret custody and audit OpenBao initialized after custody approval; not the human identity provider

OpenBao enters the story after custody approval: it holds platform secrets, unseal/recovery material handling, audit, policies, and temporary or future admin auth methods. It should not be used as the place where the human king password or OTP seed lives.

LLDAP deliberately has no public registration flow. The first-user process is administrator-provisioned:

  1. Log in to https://lldap.coulomb.social as admin.
  2. Retrieve LLDAP_LDAP_USER_PASS from operator custody — operators/lldap/admin in OpenBao (see Operator Credential Custody). Until 2026-08-28 this step named a password-safe entry that did not exist; see that section for what that cost.
  3. Create the dedicated platform-root or king user.
  4. Add the user to net-kingdom-admins for the current lightweight path.
  5. Store the new account password in operator custody (operators/) or, for a bootstrap identity, in the offline packet — never only in a browser password manager.
  6. Use pi-admin in privacyIDEA to confirm that the LLDAP resolver can see the new user and that self-enrollment is allowed.
  7. Log in to privacyIDEA self-service as platform-root and enroll the TOTP token there. The QR/setup key belongs only in the authenticator and custody storage, never in this repo, State Hub, chat, or the local control surface.
  8. Verify the OIDC login path through a registered KeyCape client. KeyCape is an issuer, not a dashboard; the root URL may return 404 while discovery, health, authorize, token, and userinfo endpoints are healthy.

The local control surface records this as non-secret progress: account reference, group assignment confirmation, password-safe confirmation, and later login-path verification. It does not read or store the password, OTP seed, QR code, or recovery codes.

Do not use a successful platform-admin login as proof of platform-root custody unless the recorded king credential was intentionally changed to platform-admin. platform-admin is the later non-root operator path; it is useful, but it is not the same as proving the platform-root custody identity.

Operator Credential Custody

Decided 2026-08-28, after NK-WP-0033.

Every credential a human operator holds has exactly one recorded home. Before this was written, this document named a password-safe entry (net-kingdom/LLDAP/admin) that had never been created. The consequence was not theoretical: the LLDAP admin credential was rotated on 2026-08-23 with nowhere to put the new value, the outgoing value survived only in a browser password manager, and it was destroyed on 2026-08-27 when that entry was overwritten with the replacement. A full working session went into diagnosing a system whose predecessor credential no longer existed anywhere.

Two stores, and the line between them

operators/ in OpenBao — everything reachable once you are logged in. Service account passwords, provider PATs, the LLDAP admin bind, privacyIDEA pi-admin, machine identities like state-hub-svc. Auditable, backed up with the rest of the platform, reachable from any machine, and writable through warden desk --act paste_once_provision so the value never reaches a terminal or shell history.

Path convention: operators/<subsystem>/<account> — for example operators/lldap/admin, operators/privacyidea/pi-admin, operators/forgejo/state-hub-svc. Distinct from platform/workloads/…, which holds credentials that workloads consume; operators/ holds credentials that people use.

The offline packet — what you need when OpenBao is unreachable. This list is deliberately short, closed, and enumerated here rather than left to judgement:

  • platform-root's own password and TOTP recovery codes
  • OpenBao unseal shares and root token
  • the break-glass account password (sso-mfa/k8s/lldap/break-glass.sh)

Anything on that list in operators/ would be a circular dependency: you would need the store to reach the credential that opens the store. Anything not on that list belongs in operators/, not in the packet — the packet is not a general-purpose safe and does not scale.

The rule that closes the gap

Every rotation procedure must name, as a numbered step, where the outgoing and incoming values go. A rotation that changes a credential without saying what happens to custody will lose one of the two, and which one it loses is a matter of luck. NK-WP-0033-T04 rotated four credential classes and had no such step; that is the root cause of the session that followed, not any of the four software defects it also surfaced.

The outgoing value is retained until its successor is verified and the predecessor is proven rejected. Only then is it destroyed, and its destruction is recorded.

Every credential says what it is

A store of unlabelled values is the drawer of unmarked keys it was supposed to replace. Every path — in operators/ and in platform/workloads/ alike — carries five custom_metadata fields. They are not secret, so anyone orienting themselves can read all of them without being granted a single value.

Field Answers
description what this credential is, in a sentence, including what it is not — the distinction between an account's password and the token it issues has already cost a session
owner which repo or subsystem decides about it
used_by what breaks when it changes. Rotation without this list is how a resolver gets left holding a superseded value for four days
rotation the procedure, by document path — not a description of one
on_loss what you do when the value is gone: is it resettable, by whom, and what does the reset cost

on_loss is the field this whole section exists for. On 2026-08-27 the LLDAP admin predecessor was destroyed and the answer had to be reconstructed from first principles under pressure. Written down in advance it is one line.

Read the inventory with ops-mason/scripts/custody-inventory.py, which walks both mounts, prints these fields, and marks any path missing them. It reads metadata only and runs under ops-mason-build, so it is safe to hand to anyone.

As of 2026-08-28 that inventory reports 21 paths, 17 of them undescribed. The four that are described are the ones this session touched. The backlog is real and is not urgent in the way a broken login is urgent — but each of those seventeen is a credential nobody can identify without reading it.

Not yet built

The operators/ mount and its policy do not exist yet. Until they do, this section describes intent rather than practice, and the same failure remains available. Building it is a railiance-platform act, not an ops-mason one.

Trust Progression

The platform moves through explicit trust stages:

Stage Name Meaning
S0 MVP/prototype Day-to-day accounts and local operator access may have created state. Nothing here is assumed clean enough for live custody.
S1 Low-trust assembly Setup operator can deploy infrastructure, but does not become platform root. No live secrets are stored.
S2 King credential creation Dedicated king credential, second factor, and offline recovery storage are created through guided UX.
S3 OpenBao bootstrap OpenBao is initialized, configured, and moved to non-root admin paths.
S4 Cleanup and hardening Bootstrap passwords, database credentials, tokens, and access paths are reset or rotated; hosts and workloads are scanned and reviewed.
S5 Reopen under custody The platform becomes usable under king credential oversight, with day-to-day admins delegated scoped access.
S6 Multi-custodian upgrade Custody moves to two-of-three or equivalent independent recovery control.

This prevents early bootstrap convenience from silently becoming permanent platform sovereignty.

Secret disclosure by stage

Decided 2026-08-27, during NK-WP-0033. Diagnosis stalls when two people cannot establish whether they are discussing the same credential. What follows is what may be said aloud — in a terminal, a transcript, a chat message, or a ticket — about a secret's value, by stage.

Stage Permitted about a value
S0S4 A fingerprint: last three characters, optionally with length. Enough to confirm two parties hold the same credential and no more.
S5+ Same, but only where the value is already in an automated pipeline may a hash (sha256 | head -c 8) be used instead; a human comparing by eye still uses the last three.
Any Never the whole value, never a working prefix, never a screenshot of one.

Last three characters, not a hash — decided deliberately. A hash looks like the stricter choice and is the worse one in practice. Browsers and secret stores have a reveal button; none of them can show you a digest. To compare hashes you must first extract the plaintext into a shell, where it lands in history, in a process listing, or in a temporary file — so the "safer" convention forces a plaintext handling step that reading three characters off a screen avoids entirely. The rule exists to reduce exposure, and the hash form increases it.

Hashes remain the right form where the value is already flowing through a pipeline — verifying a KV write from a script, comparing two stores programmatically — because there the plaintext is in memory regardless and no new handling step is created.

This is a disclosure rule, not a handling rule. It says what may be spoken about a value; it does not license reading a value out of a store that a role is not otherwise entitled to read.

Required Bootstrap Use Cases

The guided bootstrap experience must cover at least:

  • create or import the king credential;
  • verify king credential second factor and recovery storage;
  • initialize OpenBao without exposing secret output to unsafe channels;
  • onboard a new user;
  • temporarily lock a user;
  • permanently lock and offboard a user;
  • review and rotate user credentials;
  • create a new fabric with its own admin;
  • transfer a fabric to a new admin;
  • perform break-glass access;
  • rotate or rekey platform-root material;
  • add later two-of-three custody; and
  • run cleanup checks before reopening the platform for live use.

Target IAM Claims

When key-cape or Keycloak provisions the first king/admin identity, it should receive a platform-root identity envelope similar to:

Claim or group Value
sub stable subject for platform-root or the selected king identity
email notification address only; no secret transfer
tenant platform
principal_type human or break_glass depending on IAM support
groups platform-root, platform-admin, netkingdom-admin, railiance-platform-admin
roles platform-root-custodian, openbao-admin, identity-admin
assurance MFA-backed for privileged actions

The setup operator tegwick can receive scoped admin roles for day-to-day operations later, but those roles must be delegated from the king custody model and must be revocable without losing recovery authority.

Tenant administration must remain separate from platform-root claims. Being an admin for tenant:coulomb or another tenant must not imply OpenBao root, NetKingdom identity-admin, flex-auth platform-policy, or Railiance platform-service authority.

OpenBao Bootstrap Sequence

The Railiance OpenBao runbook owns the live commands. NetKingdom owns the identity and custody semantics.

Before OpenBao initialization:

  1. Use the guided bootstrap UX or checklist to decide the current trust stage.
  2. Record tegwick as setup operator/contact, not as final root custodian.
  3. Create or import the dedicated king credential and verify its second factor.
  4. Choose whether this is temporary single-custodian king custody or preferred independent escrow.
  5. Prepare offline recovery bundle locations for that strategy.
  6. Prepare the OpenBao custody packet for that strategy, including share assignment rows, quorum plan, root-token disposition, and signoff line.
  7. Approve the selected custody strategy in the NetKingdom control surface.
  8. Run Railiance make openbao-status and make openbao-verify.

During initialization:

  1. Run the OpenBao init command only in the approved maintenance window.
  2. Route each unseal share directly to the king credential custody bundle or approved escrow holder.
  3. Use the initial root token only for audit, mounts, auth methods, policies, and creating the non-root admin path.
  4. Do not persist secret output in shell history, Git, State Hub, chat, email, tickets, screenshots, or issue trackers.

After initialization:

  1. Create and store a non-root platform-admin operator credential.
  2. Revoke the initial root token, or seal it offline as break-glass material under king custody.
  3. Reset or rotate bootstrap-era database credentials, admin passwords, service tokens, and access paths before live use.
  4. Run host/workload checks, vulnerability scans where available, and the OpenBao snapshot plus isolated restore drill.
  5. Log non-secret progress with the custody posture and verification outcome.

Completion Gates

The platform-root custody path is ready for live secrets only when:

  • tegwick is recorded as setup operator/contact, not root of trust;
  • the dedicated king credential exists and is second-factor protected;
  • OpenBao init/unseal ceremony is complete;
  • routine admin access no longer depends on the initial root token;
  • root token disposition is recorded without storing the token value;
  • bootstrap-era credentials and databases have been reset or rotated as needed;
  • backup, audit, restore, and scan evidence exists;
  • NetKingdom IAM claims are ready to become the normal human admin path; and
  • two-of-three custody remains an explicit, low-friction upgrade path.