# 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//` — 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 | | --- | --- | | S0–S4 | 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.