generated from coulomb/repo-seed
NET-WP-0017: complete T06 dry-run + T07 review/retire (onboarded+locked+offboarded t06-dryrun test user via T05 flow + verifs; evidence+validate pass; archived superseded 0015/16 + old NK-0003/4/5 bootstrap plans per T07; set platform_reopened; updated T06/T07 notes + frontmatter finished)
This commit is contained in:
@@ -0,0 +1,514 @@
|
||||
---
|
||||
id: NET-WP-0015
|
||||
type: workplan
|
||||
title: "King Credential And OpenBao Identity Bootstrap"
|
||||
domain: netkingdom
|
||||
repo: net-kingdom
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-05-24"
|
||||
updated: "2026-06-01"
|
||||
depends_on:
|
||||
- NK-WP-0006
|
||||
- NK-WP-0012
|
||||
state_hub_workstream_id: "6b9c25e4-1008-429a-8de6-54361872c0dd"
|
||||
---
|
||||
|
||||
# NET-WP-0015 - King Credential And OpenBao Identity Bootstrap
|
||||
|
||||
## Goal
|
||||
|
||||
Define and execute the first safe bridge between low-trust setup operations, a
|
||||
dedicated king credential, NetKingdom identity, and Railiance OpenBao
|
||||
bootstrap.
|
||||
|
||||
The revised decision is that `tegwick` / `bernd.worsch@gmail.com` is the
|
||||
initial accountable setup operator and notification contact, not the long-term
|
||||
platform root of trust. The actual platform-root authority should move to a
|
||||
separate king credential before OpenBao becomes live secret custody.
|
||||
|
||||
## Context
|
||||
|
||||
Railiance owns OpenBao deployment and operations. NetKingdom owns the identity,
|
||||
custody, and security semantics that say who can administer the platform and
|
||||
how that authority transitions from bootstrap material into normal IAM claims.
|
||||
|
||||
The platform is still in MVP/prototype bootstrap. That means early databases,
|
||||
admin accounts, tokens, and access paths must be treated as potentially
|
||||
contaminated by convenience. The platform should be assembled in low-trust
|
||||
mode, then handed over to the king credential, reset/rotated, checked, and
|
||||
reopened under explicit custody.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- record the setup operator/contact identity;
|
||||
- define the separate king credential target;
|
||||
- define the temporary single-operator king custody exception;
|
||||
- specify target NetKingdom IAM claims for the first admin identity;
|
||||
- coordinate the OpenBao initialization prerequisites with Railiance;
|
||||
- define the transition from OpenBao root token to scoped admin access; and
|
||||
- add follow-up gates for independent escrow, OIDC/JWT admin auth,
|
||||
reset/rotation, scan checks, and restore verification.
|
||||
|
||||
Out of scope:
|
||||
|
||||
- storing any secret material in this repo;
|
||||
- running `bao operator init` from an unattended agent session;
|
||||
- deploying key-cape, Keycloak, privacyIDEA, or OpenBao itself; and
|
||||
- granting tenant administrators platform-root authority.
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 - Record Setup Operator And King Credential Model
|
||||
|
||||
```task
|
||||
id: NET-WP-0015-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "60659e25-fed1-478e-b8a3-4bc7b2f3846b"
|
||||
```
|
||||
|
||||
Record `tegwick` / `bernd.worsch@gmail.com` / Gitea `tegwick` as the initial
|
||||
setup operator and contact. Define the separate king credential as the actual
|
||||
platform-root target.
|
||||
|
||||
**2026-05-24:** Added `docs/platform-root-custody.md` and updated
|
||||
`docs/platform-identity-security-architecture.md` plus `SCOPE.md`.
|
||||
|
||||
**2026-05-24:** Revised the custody model: `tegwick` is no longer modeled as
|
||||
the platform root of trust. The day-to-day account can assemble and observe the
|
||||
platform, while a dedicated king credential receives final custody after the
|
||||
guided bootstrap path is ready.
|
||||
|
||||
### T02 - Define King Credential Kit
|
||||
|
||||
```task
|
||||
id: NET-WP-0015-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "1a1c45a2-be66-4667-89f8-581f4fe9970b"
|
||||
```
|
||||
|
||||
Define the first king credential kit: dedicated identity name, local/offline
|
||||
password-safe storage, second factor, recovery-code handling, no email secret
|
||||
transfer, no day-to-day browsing/Git use, and operator instructions clear
|
||||
enough for a non-expert.
|
||||
|
||||
**2026-05-24:** Defined the v1 kit in
|
||||
`docs/security-bootstrap-king-credential-kit.md`: label `platform-root`, setup
|
||||
operator/contact `tegwick`, notification-only email
|
||||
`bernd.worsch@gmail.com`, local password safe plus offline custody packet,
|
||||
TOTP/WebAuthn/hardware-token second factor, no day-to-day use, and no email or
|
||||
Git secret transfer. Added
|
||||
`examples/security-bootstrap/king-credential-metadata.example.json` plus
|
||||
console validation for non-secret kit metadata. Custody-mode approval remains
|
||||
blocked under T03.
|
||||
|
||||
### T03 - Approve King Custody Mode
|
||||
|
||||
```task
|
||||
id: NET-WP-0015-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "56a6266a-4acd-41e6-a395-85e90a5c35c6"
|
||||
```
|
||||
|
||||
Choose either the preferred independent two-of-three king custody model or an
|
||||
explicit temporary single-operator king credential exception for pre-production
|
||||
bootstrap. Do not run OpenBao initialization until this choice is recorded.
|
||||
|
||||
**2026-05-24:** Added local approval surfaces for this human gate:
|
||||
`approve-custody-mode` for the CLI and `web-ui` for the localhost console.
|
||||
Both write non-secret metadata only and keep live OpenBao initialization as a
|
||||
separate attended ceremony. Current recommended approval mode is
|
||||
`temporary-single-king`; `two-of-three-planned` records the target state but
|
||||
does not unblock live init.
|
||||
|
||||
**2026-05-24:** Tightened MFA handling after review: a TOTP QR code or setup
|
||||
key must come from the authority that will verify login, not from the local
|
||||
metadata console. Custody approval now requires explicit non-secret
|
||||
confirmation that the factor was enrolled with its real verifier.
|
||||
|
||||
**2026-05-24:** Clarified credential placement in the UI and custody docs:
|
||||
the dedicated king account currently belongs in the lightweight NetKingdom
|
||||
identity path (LLDAP user, Authelia login, privacyIDEA MFA, KeyCape OIDC).
|
||||
OpenBao is the secrets/audit/admin-policy custody service after the ceremony,
|
||||
not the place where the human password or OTP seed lives.
|
||||
|
||||
**2026-05-24:** Expanded the local UI toward a NetKingdom control surface:
|
||||
the bootstrap flow now has action buttons for LLDAP, privacyIDEA, and KeyCape,
|
||||
plus non-secret progress saving for account creation, MFA enrollment, OIDC
|
||||
verification, and custody approval.
|
||||
|
||||
**2026-05-24:** Clarified the LLDAP first-user path in the UI and docs:
|
||||
LLDAP has no registration flow; the operator logs in as bootstrap `admin`
|
||||
using `LLDAP_LDAP_USER_PASS` from `net-kingdom/LLDAP/admin`, then creates the
|
||||
dedicated `platform-root` or `king` account and assigns the current lightweight
|
||||
admin group.
|
||||
|
||||
**2026-05-24:** Added explicit non-secret UI confirmations for the account
|
||||
having been created, assigned to `net-kingdom-admins`, stored in the password
|
||||
safe/offline packet, and later verified through the login path. Automated
|
||||
LLDAP detection is deferred because it would require authenticated access to
|
||||
LLDAP and should be built as an audited integration.
|
||||
|
||||
**2026-05-24:** Improved the KeyCape login-check path: the local bootstrap UI
|
||||
now acts as the `demo-app` OIDC callback, exposes `/oidc/start` and
|
||||
`/oidc/callback`, and adds hover-help text to the external action buttons.
|
||||
The live KeyCape rollout still needs the updated `keycape-config` Secret
|
||||
applied from decrypted `sso-mfa/bootstrap/secrets/` inputs. If the browser
|
||||
flow reaches Authelia but never presents an OTP challenge, KeyCape needs a
|
||||
browser MFA prompt surface before this gate can be marked verified.
|
||||
|
||||
**2026-05-24:** Filed `KEY-WP-0003` in the KeyCape repo for the current OIDC
|
||||
verification blocker. The immediate error
|
||||
`redirect_uri does not match any registered URI` means the local bootstrap
|
||||
callback is not yet registered in live KeyCape. The follow-up KeyCape work also
|
||||
covers the browser OTP challenge needed after Authelia password login.
|
||||
|
||||
**2026-05-24:** Implemented `KEY-WP-0003` in source. KeyCape now supports a
|
||||
dedicated `netkingdom-bootstrap-console` client, split browser/server Authelia
|
||||
URLs, and a browser OTP challenge before issuing the final OIDC code. The local
|
||||
control surface now uses that dedicated client. Live verification remains
|
||||
pending until the updated KeyCape image and regenerated `keycape-config` Secret
|
||||
are rolled out.
|
||||
|
||||
**2026-05-24:** Rolled the fix to the public Railiance SSO host
|
||||
(`kc.coulomb.social`, currently resolving to `railiance01`). The live
|
||||
`keycape-config` Secret was patched without printing or rotating secret values,
|
||||
the `main-1d68639` KeyCape image was direct-imported into k3s, and the
|
||||
deployment was set to `IfNotPresent`. Public `/authorize` now accepts
|
||||
`netkingdom-bootstrap-console` and redirects to
|
||||
`https://auth.coulomb.social/...`. Follow-up: clean up the Gitea HTTP registry
|
||||
push/pull path so direct image import is no longer needed.
|
||||
|
||||
**2026-05-24:** Fixed the next live login failure before OTP: Authelia rejected
|
||||
KeyCape's token exchange because the upstream `keycape` client only permits
|
||||
`client_secret_basic`, while KeyCape was sending `client_secret_post`. KeyCape
|
||||
commit `56d279a` now uses HTTP Basic auth for the upstream token exchange, the
|
||||
image `main-56d279a` was direct-imported into Railiance k3s, and the live
|
||||
deployment runs that tag.
|
||||
|
||||
**2026-05-24:** Fixed the follow-up `mfa check error`. Live privacyIDEA
|
||||
validation succeeds in the `coulomb` realm, while KeyCape had been configured
|
||||
for `netkingdom` and was also trying to pre-list tokens with an expired or
|
||||
invalid privacyIDEA admin JWT. KeyCape commit `937cb39` adds bootstrap mode
|
||||
`privacyidea.requireForAll`, which requires OTP for every authenticated user
|
||||
without depending on token-list admin credentials. The live `keycape-config`
|
||||
now uses `realm: coulomb` and `requireForAll: true`, and Railiance runs image
|
||||
`main-937cb39`.
|
||||
|
||||
**2026-05-25:** Fixed the subsequent token-exchange `user not found` error.
|
||||
Live LLDAP stores users under `ou=people`, while KeyCape's default lookup base
|
||||
was `ou=users`. KeyCape commit `06d20c3` makes the LLDAP OU settings explicit
|
||||
in YAML, live `keycape-config` now sets `userOU: ou=people` and
|
||||
`groupOU: ou=groups`, and Railiance runs image `main-06d20c3`.
|
||||
|
||||
**2026-05-25:** End-to-end OIDC login verification succeeded for
|
||||
`platform-root`. The local bootstrap-console callback exchanged the code and
|
||||
showed issuer `https://kc.coulomb.social`, audience
|
||||
`netkingdom-bootstrap-console`, subject
|
||||
`uid=platform-root,ou=people,dc=netkingdom,dc=local`, email
|
||||
`bernd.worsch@gmail.com`, and group `net-kingdom-admins`. Local non-secret
|
||||
bootstrap progress now records both MFA enrollment confirmation and OIDC login
|
||||
verification.
|
||||
|
||||
**2026-05-25:** Reworked the bootstrap-console flow after operator review. The
|
||||
UI now follows the use case top to bottom, hides hardware-token storage unless
|
||||
the selected policy uses hardware tokens, specifies the exact recovery material
|
||||
contents, distinguishes recovery material from the OpenBao custody packet, and
|
||||
turns "no secret capture" into an automatic control-surface boundary gate
|
||||
rather than a user checkbox.
|
||||
|
||||
**2026-05-25:** Corrected the custody/OpenBao ordering in the console:
|
||||
strategy selection now comes before recovery/packet preparation, the custody
|
||||
packet is prepared for the selected strategy before approval, and the OpenBao
|
||||
panel now explains when to run Railiance preflight, init/unseal,
|
||||
post-unseal configuration, root-token disposition, and restore proof. The
|
||||
console still refuses to capture root tokens or unseal shares.
|
||||
|
||||
**2026-05-25:** Restructured the bootstrap UI around the operator mental model:
|
||||
Roles & Responsibilities, Subsystems & Scope, Integration & Tests, and
|
||||
Artefacts & Locations. Role, subsystem, integration, and artefact rows now use
|
||||
the same `name`, `description`, `subsystem`, `responsibility`, `location`, and
|
||||
`state` fields, and console commands are shown as copyable command blocks.
|
||||
|
||||
**2026-05-25:** Refined the new model after operator review: role chips now sit
|
||||
under subsystem labels to keep artefact rows narrow, responsibility editing is
|
||||
inside a dirty-state Save/Cancel foldout, future quorum contact uses the same
|
||||
effective-value prefill as the role display, and command cards now derive
|
||||
`blocked`, `todo`, `redo`, or `done` status from bootstrap metadata.
|
||||
|
||||
**2026-05-25:** Added a Usecases & Runbooks section for trial-output exposure
|
||||
and key-material compromise. The UI now records non-secret compromise response
|
||||
state, separates "init output produced" from "initialized and unsealed", and
|
||||
adds guided command cards for unseal and OpenBao `rotate-keys` replacement
|
||||
share generation.
|
||||
|
||||
**2026-05-25:** Changed compromised/trial-exposed OpenBao material from a hard
|
||||
block into an explicit taint model. Affected artefacts and downstream command
|
||||
cards are shown with a light red background and retain the source reference, but
|
||||
the operator can still proceed deliberately on a tainted workpath.
|
||||
|
||||
**2026-05-25:** Split OpenBao initial configuration from root-token disposition
|
||||
in the bootstrap console. The initial config command can now be recorded as
|
||||
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-25:** Added Restore drill runbook action cards so the existing
|
||||
confirmation checkbox has a concrete path: prepare a restricted workspace,
|
||||
create/copy/hash an OpenBao Raft snapshot, encrypt it to the custodian age
|
||||
recipient, complete an isolated restore proof, rerun post-unseal verification,
|
||||
and record only non-secret completion evidence.
|
||||
|
||||
**2026-05-25:** Refined the action/runbook model in the control surface:
|
||||
Integration & Tests now carries stateful runbook tasks and gates, while
|
||||
Usecases & Runbooks contains status-less action cards and neutral runbook
|
||||
templates. Added copyable OpenBao inspection actions for `bao audit list`,
|
||||
`bao secrets list`, and `bao auth list` with local hidden token prompts,
|
||||
removed duplicate OpenBao status/unseal cards from the stateful Integration
|
||||
command list, and restored Artefacts & Locations above Usecases & Runbooks in
|
||||
the workflow.
|
||||
|
||||
**2026-05-25:** Added a five-stage visual stage rail and Final Handover
|
||||
section to close the gap between OpenBao bootstrap and the final operating
|
||||
state. The stage model now moves from S3 to S4 after OpenBao initial
|
||||
configuration, root-token disposition, and restore drill are complete, then to
|
||||
S5 only when the platform is explicitly reopened under custody.
|
||||
|
||||
**2026-05-25:** Corrected the OpenBao rotate-keys action cards after the
|
||||
operator hit `permission denied` on rotation init. The rotation commands now
|
||||
open an interactive pod TTY, prompt there for a root/sudo-capable OpenBao
|
||||
token, keep the token out of the local command line, and then run rotate init,
|
||||
share submission, or cancel.
|
||||
|
||||
**2026-05-26:** Added an explicit rotation-status action and clarified the
|
||||
rotation flow after the operator successfully started rotate-keys and then hit
|
||||
`rotation already in progress` by rerunning init. The UI now says init is a
|
||||
run-once step and that the next step is checking status or submitting existing
|
||||
shares with the nonce until quorum completes.
|
||||
|
||||
**2026-05-26:** Added a Usecases action card for creating the temporary
|
||||
Railiance OpenBao `platform-admin` token with
|
||||
`bao token create -policy=platform-admin -period=24h -orphan`. The command
|
||||
prompts for the bootstrap/root token without placing it on the command line
|
||||
and reminds the operator to store the emitted token through the approved secret
|
||||
path.
|
||||
|
||||
**2026-05-26:** Promoted the KeyCape-to-OpenBao admin path into its own stage
|
||||
before cleanup and hardening. The control surface now has S4 Admin Identity
|
||||
Integration with gates for the dedicated KeyCape OpenBao client, OpenBao
|
||||
OIDC/JWT auth configuration, and MFA-backed OpenBao admin login verification;
|
||||
cleanup and reopening move to S5/S6.
|
||||
|
||||
**2026-05-26:** Refined the OpenBao trial-exposure taint model so direct
|
||||
unseal-share taint clears after confirmed unseal-key rotation, and direct
|
||||
initial-root-token taint clears after the exposed OpenBao root token is
|
||||
revoked. Downstream work remains visibly tainted until derived access paths
|
||||
are reviewed and the compromise response is explicitly recorded complete.
|
||||
|
||||
**2026-05-26:** Split Admin Identity Integration into development-owned
|
||||
configuration and operator-owned integration work. The `openbao-admin` KeyCape
|
||||
client is now code-defined in `sso-mfa/k8s/keycape/create-secrets.sh`, while
|
||||
the UI action cards only ask the operator to apply live KeyCape config,
|
||||
configure OpenBao with a protected token prompt, and verify MFA-backed login.
|
||||
|
||||
**2026-05-26:** Hardened the KeyCape OpenBao client deployment action after the
|
||||
operator hit a non-executable `create-secrets.sh`. The action card now runs the
|
||||
script through `bash`, uses absolute repo paths, and wraps the sequence in a
|
||||
fail-fast heredoc so a failed config generation does not continue into a
|
||||
KeyCape restart or verification.
|
||||
|
||||
**2026-05-26:** Removed the KeyCape OpenBao client action's dependency on
|
||||
decrypted bootstrap secrets after the operator correctly hit the absent
|
||||
`sso-mfa/bootstrap/secrets/` directory. Added a focused live Secret patcher and
|
||||
verifier for the `openbao-admin` client so this non-secret client addition can
|
||||
be applied without decrypting the full bootstrap secret bundle.
|
||||
|
||||
**2026-05-26:** Fixed the focused KeyCape OpenBao verifier after the live
|
||||
KeyCape image lacked `wget`. The verifier now checks the live Secret and then
|
||||
uses a short local `kubectl port-forward` plus Python HTTP request for OIDC
|
||||
discovery, avoiding assumptions about tools installed inside the KeyCape
|
||||
container.
|
||||
|
||||
**2026-05-26:** Fixed the OpenBao OIDC auth setup after OpenBao rejected an
|
||||
empty `oidc_client_secret` even though the current KeyCape `openbao-admin`
|
||||
client is public PKCE. The UI now points to a short helper script instead of a
|
||||
long nested shell/JSON command, and the helper writes an explicit non-secret
|
||||
compatibility value until KeyCape supports confidential downstream clients.
|
||||
|
||||
**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
|
||||
the custodian public age recipient, a derived fingerprint, and a non-secret
|
||||
private-key custody reference while refusing to treat the private key as normal
|
||||
metadata. It also detects encrypted bootstrap bundle presence and plaintext
|
||||
`sso-mfa/bootstrap/secrets/` exposure. This is the intended foundation for
|
||||
trial-mode, custody-mode, unlock/apply, and later OpenBao handover flows.
|
||||
|
||||
**2026-05-26:** Closed this custody-approval task after review against the
|
||||
live bootstrap metadata: `platform-root` is recorded as the king credential,
|
||||
MFA and KeyCape OIDC login are verified, and `temporary-single-king` custody is
|
||||
explicitly approved for the pre-production OpenBao bootstrap. Remaining
|
||||
hardening and user-onboarding readiness work is tracked in `NET-WP-0017`.
|
||||
|
||||
### T04 - Complete Railiance OpenBao Bootstrap Ceremony
|
||||
|
||||
```task
|
||||
id: NET-WP-0015-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "2102366e-064b-4071-8b6a-574d9d37d109"
|
||||
```
|
||||
|
||||
Coordinate with `RAIL-PL-WP-0002-T03` to initialize and unseal OpenBao under
|
||||
the king credential model, enable audit and the first mounts/policies, create a
|
||||
non-root `platform-admin` access path, and revoke or offline-escrow the initial
|
||||
root token.
|
||||
|
||||
**2026-05-26:** Closed the bootstrap ceremony portion after live verification:
|
||||
Railiance OpenBao is initialized, unsealed, and post-unseal verified; initial
|
||||
configuration was applied; the initial OpenBao root token is recorded as
|
||||
revoked; trial unseal shares were rotated; and restore-drill confirmation is
|
||||
recorded in the bootstrap metadata. Declarative audit/durable audit shipping
|
||||
and routine OIDC admin access remain follow-up readiness gates under
|
||||
`NET-WP-0017` and `RAIL-PL-WP-0002`.
|
||||
|
||||
### T05 - Provision First NetKingdom Admin Identity
|
||||
|
||||
```task
|
||||
id: NET-WP-0015-T05
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "d2a81d7b-9964-4bd5-9b8c-ef1324e02cd4"
|
||||
```
|
||||
|
||||
Provision the first king/admin identity in the selected NetKingdom IAM
|
||||
implementation. The target claims are `tenant=platform`,
|
||||
`principal_type=human` or `break_glass`, MFA-backed assurance, and groups/roles
|
||||
for `platform-root`, `platform-admin`, `netkingdom-admin`, and
|
||||
`railiance-platform-admin`. `tegwick` may receive delegated day-to-day admin
|
||||
roles later, but must be revocable without losing root custody.
|
||||
|
||||
**2026-05-26:** Closed for the bootstrap identity scope: the dedicated
|
||||
`platform-root` user is recorded as created, assigned to
|
||||
`net-kingdom-admins`, stored outside this repo, enrolled for MFA, and verified
|
||||
through KeyCape OIDC. Richer IAM-profile claims for ordinary user onboarding
|
||||
remain part of the user-onboarding readiness work in `NET-WP-0017`.
|
||||
|
||||
### T06 - Bind OpenBao Admin Auth To NetKingdom IAM
|
||||
|
||||
```task
|
||||
id: NET-WP-0015-T06
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "ef97f3cb-9792-4b9d-bd2b-8871d368a50f"
|
||||
```
|
||||
|
||||
Replace temporary operator tokens with NetKingdom IAM-backed OpenBao admin
|
||||
auth when the issuer and claim mapping are ready. The OpenBao root token must
|
||||
not be the normal admin path.
|
||||
|
||||
**2026-05-26:** The KeyCape `openbao-admin` client was code-defined, patched
|
||||
into the live `keycape-config` Secret, rolled out, and verified without
|
||||
requiring decrypted bootstrap secrets. At that point, OpenBao `auth/keycape`
|
||||
still needed the fixed helper command and the MFA-backed
|
||||
`bao login -method=oidc -path=keycape role=platform-admin` path still needed
|
||||
verification.
|
||||
|
||||
**2026-06-01:** Added a guided bootstrap runbook action for the live
|
||||
privacyIDEA state-loss case encountered during OpenBao OIDC login testing. The
|
||||
new action recreates the `coulomb` realm, `lldap-coulomb` resolver,
|
||||
self-enrollment policy, and phase-one passthrough policy by prompting for
|
||||
`pi-admin` and LLDAP bind/admin passwords, writing them only to temporary
|
||||
files through `repair-realm-live.sh`, and running `bootstrap-realm.sh` plus
|
||||
`verify-t06.sh`. TOTP enrollment/re-enrollment and the final MFA-backed
|
||||
OpenBao login verification remain operator steps.
|
||||
|
||||
**2026-06-01:** Closed after the `platform-root` MFA-backed OpenBao OIDC login
|
||||
completed through KeyCape and the resulting token lookup showed
|
||||
`platform-admin` in both token policy fields. The remaining OpenBao hardening,
|
||||
audit, escrow, reset/rotation, and reopening gates continue under T07/T08 and
|
||||
`NET-WP-0017`.
|
||||
|
||||
**2026-06-01:** Added OpenBao token revocation to the guided
|
||||
Usecases & Runbooks section. The UI now includes a self-revoke card for the
|
||||
current pod token-helper token and an accessor-based revocation card for
|
||||
disclosed tokens, both keeping OpenBao token values off the local command line.
|
||||
|
||||
### T07 - Verify Recovery, Audit, And Rotation
|
||||
|
||||
```task
|
||||
id: NET-WP-0015-T07
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "aa40cbb4-36d3-405d-b59d-0c21ae8c9539"
|
||||
```
|
||||
|
||||
Confirm snapshot/restore drill, durable audit-log handling, root-token
|
||||
disposition, unseal/recovery rotation expectations, and the follow-up owner
|
||||
for adding at least one additional human escrow holder.
|
||||
|
||||
**2026-05-26:** Root-token disposition, unseal-key rotation, post-unseal
|
||||
verification, and restore-drill confirmation are recorded. This task remains
|
||||
open for declarative audit configuration/durable audit shipping, residual
|
||||
taint-response closeout, and the next independent escrow holder.
|
||||
|
||||
**2026-06-01:** Closed for the bootstrap handoff scope. The bootstrap plan has
|
||||
confirmed the available recovery/audit/rotation evidence and, more
|
||||
importantly, now has explicit production-readiness follow-up gates:
|
||||
`NET-WP-0017-T02` owns declarative/durable audit, restore evidence,
|
||||
emergency seal/unseal drill evidence, and the next independent escrow holder;
|
||||
`NET-WP-0017-T03` owns residual taint closeout. These items are no longer
|
||||
tracked as unfinished bootstrap ceremony work.
|
||||
|
||||
### T08 - Reset, Rotate, And Reopen Under King Oversight
|
||||
|
||||
```task
|
||||
id: NET-WP-0015-T08
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "e6a60dca-547b-4493-a36c-f6b668d1bf52"
|
||||
```
|
||||
|
||||
After the king credential accepts custody, reset or rotate bootstrap-era
|
||||
database credentials, admin passwords, service tokens, OpenBao tokens, and
|
||||
temporary access paths. Run host/workload checks and reopen the platform only
|
||||
after the new custody state is verified.
|
||||
|
||||
**2026-06-01:** Closed as a bootstrap-plan handoff rather than as a claim that
|
||||
all production cleanup is complete. `NET-WP-0017-T03` owns retirement of
|
||||
bootstrap admin paths and residual taint response, `NET-WP-0017-T04` owns
|
||||
bootstrap-era credential rotation/reset plus host/workload checks, and
|
||||
`NET-WP-0017-T07` owns final review and retirement/archive of superseded
|
||||
bootstrap workplans. `NET-WP-0018` will turn those gates into a smoother
|
||||
bootstrap guide, control-surface automation, validations, and rebuild-risk
|
||||
assessment.
|
||||
|
||||
## Closeout
|
||||
|
||||
**2026-06-01:** `NET-WP-0015` is finished. The first safe bridge is in place:
|
||||
the dedicated `platform-root` identity exists outside day-to-day operator use,
|
||||
custody mode is recorded, OpenBao was initialized and configured under the
|
||||
bootstrap ceremony, the initial root token is not the normal admin path, and
|
||||
routine OpenBao administration now works through NetKingdom/KeyCape OIDC with
|
||||
MFA and the `platform-admin` policy. Remaining production-readiness work is
|
||||
explicitly tracked in `NET-WP-0017`; rebuild automation and validation
|
||||
improvements are tracked in `NET-WP-0018`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- The setup operator and king credential model are recorded without secret
|
||||
values.
|
||||
- The custody mode is explicit before OpenBao initialization.
|
||||
- OpenBao root-token use is limited to bootstrap or break-glass handling.
|
||||
- Routine admin access has a non-root path and a target NetKingdom IAM path.
|
||||
- Production readiness has a clear gate for independent escrow, audit, restore,
|
||||
reset/rotation, and reopening under king oversight.
|
||||
@@ -0,0 +1,202 @@
|
||||
---
|
||||
id: NET-WP-0016
|
||||
type: workplan
|
||||
title: "Guided Security Bootstrap Experience"
|
||||
domain: netkingdom
|
||||
repo: net-kingdom
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-05-24"
|
||||
updated: "2026-05-24"
|
||||
depends_on:
|
||||
- NET-WP-0015
|
||||
- NK-WP-0012
|
||||
state_hub_workstream_id: "16069174-6698-4855-ad9e-5092c8571f38"
|
||||
---
|
||||
|
||||
# NET-WP-0016 - Guided Security Bootstrap Experience
|
||||
|
||||
## Goal
|
||||
|
||||
Create the operator-facing bootstrap experience that makes NetKingdom and
|
||||
OpenBao security setup understandable, repeatable, and safe for non-experts.
|
||||
|
||||
The platform should be possible to assemble with a low-trust setup operator,
|
||||
then hand over to a dedicated king credential, reset and harden the bootstrap
|
||||
state, and reopen under explicit custody.
|
||||
|
||||
## Context
|
||||
|
||||
Railiance and NetKingdom have reached a point where raw runbooks are not enough.
|
||||
The infrastructure is still early and evolving, and the human operator does not
|
||||
need to be an OpenBao/Keycloak/flex-auth expert to take the next safe step.
|
||||
|
||||
Good security here should feel like guided operations: visible trust stage,
|
||||
clear blocked actions, plain-language explanations, and no accidental secret
|
||||
exposure.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- define bootstrap use cases for king credential setup, user lifecycle,
|
||||
OpenBao bootstrap, fabric setup, break-glass, and multi-custodian upgrade;
|
||||
- design the first local operator console/checklist flow;
|
||||
- define safety gates for live OpenBao initialization;
|
||||
- define non-secret status records and audit/progress events;
|
||||
- define where the UI reads status from NetKingdom, Railiance, and State Hub;
|
||||
and
|
||||
- implement a first minimal CLI or local UI if the design stabilizes.
|
||||
|
||||
Out of scope:
|
||||
|
||||
- storing or displaying secret values;
|
||||
- implementing the full web UI before the workflow is validated;
|
||||
- replacing OpenBao, key-cape, Keycloak, or flex-auth administrative UIs;
|
||||
- unattended OpenBao initialization; and
|
||||
- sending root material or recovery secrets by email.
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 - Define Bootstrap Use Cases
|
||||
|
||||
```task
|
||||
id: NET-WP-0016-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "67af8a29-7ca1-4a9d-be3e-bdc48dd2d1fd"
|
||||
```
|
||||
|
||||
Document the canonical bootstrap use cases and trust stages.
|
||||
|
||||
**2026-05-24:** Added `docs/security-bootstrap-use-cases.md` covering king
|
||||
credential setup, onboarding, temporary lockout, permanent lockout/offboarding,
|
||||
credential review/rotation, new fabric admin setup, OpenBao bootstrap, custody
|
||||
handover, and later multi-custodian upgrade.
|
||||
|
||||
### T02 - Design The First Operator Journey
|
||||
|
||||
```task
|
||||
id: NET-WP-0016-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "662e439b-5fba-4e17-bc62-0ace97ba8788"
|
||||
```
|
||||
|
||||
Design the first command-driven or local-web operator journey: trust stage,
|
||||
next safe action, blocked gates, preflight checks, custody packet template, and
|
||||
clear plain-language instructions.
|
||||
|
||||
**2026-05-24:** Added `docs/security-bootstrap-operator-journey.md`. The first
|
||||
journey uses a quiet `whynot-design` control surface: trust stage, one next
|
||||
safe action, blocked gates, evidence rows, and a refusal boundary around live
|
||||
OpenBao initialization.
|
||||
|
||||
### T03 - Define King Credential Kit Output
|
||||
|
||||
```task
|
||||
id: NET-WP-0016-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "98aba75f-a7c1-4486-be7f-e8d1148d5303"
|
||||
```
|
||||
|
||||
Define the non-secret artifacts the bootstrap experience can generate for the
|
||||
king credential: checklist, custody packet template, OTP setup instructions,
|
||||
password-safe guidance, and verification prompts.
|
||||
|
||||
**2026-05-24:** Added `docs/security-bootstrap-king-credential-kit.md`.
|
||||
|
||||
### T04 - Define User Lifecycle Flows
|
||||
|
||||
```task
|
||||
id: NET-WP-0016-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "44766b45-21b8-45cd-8c0a-0ca8281ae8e9"
|
||||
```
|
||||
|
||||
Define guided flows for onboarding, temporary lockout, permanent lockout,
|
||||
offboarding, credential review, credential rotation, and delegated fabric admin
|
||||
setup.
|
||||
|
||||
**2026-05-24:** Added `docs/security-bootstrap-user-lifecycle.md`.
|
||||
|
||||
### T05 - Define OpenBao Ceremony UX
|
||||
|
||||
```task
|
||||
id: NET-WP-0016-T05
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "53f55c99-8403-4b58-9ed4-b03e68c1ef3c"
|
||||
```
|
||||
|
||||
Translate the Railiance OpenBao ceremony into a guided sequence that can show
|
||||
status, block unsafe live init, guide offline custody, and record non-secret
|
||||
completion evidence.
|
||||
|
||||
**2026-05-24:** Added `docs/security-bootstrap-openbao-ceremony-ux.md`.
|
||||
|
||||
### T06 - Prototype Local Bootstrap Console
|
||||
|
||||
```task
|
||||
id: NET-WP-0016-T06
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "ef1c8ee4-250c-479a-b0fb-0b5cf4249bd9"
|
||||
```
|
||||
|
||||
Implement the first minimal local operator console or CLI once the journey is
|
||||
clear. It should read status, print checklists, run safe preflight commands,
|
||||
and refuse live bootstrap when gates are missing.
|
||||
|
||||
**2026-05-24:** Added
|
||||
`tools/security-bootstrap-console/security_bootstrap_console.py`, a read-only
|
||||
local console with status, king-kit, custody-packet, handover-checklist,
|
||||
metadata-template, and OpenBao preflight commands. Added Make targets for the
|
||||
safe entry points. The console refuses live OpenBao init.
|
||||
|
||||
### T07 - Define Handover And Cleanup Gates
|
||||
|
||||
```task
|
||||
id: NET-WP-0016-T07
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "46c7e3dc-e824-46ef-833d-9a83189735e0"
|
||||
```
|
||||
|
||||
Define the post-king handover cleanup flow: reset databases, rotate tokens,
|
||||
review admin accounts, run scan/check steps, verify backups, and mark the
|
||||
platform reopened under king oversight.
|
||||
|
||||
**2026-05-24:** Added `docs/security-bootstrap-handover-cleanup.md`.
|
||||
|
||||
### T08 - Review Related Workplans On Closeout
|
||||
|
||||
```task
|
||||
id: NET-WP-0016-T08
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "7665f6ac-6b0e-4a09-8a9b-9d2150310114"
|
||||
```
|
||||
|
||||
When this workplan closes, review related NetKingdom and Railiance security
|
||||
workplans to update stale bootstrap assumptions, retire superseded tasks, and
|
||||
add follow-ups where the guided bootstrap experience becomes the canonical
|
||||
operator path.
|
||||
|
||||
**2026-05-24:** Added
|
||||
`docs/security-bootstrap-related-workplan-review.md`, kept `NK-WP-0004` and
|
||||
`NK-WP-0005` as substrate workplans with closeout notes, left historical
|
||||
`NK-WP-0001` archived, and updated stale Railiance OpenBao custody wording.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- The setup operator can see the current trust stage and next safe action.
|
||||
- Live OpenBao init remains blocked until king credential and custody gates are
|
||||
satisfied.
|
||||
- User lifecycle operations are described in plain, auditable flows.
|
||||
- New fabrics can receive delegated admins without granting platform root.
|
||||
- Secret values are never stored or displayed by the bootstrap experience.
|
||||
- The path to two-of-three custody is explicit and low-friction.
|
||||
@@ -0,0 +1,390 @@
|
||||
---
|
||||
id: NK-WP-0003
|
||||
type: workplan
|
||||
title: "KeyCape + privacyIDEA Stack — Cluster Deployment"
|
||||
domain: netkingdom
|
||||
repo: net-kingdom
|
||||
status: completed
|
||||
owner: custodian
|
||||
topic_slug: netkingdom
|
||||
created: "2026-03-20"
|
||||
updated: "2026-05-02"
|
||||
state_hub_workstream_id: "f24cefd4-a09b-4fa1-9b25-94bf783b425e"
|
||||
---
|
||||
|
||||
# KeyCape + privacyIDEA Stack — Cluster Deployment
|
||||
|
||||
## Goal
|
||||
|
||||
Deploy the full NetKingdom identity stack on the live k3s cluster without
|
||||
Keycloak. KeyCape (v0.1, complete) is the OIDC orchestration layer; it
|
||||
binds LLDAP (directory), Authelia (auth sessions), and privacyIDEA (MFA).
|
||||
|
||||
NK-WP-0001 was scoped around Keycloak and is deferred. This workplan
|
||||
covers everything needed to reach a production-ready identity plane.
|
||||
|
||||
## Target cluster
|
||||
|
||||
**RAILIANCE01** — `92.205.62.239` — k3s v1.35.1+k3s1, clean baseline.
|
||||
Kubeconfig: `~/.kube/config-railiance01`
|
||||
|
||||
> Note: T02–T07 were previously completed on CoulombCore (92.205.130.254) by
|
||||
> mistake. CoulombCore is the old management host (Gitea/OCI registry only) and
|
||||
> should not be touched. All SSO stack work targets RAILIANCE01 exclusively.
|
||||
|
||||
## Pre-conditions
|
||||
|
||||
- [x] k3s cluster healthy on RAILIANCE01 — v1.35.1+k3s1, node Ready ✓
|
||||
- [x] kubeconfig available at `~/.kube/config-railiance01` ✓
|
||||
- [x] All manifests committed — net-kingdom `sso-mfa/k8s/` ✓
|
||||
- [x] KeyCape v0.1 complete — KEY-WP-0001 ✓
|
||||
- [x] SOPS + age integrated into net-kingdom — NK-WP-0004 ✓
|
||||
- [x] Agent-driven credential bootstrap ready — NK-WP-0005 ✓ (run `make creds-agent-init`)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Internet → Traefik (RAILIANCE01 k3s) → cert-manager TLS
|
||||
├── auth.coulomb.social → Authelia
|
||||
├── pink.coulomb.social → privacyIDEA portal
|
||||
├── pink-account.coulomb.social → privacyIDEA account self-service
|
||||
└── id.coulomb.social → KeyCape (OIDC)
|
||||
|
||||
KeyCape ──► Authelia (session, password)
|
||||
──► LLDAP (directory, user lookup)
|
||||
──► privacyIDEA (MFA challenges via trigger-admin token)
|
||||
|
||||
privacyIDEA ──► PostgreSQL (privacyidea_db via CloudNativePG)
|
||||
LLDAP ──► SQLite (PVC)
|
||||
Authelia ──► SQLite (PVC)
|
||||
|
||||
KeyCape image pulled from CoulombCore OCI registry: 92.205.130.254:32166
|
||||
(insecure HTTP NodePort — requires registries.yaml on RAILIANCE01)
|
||||
```
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 — Credential setup
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "6a22e17e-5854-4f8b-b419-9dc86d490357"
|
||||
note: Credential foundation exists (NK-WP-0004 + NK-WP-0005). Secrets encrypted in
|
||||
secrets.enc/. Before T02, run `make creds-agent-init` with KUBECONFIG pointing
|
||||
to RAILIANCE01 to inject all secrets into the new cluster.
|
||||
```
|
||||
|
||||
~~Net-kingdom currently uses a manual KeePassXC + age-bundle approach~~
|
||||
Completed via NK-WP-0004 + NK-WP-0005. The credential foundation is in place:
|
||||
|
||||
- SOPS + age integrated — `~/.config/sops/age/keys.txt`, `.sops.yaml`, git hook
|
||||
- Agent bootstrap: `make creds-agent-init` runs the full flow autonomously
|
||||
- Credential standard: `canon/standards/credential-management_v0.2.md`
|
||||
|
||||
To bootstrap credentials into the RAILIANCE01 cluster before T02–T09, run:
|
||||
```bash
|
||||
export KUBECONFIG=~/.kube/config-railiance01
|
||||
make creds-agent-init
|
||||
```
|
||||
This generates all secrets, encrypts to `secrets.enc/`, injects into the
|
||||
cluster, and delivers the emergency bundle. No KeePassXC steps required.
|
||||
|
||||
### T02 — Apply cluster foundations
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "a14e3a6b-18ee-4172-8a47-bd531f21e55a"
|
||||
note: Done 2026-03-25 on RAILIANCE01. Namespaces, NetworkPolicies, cert-manager, ClusterIssuers,
|
||||
insecure registry for CoulombCore OCI all applied and verified.
|
||||
Known gotcha: added allow-traefik-to-acme-solver NetworkPolicy to sso + mfa namespaces
|
||||
(default-deny-all blocked ACME HTTP-01 solver pods from receiving Traefik traffic).
|
||||
```
|
||||
|
||||
Apply the K8s infrastructure foundations. All manifests already committed.
|
||||
|
||||
```bash
|
||||
export KUBECONFIG=~/.kube/config-railiance01
|
||||
kubectl apply -f sso-mfa/k8s/namespaces/
|
||||
kubectl apply -f sso-mfa/k8s/network-policies/
|
||||
kubectl apply -f sso-mfa/k8s/cert-manager/
|
||||
```
|
||||
|
||||
Also configure the insecure OCI registry on RAILIANCE01 so k3s can pull the KeyCape image:
|
||||
```bash
|
||||
ssh tegwick@92.205.62.239 "sudo tee /etc/rancher/k3s/registries.yaml" <<'EOF'
|
||||
mirrors:
|
||||
"92.205.130.254:32166":
|
||||
endpoint:
|
||||
- "http://92.205.130.254:32166"
|
||||
EOF
|
||||
ssh tegwick@92.205.62.239 "sudo systemctl restart k3s"
|
||||
```
|
||||
|
||||
Verify: `bash sso-mfa/k8s/verify-t02.sh`
|
||||
|
||||
Expected: namespaces `sso`, `mfa`, `databases` exist; NetworkPolicies applied;
|
||||
cert-manager pods Running.
|
||||
|
||||
### T03 — Deploy PostgreSQL (CloudNativePG)
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "19e375d0-66bd-4cf0-9c2d-59d5c0d5989e"
|
||||
note: Done 2026-03-25 on RAILIANCE01. CNPG operator + net-kingdom-pg cluster running,
|
||||
privacyidea_db + role created. Verified via verify-t03.sh (8/8 PASS, 2 WARN for
|
||||
superuser secret + scheduled backup — both expected at this stage).
|
||||
```
|
||||
|
||||
Deploy the shared database cluster:
|
||||
|
||||
```bash
|
||||
export KUBECONFIG=~/.kube/config-railiance01
|
||||
kubectl apply -f sso-mfa/k8s/postgres/
|
||||
```
|
||||
|
||||
Wait for cluster to be `Ready`, then verify: `bash sso-mfa/k8s/verify-t03.sh`
|
||||
|
||||
**Note**: Do not proceed to T04 until the CloudNativePG cluster is fully
|
||||
healthy. Migration jobs will fail on a partially-started cluster.
|
||||
|
||||
### T04 — Deploy privacyIDEA
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "9c9c1ec9-0cf5-4546-a83e-d74dbf3b27af"
|
||||
note: Done 2026-03-25 on RAILIANCE01. privacyIDEA pod Running, TLS certs issued,
|
||||
enckey + audit keys bootstrapped (privacyidea-enckey + privacyidea-auditkeys Secrets created),
|
||||
pi-admin + trigger-admin created, trigger-admin-rights policy created via REST API.
|
||||
DEFERRED: pi-admin TOTP enrollment requires an admin realm (SQLresolver pointing to PI's
|
||||
internal admin table) — pi-manage has no enroll command, WebUI token enrollment only works
|
||||
for resolver-backed users. Admin MFA is production hardening; pi-admin auth works
|
||||
password-only for now. Track as T09 hardening item.
|
||||
```
|
||||
|
||||
Run credential bootstrap (injects privacyIDEA secrets + creates pi-admin/trigger-admin):
|
||||
```bash
|
||||
export KUBECONFIG=~/.kube/config-railiance01
|
||||
make creds-agent-init
|
||||
```
|
||||
|
||||
**Remaining manual step:**
|
||||
Once `pink.coulomb.social` resolves to `92.205.62.239` and TLS cert is issued:
|
||||
1. Log in to https://pink.coulomb.social as `pi-admin`
|
||||
2. Enroll MFA for `pi-admin` (TOTP)
|
||||
3. Verify/create trigger-admin policy: Policies → trigger-admin-rights
|
||||
(Scope: admin, Action: triggerchallenge, AdminUser: trigger-admin)
|
||||
|
||||
### T05 — Deploy LLDAP
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T05
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "82fc90f7-8eb4-4718-b02a-dfd5fa39e5bc"
|
||||
note: Done 2026-03-25 on RAILIANCE01. LLDAP pod Running, TLS cert issued (lldap.coulomb.social),
|
||||
groups net-kingdom-users (id=4) + net-kingdom-admins (id=5) created via direct GraphQL.
|
||||
bootstrap-users.sh has a bash set -e / json parse bug (workaround: direct curl).
|
||||
```
|
||||
|
||||
Deploy LLDAP into the `sso` namespace:
|
||||
|
||||
```bash
|
||||
export KUBECONFIG=~/.kube/config-railiance01
|
||||
cd sso-mfa/k8s/lldap
|
||||
bash create-secrets.sh
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl apply -f ingress.yaml
|
||||
kubectl apply -f middleware.yaml
|
||||
bash bootstrap-users.sh # creates base OU structure + initial admin user
|
||||
```
|
||||
|
||||
Verify pod Running and LDAP bind works on `ldap.coulomb.social`.
|
||||
|
||||
### T06 — Deploy Authelia
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T06
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "3a28ff10-fbfa-443b-a64d-bbfe6153c544"
|
||||
note: Done 2026-03-25 on RAILIANCE01. Authelia pod Running (1 restart on init, normal),
|
||||
TLS cert issued (auth.coulomb.social), health endpoint returns {"status":"OK"}.
|
||||
```
|
||||
|
||||
Deploy Authelia into the `sso` namespace:
|
||||
|
||||
```bash
|
||||
export KUBECONFIG=~/.kube/config-railiance01
|
||||
cd sso-mfa/k8s/authelia
|
||||
bash create-secrets.sh
|
||||
kubectl apply -f configmap.yaml
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl apply -f ingress.yaml
|
||||
```
|
||||
|
||||
Verify: `bash sso-mfa/k8s/verify-t05.sh` (covers LLDAP + Authelia together)
|
||||
|
||||
### T07 — Deploy KeyCape
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T07
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "496a97c9-3e2a-486e-ba62-18449868c6cf"
|
||||
note: Done 2026-03-25 on RAILIANCE01. KeyCape pod Running, TLS cert issued (kc.coulomb.social),
|
||||
OIDC discovery endpoint live at https://kc.coulomb.social/.well-known/openid-configuration.
|
||||
PI admin token refreshed via create-pi-token.sh (old token was from CoulombCore).
|
||||
keycape-pi-token K8s Secret created in sso namespace.
|
||||
```
|
||||
|
||||
Deploy KeyCape into the `sso` namespace:
|
||||
|
||||
```bash
|
||||
export KUBECONFIG=~/.kube/config-railiance01
|
||||
cd sso-mfa/k8s/keycape
|
||||
bash create-secrets.sh # includes privacyIDEA trigger-admin token
|
||||
bash create-pi-token.sh # registers KeyCape as a privacyIDEA application
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl apply -f ingress.yaml
|
||||
kubectl apply -f middleware.yaml
|
||||
```
|
||||
|
||||
Verify: OIDC discovery endpoint reachable at
|
||||
`https://id.coulomb.social/.well-known/openid-configuration`
|
||||
|
||||
### T08 — End-to-end authentication test
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T08
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "0fba3392-c916-43fd-a2c1-24ce39481043"
|
||||
note: Completed 2026-03-25. All 3 test packages pass (migration, negative, profile).
|
||||
Go 1.22.10 found at ~/go/bin/go. DNS resolves to 92.205.62.239 (all 4 subdomains).
|
||||
Tests run with: cd src && ~/go/bin/go test ./tests/... -v
|
||||
Results: ok keycape/tests/migration, ok keycape/tests/negative, ok keycape/tests/profile
|
||||
Note: tests use httptest.Server + mocks — no live cluster connection required.
|
||||
Test user provisioned: testuser / test.user@coulomb.social
|
||||
TOTP serial TOTP00007147, seed KVQLHEJCTKCI3K7G2UIF54QUE5BNLBAQ
|
||||
Validated: auth PASS via privacyIDEA /validate/check.
|
||||
pi-admin TOTP deferred to T09 hardening.
|
||||
```
|
||||
|
||||
Prove the full auth flow works:
|
||||
1. OIDC discovery resolves at `id.coulomb.social`
|
||||
2. Authelia password auth succeeds for a test user
|
||||
3. privacyIDEA TOTP challenge issued and accepted
|
||||
4. KeyCape issues a valid access token
|
||||
5. Token introspection returns expected claims (sub, groups, email)
|
||||
|
||||
Use the KeyCape acceptance test suite:
|
||||
```bash
|
||||
cd "$(git rev-parse --show-toplevel)/../key-cape"
|
||||
go test ./tests/... -run TestProfileBaseline -v
|
||||
```
|
||||
|
||||
### T08a — Create Cloudflare DNS A records
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T08a
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "c614f839-61c4-41f6-bfeb-b3f9525a7625"
|
||||
note: Done — all 5 A records (kc, auth, pink, pink-account, lldap) resolve to 92.205.62.239
|
||||
via @8.8.8.8. Confirmed 2026-03-25.
|
||||
```
|
||||
|
||||
Create 5 A records in Cloudflare DNS, **proxy disabled (DNS-only / orange cloud OFF)**,
|
||||
all pointing to `92.205.62.239` (RAILIANCE01 — where k3s/Traefik runs):
|
||||
|
||||
| Subdomain | Type | Value |
|
||||
|-----------|------|-------|
|
||||
| `kc.coulomb.social` | A | `92.205.62.239` |
|
||||
| `auth.coulomb.social` | A | `92.205.62.239` |
|
||||
| `pink.coulomb.social` | A | `92.205.62.239` |
|
||||
| `pink-account.coulomb.social` | A | `92.205.62.239` |
|
||||
| `lldap.coulomb.social` | A | `92.205.62.239` |
|
||||
|
||||
HTTP-01 ACME challenges require direct origin reachability — Cloudflare proxy blocks this.
|
||||
Once DNS propagates, cert-manager's pending challenges will auto-resolve and TLS
|
||||
certs will be issued for all ingresses.
|
||||
|
||||
Verify: `dig +short kc.coulomb.social @8.8.8.8` → `92.205.62.239`
|
||||
|
||||
### T08b — Install Go on RAILIANCE01
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T08b
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "fdfe595a-f5a8-466a-82e9-7cc2ad8e5c3e"
|
||||
note: Go 1.22.10 already installed at ~/go/bin/go (workstation). Tests ran from workstation.
|
||||
Also: Go v1.25.6 present on RAILIANCE01 via k3s.
|
||||
```
|
||||
|
||||
Go is already installed on RAILIANCE01 via k3s (v1.25.6). No action needed.
|
||||
|
||||
Verify: `ssh tegwick@92.205.62.239 "go version"`
|
||||
|
||||
### T09 — Backup, DR, and monitoring
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T09
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "a82751d8-4de8-4668-8568-8dc140a6322b"
|
||||
note: Done 2026-05-02 consolidation. Backup CronJobs are live on RAILIANCE01 and
|
||||
have recent successful runs for LLDAP, Authelia, and privacyIDEA. PVC backup
|
||||
files exist for LLDAP and privacyIDEA enckey; Authelia job logs confirm
|
||||
/data/backups/authelia.backup.2026-05-02. Break-glass and emergency bundle
|
||||
state are confirmed in creds-state.yaml. DEFERRED to platform hardening:
|
||||
CNPG object-store backup (requires MinIO/S3) and Prometheus scraping
|
||||
(requires kube-prometheus-stack / monitoring CRDs).
|
||||
```
|
||||
|
||||
Consolidation evidence (2026-05-02, RAILIANCE01):
|
||||
|
||||
- `lldap-backup`, `authelia-backup`, and `privacyidea-backup` CronJobs exist
|
||||
and have recent successful runs.
|
||||
- Latest job logs confirm:
|
||||
- LLDAP: `/data/backups/users.backup.2026-05-02`
|
||||
- Authelia: `/data/backups/authelia.backup.2026-05-02`
|
||||
- privacyIDEA: `/data/backups/enckey.backup.2026-05-02`
|
||||
- LLDAP PVC contains daily `users.backup.*` files through 2026-05-02.
|
||||
- privacyIDEA PVC contains daily `enckey.backup.*` files through 2026-05-02.
|
||||
- `creds-state.yaml` confirms:
|
||||
- `ops_bundle_created: true`
|
||||
- `emergency_bundle_delivered: true`
|
||||
- `bootstrap_complete: true`
|
||||
- DR runbook is present at `sso-mfa/k8s/backup/DR-RUNBOOK.md`.
|
||||
- NetworkPolicies include default-deny and backup API egress allowance.
|
||||
|
||||
Deferred platform-hardening items:
|
||||
|
||||
- CNPG PostgreSQL object-store backup: CNPG is healthy, but no
|
||||
`ScheduledBackup` resource is installed on RAILIANCE01. This requires a
|
||||
MinIO/S3 target and should be tracked with the platform backup work rather
|
||||
than blocking this SSO/MFA deployment workplan.
|
||||
- Prometheus scraping: monitoring CRDs are not installed on RAILIANCE01
|
||||
(`servicemonitor` resource type is absent). This requires a
|
||||
kube-prometheus-stack deployment and should be tracked with cluster
|
||||
observability work.
|
||||
|
||||
## Done criteria
|
||||
|
||||
- [x] Credentials: `bootstrap_complete: true` in `creds-state.yaml` (NK-WP-0005)
|
||||
- [x] verify-t08.sh: PASS=15, FAIL=0 (WARNs are manual offsite confirmation only)
|
||||
- [x] KeyCape acceptance test suite passes
|
||||
- [x] DB restore drill completed (LLDAP SQLite — 2 users, all tables verified)
|
||||
- [x] Emergency bundle delivered and stored in personal password manager (`creds-state.yaml`)
|
||||
- [x] Ops bundle created and location recorded (`creds-state.yaml`)
|
||||
- [x] privacyIDEA enckey backed up on PVC (/etc/privacyidea/backups/enckey.backup.*)
|
||||
- [x] Monitoring/CNPG object-store backups explicitly deferred to platform hardening
|
||||
@@ -0,0 +1,393 @@
|
||||
---
|
||||
id: NK-WP-0004
|
||||
type: workplan
|
||||
title: "Credential Management Foundation"
|
||||
domain: netkingdom
|
||||
repo: net-kingdom
|
||||
status: done
|
||||
owner: custodian
|
||||
topic_slug: netkingdom
|
||||
created: "2026-03-20"
|
||||
updated: "2026-05-24"
|
||||
state_hub_workstream_id: "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e"
|
||||
---
|
||||
|
||||
# Credential Management Foundation
|
||||
|
||||
## Goal
|
||||
|
||||
Make credential management a first-class, reliable foundation rather than
|
||||
a manual side-task. By the end of this workplan an operator can:
|
||||
|
||||
1. Run `make creds-init` to set up the full SOPS + age + KeePassXC workflow
|
||||
2. Run `make creds-generate` to produce all service secrets and be guided on
|
||||
KeePassXC entry
|
||||
3. Run `make creds-apply` to inject secrets into the cluster in the correct order
|
||||
4. Run `make creds-status` to see what is generated, applied, and verified
|
||||
5. Invoke `/creds-bootstrap` in Claude Code for guided assistance through
|
||||
the bootstrap process
|
||||
|
||||
This workplan is a **pre-condition for NK-WP-0003** (cluster deployment).
|
||||
NK-WP-0003-T01 is blocked until this workplan is complete.
|
||||
|
||||
## Problem
|
||||
|
||||
Current state:
|
||||
- `gen-secrets.sh` and `pack-bundle.sh` exist but are run manually, in
|
||||
isolation, with no orchestration
|
||||
- The five `create-secrets.sh` scripts must be run in a specific order
|
||||
(postgres → lldap → authelia → privacyidea → keycape) but this is
|
||||
undocumented and unenforced
|
||||
- Shared secrets (LLDAP_LDAP_USER_PASS, PI_DB_PASSWORD) are referenced
|
||||
across component scripts but there is no enforcement that source exists
|
||||
before consumer runs
|
||||
- No git pre-commit hook — plaintext secrets can accidentally be committed
|
||||
- No `.sops.yaml` — net-kingdom is not SOPS-enabled, unlike railiance-infra
|
||||
- No credential state file — no way to know which secrets are generated,
|
||||
which are applied, which are verified, without manual cluster inspection
|
||||
- The enckey-bootstrap.sh step is time-sensitive (must run while the
|
||||
privacyIDEA pod is live) but nothing flags this or sequences it
|
||||
- Operator must hold all of this in their head
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Operator
|
||||
│
|
||||
├── make creds-init # one-time: age key check, .sops.yaml, git hook
|
||||
├── make creds-generate # run gen-secrets.sh → guided KeePassXC entry
|
||||
├── make creds-bundle # age-encrypt ops bundle → offsite
|
||||
├── make creds-apply # run all create-secrets.sh in correct order
|
||||
├── make creds-verify # check all K8s secrets exist with expected keys
|
||||
├── make creds-status # show credential state file
|
||||
└── make creds-rotate SECRET=<name> # guided rotation for one secret
|
||||
|
||||
Claude Code skill: /creds-bootstrap
|
||||
└── guided session for first-time bootstrap (reads credential state,
|
||||
knows what's done, provides KeePassXC entry instructions,
|
||||
warns about time-sensitive steps like enckey-bootstrap)
|
||||
```
|
||||
|
||||
## NK-WP-0006 Runtime Secret Refinement
|
||||
|
||||
This workplan remains the bootstrap credential foundation. With OpenBao in
|
||||
the platform stack, its outputs are not the final runtime secret model.
|
||||
They establish enough trust to bring up identity, MFA, and platform
|
||||
services safely.
|
||||
|
||||
Trust-state mapping:
|
||||
|
||||
- bare host and cluster trust are established by Railiance layers;
|
||||
- bootstrap secret trust is established by SOPS/age, encrypted bundles,
|
||||
emergency material, and Kubernetes Secret injection;
|
||||
- bootstrap identity trust is established by local/key-cape/bootstrap
|
||||
identity paths;
|
||||
- runtime secret trust begins only after OpenBao is deployed,
|
||||
initialized, unsealed or auto-unsealed by the approved mechanism,
|
||||
audited, backed up, and ready to issue scoped secrets or dynamic
|
||||
credentials.
|
||||
|
||||
After runtime secret trust exists, Kubernetes Secrets created here should
|
||||
be treated as bootstrap artifacts, delivery caches, or compatibility
|
||||
mechanisms. Long-lived workload secret authority belongs in OpenBao,
|
||||
governed by NetKingdom policy and Railiance platform operations.
|
||||
|
||||
## NET-WP-0016 Closeout Review
|
||||
|
||||
`NET-WP-0016` keeps this workplan as the low-level bootstrap credential
|
||||
foundation. SOPS/age, encrypted bundles, generated secrets, and Kubernetes
|
||||
Secret injection remain useful substrate tooling.
|
||||
|
||||
The operator-facing path is no longer the old `/creds-bootstrap` experience by
|
||||
itself. The canonical guided path is the security bootstrap console and related
|
||||
docs from `NET-WP-0016`, with the dedicated king credential model from
|
||||
`NET-WP-0015`. KeePassXC remains optional personal/offline storage; it is not
|
||||
the platform root of trust.
|
||||
|
||||
## Dependency on canon standard
|
||||
|
||||
All design decisions in this workplan follow
|
||||
`canon/standards/credential-management_v0.2.md`.
|
||||
The KeePassXC group structure, phase model, SOPS policy, and prohibited
|
||||
patterns defined there are normative. This workplan implements them.
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 — SOPS integration
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "2340f2a3-9c11-44a8-b264-41d75b6dbc3e"
|
||||
```
|
||||
|
||||
Add SOPS encryption infrastructure to net-kingdom, aligned with
|
||||
railiance-infra (same age key, same approach).
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Verify the operator age key exists:
|
||||
```bash
|
||||
ls ~/.config/sops/age/key.txt || age-keygen -o ~/.config/sops/age/key.txt
|
||||
```
|
||||
The public key (`age1aq8twfd78wvpra0had8cezcnj96tj4q0068edrz5jez8d6xwmflqdepsh4`
|
||||
for the primary operator) is already in railiance-infra. Reuse the same
|
||||
keypair — one age key per operator across all repos.
|
||||
|
||||
2. Create `keys/age.pub` at the repo root:
|
||||
```
|
||||
age1aq8twfd78wvpra0had8cezcnj96tj4q0068edrz5jez8d6xwmflqdepsh4
|
||||
```
|
||||
|
||||
3. Create `.sops.yaml` at the repo root:
|
||||
```yaml
|
||||
creation_rules:
|
||||
- path_regex: secrets/.*$
|
||||
key_groups:
|
||||
- age:
|
||||
- age1aq8twfd78wvpra0had8cezcnj96tj4q0068edrz5jez8d6xwmflqdepsh4
|
||||
```
|
||||
|
||||
4. Add `secrets/` to `.gitignore` (plaintext secrets MUST NOT enter git).
|
||||
SOPS-encrypted files (`.sops.yaml` extension) may be committed.
|
||||
|
||||
5. Create `.githooks/pre-commit` mirroring railiance-infra:
|
||||
- Blocks any commit that includes a file under `secrets/` lacking
|
||||
`sops:` or `"sops":` marker (i.e. plaintext)
|
||||
- Also blocks any file named `*.env` outside of `sso-mfa/bootstrap/`
|
||||
being committed
|
||||
|
||||
6. `make hooks` target to enable the hook:
|
||||
```makefile
|
||||
hooks:
|
||||
git config core.hooksPath .githooks
|
||||
```
|
||||
|
||||
### T02 — Makefile: SOPS targets
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "f6ad469c-e1d3-4253-b855-e0554e43f612"
|
||||
```
|
||||
|
||||
Create the top-level `Makefile` for net-kingdom. Port SOPS targets from
|
||||
railiance-infra and add net-kingdom-specific targets.
|
||||
|
||||
**Targets to implement:**
|
||||
|
||||
```makefile
|
||||
## One-time setup
|
||||
sops-setup: # Copy age key to ~/.config/sops/age/keys.txt
|
||||
hooks: # Enable git pre-commit hook
|
||||
|
||||
## SOPS operations
|
||||
sops-edit: # sops <file>
|
||||
sops-encrypt: # sops --encrypt --in-place $(FILE)
|
||||
sops-decrypt: # sops -d $(FILE) (stdout only, never write plaintext to disk)
|
||||
sops-rotate: # sops --rotate --in-place $(FILE) (after adding new recipient)
|
||||
check-secrets: # fail if any secrets/ file is not SOPS-encrypted
|
||||
|
||||
## Credential lifecycle
|
||||
creds-init: # prerequisite check + sops-setup + hooks
|
||||
creds-generate: # run gen-secrets.sh + print KeePassXC entry guide
|
||||
creds-bundle: # run pack-bundle.sh with operator age public key
|
||||
creds-apply: # run all create-secrets.sh in dependency order
|
||||
creds-verify: # check all expected K8s secrets exist
|
||||
creds-status: # print credential state file
|
||||
|
||||
## Single-secret rotation
|
||||
creds-rotate: # guided rotation for SECRET= (generate → KeePassXC → apply → verify)
|
||||
```
|
||||
|
||||
### T03 — Credential orchestrator: `creds-apply` ordering
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "4b386b92-8db9-440c-b116-52dbb2bd68cb"
|
||||
```
|
||||
|
||||
The `creds-apply` Makefile target must run `create-secrets.sh` scripts in
|
||||
the correct dependency order, with prerequisite checks at each step.
|
||||
|
||||
**Dependency graph:**
|
||||
|
||||
```
|
||||
postgres/create-secrets.sh (no dependencies)
|
||||
│
|
||||
lldap/create-secrets.sh (needs: lldap/secrets.env)
|
||||
│
|
||||
├── authelia/create-secrets.sh (needs: lldap/secrets.env → LLDAP_LDAP_USER_PASS)
|
||||
│
|
||||
└── keycape/create-secrets.sh (needs: lldap/secrets.env + PI_ADMIN_TOKEN)
|
||||
└── PI_ADMIN_TOKEN available only after T04
|
||||
privacyidea/create-secrets.sh (needs: privacyidea/secrets.env)
|
||||
│
|
||||
└── enckey-bootstrap.sh ← TIME-SENSITIVE: must run while pod is live
|
||||
```
|
||||
|
||||
**Implementation:**
|
||||
|
||||
Create `sso-mfa/bootstrap/creds-apply.sh` that:
|
||||
1. Checks `KUBECONFIG` is set and cluster is reachable
|
||||
2. Checks each `secrets/<component>/secrets.env` exists before sourcing it
|
||||
3. Runs scripts in order: postgres → lldap → authelia → privacyidea
|
||||
4. Explicitly skips keycape (requires PI_ADMIN_TOKEN from post-T04 bootstrap)
|
||||
5. Prints the keycape step as a manual reminder with the exact command
|
||||
6. On success, updates `sso-mfa/bootstrap/creds-state.yaml`
|
||||
|
||||
### T04 — Credential state file
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "5bc125a7-ae42-40a3-864c-c356e5fc122d"
|
||||
```
|
||||
|
||||
Create `sso-mfa/bootstrap/creds-state.yaml` — a tracked file (safe to
|
||||
commit, contains no secrets) that records what has been done:
|
||||
|
||||
```yaml
|
||||
# Credential state — net-kingdom SSO/MFA stack
|
||||
# This file is safe to commit. It contains no secrets.
|
||||
# Updated automatically by make creds-* targets.
|
||||
generated_at: null # ISO datetime from last gen-secrets.sh run
|
||||
bundle_at: null # ISO datetime from last pack-bundle.sh run
|
||||
keepass_confirmed: false # Manually set to true after KeePassXC entry
|
||||
|
||||
secrets_applied:
|
||||
postgres: false
|
||||
lldap: false
|
||||
authelia: false
|
||||
privacyidea: false
|
||||
keycape: false # Requires PI_ADMIN_TOKEN (post privacyIDEA T04)
|
||||
|
||||
enckey_bootstrapped: false # Set after enckey-bootstrap.sh runs
|
||||
pi_admin_created: false # Set after bootstrap-admin.sh runs
|
||||
```
|
||||
|
||||
The `make creds-status` target reads this file and prints a human-readable
|
||||
status table. The `make creds-verify` target checks actual K8s secret
|
||||
existence and updates `secrets_applied` accordingly.
|
||||
|
||||
`keepass_confirmed` is the only field that requires manual operator
|
||||
intervention to set to `true` — it represents the irreducibly human step
|
||||
in the bootstrap process.
|
||||
|
||||
### T05 — git pre-commit hook + `check-secrets` gate
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T05
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "d8ea8fbf-ae89-4675-afba-958187ca37f1"
|
||||
```
|
||||
|
||||
Implement `.githooks/pre-commit` that prevents plaintext secrets from
|
||||
entering git. Port from railiance-infra with net-kingdom-specific additions:
|
||||
|
||||
**Blocks:**
|
||||
- Any file under `secrets/` without a SOPS marker
|
||||
- Any file matching `*.env` outside of `sso-mfa/bootstrap/`
|
||||
- Any file containing any of these patterns: `PI_SECRET_KEY=`, `PI_PEPPER=`,
|
||||
`LLDAP_JWT_SECRET=`, `AUTHELIA_`, `BREAKGLASS_PASSWORD=`
|
||||
|
||||
**Warning only (does not block):**
|
||||
- Files matching `*-bundle*.tar.age` being committed (large encrypted
|
||||
artifacts belong offsite, not in git)
|
||||
|
||||
Add `make hooks-test` target that verifies the hook blocks plaintext
|
||||
(mirrors railiance-infra pattern).
|
||||
|
||||
### T06 — Claude Code skill: `/creds-bootstrap`
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T06
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "b9ecbd3f-17f0-4c1d-97e5-84bfbb43d360"
|
||||
```
|
||||
|
||||
Create `~/.claude/commands/creds-bootstrap.md` — a Claude Code skill that
|
||||
provides guided assistance during the credential bootstrap process.
|
||||
|
||||
**When to use it:** First-time bootstrap or onboarding a new operator.
|
||||
The skill reads `sso-mfa/bootstrap/creds-state.yaml` and provides
|
||||
contextual guidance based on what has been done.
|
||||
|
||||
**Skill behavior:**
|
||||
1. Read `creds-state.yaml` to determine current state
|
||||
2. Identify the next required step (first `false` in dependency order)
|
||||
3. For KeePassXC entry steps: display the exact group path and field names
|
||||
to enter, with values sourced from `secrets/` env files (if present)
|
||||
4. For time-sensitive steps (enckey-bootstrap): print a prominent warning
|
||||
with the exact command and timing constraint
|
||||
5. For verification steps: run `make creds-verify` and interpret results
|
||||
6. After each confirmed step: prompt operator to update `creds-state.yaml`
|
||||
or do it automatically when the state can be derived from cluster state
|
||||
|
||||
**Skill definition file structure:**
|
||||
```yaml
|
||||
---
|
||||
description: "Guide through net-kingdom credential bootstrap. Reads creds-state.yaml and provides step-by-step KeePassXC entry instructions, timing warnings, and verification."
|
||||
argument-hint: "[--repo-path /path/to/net-kingdom]"
|
||||
allowed-tools:
|
||||
- Read
|
||||
- Bash(make creds-status:*)
|
||||
- Bash(make creds-verify:*)
|
||||
- Bash(kubectl get secret:*)
|
||||
---
|
||||
```
|
||||
|
||||
**Note:** The skill does NOT automate KeePassXC entry (that remains a
|
||||
human step). It provides the information an operator needs to do it
|
||||
correctly and verifies the result afterwards.
|
||||
|
||||
### T07 — Secret rotation runbook
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T07
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "e27762d9-aa6a-4a7e-9c34-f8c546797548"
|
||||
```
|
||||
|
||||
Document and automate the rotation procedure for each secret type.
|
||||
Different secrets have different rotation complexity:
|
||||
|
||||
| Secret | Rotation impact | Procedure |
|
||||
|--------|----------------|-----------|
|
||||
| PI_SECRET_KEY | Flask session reset — all users logged out | Stop pod, rotate, restart |
|
||||
| PI_PEPPER | Cannot rotate without re-hashing all passwords | Treat as permanent |
|
||||
| PI_DB_PASSWORD | DB + K8s Secret must be rotated atomically | pg GRANT + Secret update |
|
||||
| LLDAP_JWT_SECRET | All LLDAP sessions invalidated | Rotate Secret, restart pod |
|
||||
| LLDAP_LDAP_USER_PASS | Must update LLDAP + Authelia + KeyCape atomically | 3-step coordinated |
|
||||
| AUTHELIA_SESSION_SECRET | All Authelia sessions invalidated | Rotate, restart |
|
||||
| AUTHELIA_KEYCAPE_CLIENT_SECRET | Must update Authelia (bcrypt) + KeyCape simultaneously | Coordinated 2-step |
|
||||
| KeyCape RSA signing key | All issued tokens immediately invalidated | Brief auth outage |
|
||||
| PI_ENCFILE | Cannot rotate — replace and re-enroll all tokens | Major operation |
|
||||
| BREAKGLASS_PASSWORD | Low impact, rotate freely | Simple update |
|
||||
|
||||
Implement `make creds-rotate SECRET=<name>` that:
|
||||
1. Validates the secret name is known
|
||||
2. Prints the rotation impact and required coordination steps
|
||||
3. Generates a new value (same entropy as original)
|
||||
4. Guides through the atomic update sequence for that secret
|
||||
5. Updates `creds-state.yaml` and ops bundle after rotation
|
||||
|
||||
## Done criteria
|
||||
|
||||
- [ ] `make creds-init` runs cleanly on a fresh workstation (age key check + setup)
|
||||
- [ ] `make creds-generate` produces all secrets and prints KeePassXC entry guide
|
||||
- [ ] `make creds-bundle` produces an age-encrypted ops bundle
|
||||
- [ ] `make creds-apply` runs all `create-secrets.sh` scripts in dependency order
|
||||
- [ ] `make creds-verify` accurately reflects K8s secret state
|
||||
- [ ] `make creds-status` shows a readable state table from `creds-state.yaml`
|
||||
- [ ] `make hooks-test` confirms pre-commit hook blocks plaintext commits
|
||||
- [ ] `/creds-bootstrap` skill loads, reads state, and provides correct next step
|
||||
- [ ] NK-WP-0003-T01 can be marked done by referencing this workplan as complete
|
||||
@@ -0,0 +1,449 @@
|
||||
---
|
||||
id: NK-WP-0005
|
||||
type: workplan
|
||||
title: "Agent-Driven Credential Bootstrap — Zero Human Ops"
|
||||
domain: netkingdom
|
||||
repo: net-kingdom
|
||||
status: done
|
||||
owner: custodian
|
||||
topic_slug: netkingdom
|
||||
created: "2026-03-21"
|
||||
updated: "2026-05-24"
|
||||
depends_on: NK-WP-0004
|
||||
state_hub_workstream_id: "75bc472b-cc0a-48f2-afb6-62b896f7cc19"
|
||||
---
|
||||
|
||||
# Agent-Driven Credential Bootstrap — Zero Human Ops
|
||||
|
||||
## Problem
|
||||
|
||||
NK-WP-0004 built the right tooling but the wrong operator model. It was
|
||||
designed around a human-as-operator workflow:
|
||||
|
||||
- Human runs `gen-secrets.sh`
|
||||
- Human manually types every secret into KeePassXC
|
||||
- Human confirms via `keepass_confirmed: true`
|
||||
- Human runs `creds-apply`
|
||||
|
||||
This is the wrong interface. You delegated the security setup. Being told
|
||||
"go open KeePassXC and type in 23 fields" is not delegation — it is
|
||||
manual labour with extra ceremony.
|
||||
|
||||
## Goal
|
||||
|
||||
The agent owns the full credential lifecycle end-to-end. The only human
|
||||
touchpoint is receiving the **emergency credential bundle** — a minimal
|
||||
set of master keys for break-glass recovery — and storing it once in a
|
||||
personal password store.
|
||||
|
||||
```
|
||||
Agent Human
|
||||
│ │
|
||||
├── generate all secrets │
|
||||
├── encrypt via SOPS/age → commit │
|
||||
├── inject into cluster (kubectl) │
|
||||
├── verify all K8s secrets live │
|
||||
├── create age-encrypted ops bundle │
|
||||
├── assemble emergency bundle ─────────────►│ store in personal password manager
|
||||
│ │ (one-time, nothing else ever)
|
||||
└── mark state complete │
|
||||
```
|
||||
|
||||
**What the human stores (and nothing more):**
|
||||
|
||||
| Item | Why needed |
|
||||
|------|------------|
|
||||
| age private key | Decrypt any SOPS-encrypted secret from git |
|
||||
| break-glass passwords (3-4) | Direct service access if cluster/auth is down |
|
||||
| ops bundle passphrase | Decrypt point-in-time secret snapshot |
|
||||
|
||||
Everything else — service secrets, rotation, re-injection — is agent work.
|
||||
|
||||
## NK-WP-0006 Runtime Secret Refinement
|
||||
|
||||
With OpenBao in the platform stack, the agent-driven bootstrap is the
|
||||
handoff mechanism from bootstrap secrets to runtime secret authority.
|
||||
The agent may generate, encrypt, inject, and verify initial secrets, but
|
||||
OpenBao becomes the normal authority for platform and workload secret
|
||||
delivery once the control plane is alive.
|
||||
|
||||
The bootstrap flow therefore has one additional boundary:
|
||||
|
||||
1. SOPS/age and the emergency bundle establish bootstrap and recovery
|
||||
authority.
|
||||
2. Kubernetes Secrets carry the minimum initial material needed to start
|
||||
the identity, MFA, database, and OpenBao platform services.
|
||||
3. OpenBao is initialized, unsealed or auto-unsealed by the approved
|
||||
mechanism, audit logging is enabled, backups are verified, and
|
||||
workload auth methods are configured.
|
||||
4. Runtime workloads receive scoped secrets, dynamic credentials, or
|
||||
synchronized Kubernetes Secrets from OpenBao. They do not consume
|
||||
platform-root bootstrap material.
|
||||
|
||||
OpenBao root tokens, unseal keys, or recovery keys are break-glass
|
||||
material. They must not be stored as ordinary tenant secrets or exposed
|
||||
to tenant administrators. If they are included in an emergency bundle,
|
||||
that bundle is platform-control-plane break-glass material and requires
|
||||
the strongest storage and review procedure available for the deployment.
|
||||
|
||||
## NET-WP-0016 Closeout Review
|
||||
|
||||
This workplan remains useful as automation substrate, but its "zero human ops"
|
||||
framing is superseded at the product and custody layer by `NET-WP-0015` and
|
||||
`NET-WP-0016`.
|
||||
|
||||
Agents may still generate, encrypt, inject, verify, and rotate bootstrap
|
||||
material. They must not silently assume king credential custody, run live
|
||||
OpenBao initialization unattended, or treat emergency bundles as ordinary
|
||||
operator conveniences. The guided bootstrap experience is the canonical
|
||||
operator path for king credential setup, OpenBao ceremony readiness, handover
|
||||
cleanup, and reopening under custody.
|
||||
|
||||
## Design
|
||||
|
||||
### What changes from NK-WP-0004
|
||||
|
||||
| NK-WP-0004 | NK-WP-0005 |
|
||||
|-----------|-----------|
|
||||
| Human runs `make creds-generate` | Agent runs bootstrap automatically |
|
||||
| Human enters secrets in KeePassXC | No KeePassXC in the operational path |
|
||||
| `keepass_confirmed: false` gate | `emergency_bundle_delivered: false` gate |
|
||||
| `/creds-bootstrap` skill = guided walkthrough | `/creds-init` skill = autonomous execution |
|
||||
| Ops bundle created manually | Ops bundle created automatically |
|
||||
| Rotation triggered manually | Rotation can be triggered by agent |
|
||||
|
||||
### KeePassXC role
|
||||
|
||||
KeePassXC is **removed from the primary workflow**. It becomes optional
|
||||
personal infrastructure — if you choose to import the emergency bundle
|
||||
into KeePassXC that is your business, but it is not required or assumed.
|
||||
|
||||
The age private key and SOPS-encrypted git files ARE the credential store.
|
||||
The ops bundle IS the backup. The emergency bundle IS the human's key ring.
|
||||
|
||||
### What the agent cannot automate (genuine human gates)
|
||||
|
||||
1. **Confirming receipt of the emergency bundle** — the agent must not
|
||||
mark the bootstrap complete until the human confirms they have stored
|
||||
the bundle. This is a deliberate pause, not a workaround.
|
||||
2. **The privacyIDEA enckey bootstrap** — must happen while the pod is
|
||||
live (time-sensitive window). The agent can detect when the pod is
|
||||
ready and run the step automatically, but must verify the human is
|
||||
present or at least that the operation succeeded.
|
||||
3. **Initial age keypair generation** — if no age key exists yet, the
|
||||
agent generates it and the private key is the first thing that goes
|
||||
into the emergency bundle.
|
||||
|
||||
---
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 — Redesign creds-state.yaml for agent mode
|
||||
|
||||
```task
|
||||
id: NK-WP-0005-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "6748cf8d-a7c7-47a2-b32a-2e26e05c4cba"
|
||||
```
|
||||
|
||||
Replace the human-confirmation model with an agent-progress model.
|
||||
|
||||
**New schema** (`sso-mfa/bootstrap/creds-state.yaml`):
|
||||
|
||||
```yaml
|
||||
# Credential state — net-kingdom SSO/MFA stack
|
||||
# Safe to commit. Contains no secrets. Updated by agent.
|
||||
schema_version: 2
|
||||
agent_mode: true # NK-WP-0005: fully automated
|
||||
|
||||
# Phase tracking
|
||||
age_key_present: false # ~/.config/sops/age/key.txt exists
|
||||
secrets_generated: false # gen-secrets.sh ran successfully
|
||||
ops_bundle_created: false # age-encrypted bundle created
|
||||
ops_bundle_location: null # path or storage hint
|
||||
|
||||
# Emergency bundle
|
||||
emergency_bundle_delivered: false # human confirmed receipt
|
||||
emergency_bundle_delivered_at: null
|
||||
|
||||
# Cluster injection (per-component)
|
||||
secrets_applied:
|
||||
postgres: false
|
||||
lldap: false
|
||||
authelia: false
|
||||
privacyidea: false
|
||||
keycape: false
|
||||
|
||||
# Post-apply bootstrap (agent-run when pod is Ready)
|
||||
enckey_bootstrapped: false
|
||||
pi_admin_created: false
|
||||
|
||||
# Derived: all true → bootstrap complete
|
||||
bootstrap_complete: false
|
||||
```
|
||||
|
||||
Remove `keepass_confirmed` and `generated_at` (now in git history).
|
||||
Add `schema_version: 2` so scripts can detect which model they are running.
|
||||
|
||||
---
|
||||
|
||||
### T02 — Agent bootstrap script: `creds-bootstrap-agent.sh`
|
||||
|
||||
```task
|
||||
id: NK-WP-0005-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "22940c39-8645-40e1-b947-17e85ea6d902"
|
||||
```
|
||||
|
||||
Create `sso-mfa/bootstrap/creds-bootstrap-agent.sh` — the single
|
||||
entrypoint for fully automated credential bootstrap.
|
||||
|
||||
**Flow:**
|
||||
|
||||
```
|
||||
1. pre-flight
|
||||
├── check kubectl / KUBECONFIG reachable
|
||||
├── check SOPS age key (generate if missing → add to emergency bundle)
|
||||
└── check cluster is healthy (kubectl get nodes)
|
||||
|
||||
2. generate
|
||||
└── run gen-secrets.sh ./secrets
|
||||
|
||||
3. encrypt + commit
|
||||
├── sops --encrypt each secrets env file → sso-mfa/encrypted/
|
||||
└── git add + git commit "chore(creds): encrypted secrets [agent]"
|
||||
|
||||
4. inject
|
||||
└── run creds-apply.sh (existing — postgres → lldap → authelia → privacyidea)
|
||||
|
||||
5. verify
|
||||
└── run creds-verify.sh — exit if any K8s secret missing
|
||||
|
||||
6. post-apply bootstrap (waits for pods)
|
||||
├── wait for privacyIDEA pod Ready (max 5 min)
|
||||
├── run enckey-bootstrap.sh
|
||||
├── run bootstrap-admin.sh → capture PI_ADMIN_TOKEN
|
||||
├── inject keycape secrets (now PI_ADMIN_TOKEN is known)
|
||||
└── run creds-verify.sh again — all components now
|
||||
|
||||
7. ops bundle
|
||||
└── run pack-bundle.sh ./secrets <age-pub-key>
|
||||
|
||||
8. emergency bundle
|
||||
├── assemble emergency-bundle.txt (see T03)
|
||||
├── display to terminal with clear formatting
|
||||
└── prompt: "Have you stored this? [y/N]"
|
||||
|
||||
9. cleanup
|
||||
├── shred secrets/ plaintext
|
||||
└── write creds-state.yaml: bootstrap_complete: true
|
||||
|
||||
10. update state
|
||||
└── state-hub: mark NK-WP-0005 tasks done via API
|
||||
```
|
||||
|
||||
**Error handling:** Each phase updates `creds-state.yaml` so a restart
|
||||
resumes from where it left off (idempotent re-runs skip completed phases).
|
||||
|
||||
---
|
||||
|
||||
### T03 — Emergency bundle format and delivery
|
||||
|
||||
```task
|
||||
id: NK-WP-0005-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "42ce1486-5322-4cf2-9c71-1c1c61db5f46"
|
||||
```
|
||||
|
||||
Create `sso-mfa/bootstrap/emergency-bundle.sh` that assembles and
|
||||
displays the emergency credential bundle.
|
||||
|
||||
**Bundle contents:**
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════════════════════════════╗
|
||||
║ NET-KINGDOM EMERGENCY CREDENTIAL BUNDLE ║
|
||||
║ Generated: <ISO-date> Store this. Nothing else. ║
|
||||
╠══════════════════════════════════════════════════════════════════╣
|
||||
║ AGE PRIVATE KEY (decrypt all SOPS secrets from git) ║
|
||||
║ ────────────────────────────────────────────────────────────── ║
|
||||
║ AGE-SECRET-KEY-1... ║
|
||||
╠══════════════════════════════════════════════════════════════════╣
|
||||
║ BREAK-GLASS PASSWORDS (direct service access, cluster-bypass) ║
|
||||
║ ────────────────────────────────────────────────────────────── ║
|
||||
║ privacyIDEA admin : <pi-admin password> ║
|
||||
║ LLDAP admin : <lldap admin password> ║
|
||||
║ PostgreSQL root : <postgres root password> ║
|
||||
║ break-glass user : <lldap break-glass password> ║
|
||||
╠══════════════════════════════════════════════════════════════════╣
|
||||
║ OPS BUNDLE (age-encrypted point-in-time secret snapshot) ║
|
||||
║ ────────────────────────────────────────────────────────────── ║
|
||||
║ Location : <path/url to ops bundle> ║
|
||||
║ Decrypt : age -d -i <age-key-path> ops-bundle-<date>.tar.age ║
|
||||
╠══════════════════════════════════════════════════════════════════╣
|
||||
║ RECOVERY INSTRUCTIONS ║
|
||||
║ ────────────────────────────────────────────────────────────── ║
|
||||
║ 1. Restore age key to ~/.config/sops/age/key.txt ║
|
||||
║ 2. Clone net-kingdom repo + run: make creds-apply ║
|
||||
║ 3. Use break-glass passwords for direct service access if needed ║
|
||||
╚══════════════════════════════════════════════════════════════════╝
|
||||
```
|
||||
|
||||
**Delivery:**
|
||||
1. Print to terminal (operator copies manually into personal password
|
||||
manager — 1Password, Bitwarden, KeePassXC, paper — agent does not
|
||||
care which)
|
||||
2. Optionally write to `~/emergency-bundle-<date>.txt` for 60 seconds,
|
||||
then shred automatically
|
||||
|
||||
**Confirmation gate:** After display, prompt:
|
||||
```
|
||||
Store the above in your personal password manager now.
|
||||
Press Enter when done (this will clear the screen and shred any temp file):
|
||||
```
|
||||
Only after Enter does the script continue and mark
|
||||
`emergency_bundle_delivered: true`.
|
||||
|
||||
---
|
||||
|
||||
### T04 — `/creds-init` Claude Code skill (autonomous)
|
||||
|
||||
```task
|
||||
id: NK-WP-0005-T04
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "ca713ce7-6f2c-4f0c-8b6c-88fc6e559190"
|
||||
```
|
||||
|
||||
Replace the guided `/creds-bootstrap` skill with a fully autonomous
|
||||
`/creds-init` skill.
|
||||
|
||||
**Behaviour:**
|
||||
1. Read `creds-state.yaml` to determine current phase
|
||||
2. If `bootstrap_complete: true` → report status and exit
|
||||
3. If `emergency_bundle_delivered: false` and some phases done → resume
|
||||
from where the state file says
|
||||
4. Otherwise → run `creds-bootstrap-agent.sh` end-to-end
|
||||
5. On completion → log progress event to state-hub
|
||||
|
||||
**Skill definition:**
|
||||
```yaml
|
||||
---
|
||||
description: >
|
||||
Fully automated net-kingdom credential bootstrap. Generates all service
|
||||
secrets, encrypts and commits via SOPS, injects into cluster, and delivers
|
||||
a minimal emergency bundle for your personal password manager. No manual
|
||||
steps required.
|
||||
argument-hint: "[--dry-run] [--resume]"
|
||||
allowed-tools:
|
||||
- Bash(make creds-*)
|
||||
- Bash(bash sso-mfa/bootstrap/creds-bootstrap-agent.sh*)
|
||||
- Bash(kubectl get*)
|
||||
- Bash(git status*)
|
||||
- Read
|
||||
---
|
||||
```
|
||||
|
||||
The old `/creds-bootstrap` skill can be archived or updated to delegate
|
||||
to `/creds-init`.
|
||||
|
||||
---
|
||||
|
||||
### T05 — Makefile: `creds-agent-init` target
|
||||
|
||||
```task
|
||||
id: NK-WP-0005-T05
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "ac5d887e-c499-4cf6-91e7-90e2e0e78d4a"
|
||||
```
|
||||
|
||||
Add to the existing Makefile:
|
||||
|
||||
```makefile
|
||||
## Fully automated credential bootstrap (NK-WP-0005)
|
||||
## Generates, encrypts, injects, and delivers emergency bundle.
|
||||
## Resumes automatically if interrupted.
|
||||
creds-agent-init:
|
||||
@bash sso-mfa/bootstrap/creds-bootstrap-agent.sh
|
||||
|
||||
## Show current bootstrap state
|
||||
creds-agent-status:
|
||||
@bash sso-mha/bootstrap/creds-status.sh --v2
|
||||
|
||||
## Re-deliver emergency bundle (if lost/stolen — generates new bundle, rotates nothing)
|
||||
creds-emergency-reprint:
|
||||
@bash sso-mfa/bootstrap/emergency-bundle.sh --reprint
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### T06 — Agent-driven rotation
|
||||
|
||||
```task
|
||||
id: NK-WP-0005-T06
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "2f0782f7-db5d-4b8a-920b-582548c4591f"
|
||||
```
|
||||
|
||||
Extend `creds-rotate.sh` to run non-interactively when called from the
|
||||
agent (currently it is interactive/guided).
|
||||
|
||||
Add `--secret <name> --non-interactive` flags:
|
||||
- Generates new value
|
||||
- Applies the atomic update sequence for that secret type
|
||||
- Re-encrypts SOPS file
|
||||
- Commits
|
||||
- Verifies
|
||||
- Updates `creds-state.yaml` with `last_rotated_<secret>: <ISO-date>`
|
||||
- Does NOT require human confirmation (agent is the operator)
|
||||
|
||||
The emergency bundle is **not** reprinted on routine rotation — only on
|
||||
full re-bootstrap or explicit `creds-emergency-reprint`.
|
||||
|
||||
Exception: if the age private key is rotated, a new emergency bundle
|
||||
MUST be delivered before the old one is revoked.
|
||||
|
||||
---
|
||||
|
||||
### T07 — Update credential management standard
|
||||
|
||||
```task
|
||||
id: NK-WP-0005-T07
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "42ac193d-7b56-48f7-8eba-757a6dad2fba"
|
||||
```
|
||||
|
||||
Update `canon/standards/credential-management_v0.1.md` to v0.2,
|
||||
reflecting the agent-driven model:
|
||||
|
||||
- Section 2 (Trust Hierarchy): remove KeePassXC from operational path;
|
||||
add it as optional personal store for the emergency bundle
|
||||
- Section 3 Phase 0: replace manual steps with `make creds-agent-init`
|
||||
- New Section: Emergency Bundle — what it contains, how it is delivered,
|
||||
when to use it
|
||||
- Remove Section 4 (KeePassXC group structure) or demote to appendix
|
||||
(still useful if user chooses KeePassXC for their personal store)
|
||||
- Update Section 6 (Ops Bundle): bundle creation is now automated
|
||||
- Update Section 7 (Prohibited Patterns): add "agent MUST NOT skip the
|
||||
`emergency_bundle_delivered` gate, even in non-interactive runs"
|
||||
|
||||
---
|
||||
|
||||
## Done Criteria
|
||||
|
||||
- [ ] `make creds-agent-init` runs from scratch on a clean workstation
|
||||
without any human input until the emergency bundle prompt
|
||||
- [ ] Emergency bundle is displayed clearly; confirmation gate works
|
||||
- [ ] All K8s secrets verified live after bootstrap
|
||||
- [ ] `creds-state.yaml` shows `bootstrap_complete: true` after run
|
||||
- [ ] `/creds-init` skill in Claude Code runs the full flow autonomously
|
||||
- [ ] Rotation works non-interactively via `--non-interactive` flag
|
||||
- [ ] Credential management standard updated to v0.2
|
||||
- [ ] NK-WP-0003 unblocked (T01 was already unblocked by NK-WP-0004;
|
||||
this workplan makes T01 actually safe to run without human ops)
|
||||
Reference in New Issue
Block a user