The CNPG Option A dumps prune only locally, so r01-* folders grow without bound; the Backup account filled and the identity backup has refused since 2026-10-20. plan is read-only and hashes the plan; delete needs that hash, re-lists and refuses on any change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 308701@bnt-lap001 Assistant-Session: 5548e447-444c-4dcc-bed1-05c16eebfe56 |
||
|---|---|---|
| .. | ||
| capture-identity.py | ||
| check-privacyidea-database.py | ||
| fetch-privacyidea-backup.py | ||
| identity-backup-profile.json | ||
| identity-bundle.py | ||
| identity-credential-setup.py | ||
| identity-custody.json | ||
| identity-files.py | ||
| identity-isolation.py | ||
| identity-offsite.py | ||
| identity-retention.py | ||
| identity-runtime.py | ||
| install-identity-backup.py | ||
| inventory-scaleway.py | ||
| inventory-storage-share.py | ||
| isolated-authelia.py | ||
| isolated-keycape-session.py | ||
| isolated-keycape.py | ||
| isolated-lldap.py | ||
| isolated-privacyidea-mfa.py | ||
| isolated-privacyidea.py | ||
| kingpath-offsite.py | ||
| prepare-identity-coverage.py | ||
| prepare-isolated-keycape.py | ||
| prune-storage-share-dumps.py | ||
| README.md | ||
| recovery-kit.py | ||
| retrieve-identity-offline.py | ||
| retrieve-identity.py | ||
| run-isolated-authelia.sh | ||
| run-isolated-keycape.sh | ||
| run-isolated-lldap.sh | ||
| run-isolated-privacyidea-mfa.sh | ||
| run-isolated-privacyidea.sh | ||
| run-offline-recovery.sh | ||
| verify-bundle.py | ||
| verify-identity-delivery.py | ||
| verify-identity.py | ||
| verify-keycape-attempt-receipt.py | ||
Bootstrap recovery transport check
Owner work: NK-WP-0049. Follow the exact recovery ceremony.
For an identity/custody outage, start with the
Storage Share disaster recovery guide.
retrieve-identity-offline.py performs fixed-provider ciphertext retrieval from
privately entered independent provider access, without OpenBao/SSO. Its source
receipt pins the object, strong ETag, size and digest; failed downloads are removed.
recovery-kit.py builds an allowlisted standalone tools ZIP and can publish only
three new versioned guide/ZIP/checksum files beside the backups with exact
readback. run-offline-recovery.sh is the owner's private two-prompt handoff for
publication, outage-path retrieval and the existing archive recovery check.
Run secret prompts in the owner's own terminal, never an agent-captured PTY.
Actual owner publication/readback and outage-path archive recovery passed on
2026-10-16; see the published location and receipts.
Service restore admission remains in the
isolated restore packet.
The first native-service stage is run-isolated-lldap.sh USER TRANSFER_RECEIPT.
It preflights the exact local Docker engine/images before asking privately for
the age key and backup-era existing-user password. isolated-lldap.py validates
the encrypted archive, restores original directory/key material only into
network-none/read-only/tmpfs containers, proves native LDAP bind and explicit
invalid-credential denial, compares durable identity rows and removes its owned
containers/plaintext. It uses no live Kubernetes/OpenBao endpoint or provider
grant. Actual-image synthetic tests and the real owner backup-recovery run
passed on 2026-10-16; T16 is done. See the packet and actual LLDAP receipt for
the exact target, resource limits, accepted scope and repeat-drill command.
run-isolated-authelia.sh USER TRANSFER_RECEIPT is the Authelia stage (T17,
prepared under T41). It asks for the same two private inputs. isolated-authelia.py
runs restored LLDAP as the network-none anchor with Authelia joining its
namespace, and checks the original config, storage encryption key, issuer
key, KeyCape's keycape client secret and existing-user first factor, with
wrong-password and wrong-client-secret refusal. Notifier, NTP and healthcheck
writes are confined; nothing leaves loopback. It imports isolated-lldap.py
unedited. Synthetic native tests and the actual owner run (2026-10-24) passed; T17 is done.
run-isolated-privacyidea-mfa.sh TRANSFER_RECEIPT is the existing-token MFA step (T19, prepared under
T42). After the age key it asks for your login name, then the PIN+code you would type at login.
isolated-privacyidea-mfa.py restores LLDAP, the database and the original keys into one network-free
namespace, repoints the archived LDAP resolver to the restored directory in the disposable copy only, and calls
/validate/check by user and realm, as KeyCape does: wrong key refused, wrong code refused, your code accepted.
It reuses isolated-privacyidea.py and isolated-lldap.py unedited. Fixture tests and the actual owner run (2026-10-24) passed; T19 is done.
run-isolated-privacyidea.sh TRANSFER_RECEIPT is the token-material/audit recovery step.
It asks only for the existing backup age key. isolated-privacyidea.py restores
the linked dump into a loopback-only PostgreSQL/container namespace, reads
retained token material internally, and verifies all stored audit signatures
plus one new isolated entry and wrong-public-key/tampering negatives. Original
keys are preserved; audit exceptions are reported without rewriting history.
No MFA code is consumed or native MFA acceptance claimed. Exit2 requires owner
review of audit exceptions; see the packet for bounds and safe receipts.
The actual owner run passed on 2026-10-16: five retained token materials,
279,209 historical signatures, new-entry and negative checks, and cleanup.
T20/T31 are done; actual existing-token MFA remains T19.
verify-bundle.py validates a digest-bound age-encrypted secrets/ tar archive
using an explicit private temporary key and registered keys/age.pub. It checks
an exact required-file list, unrelated wrong-key refusal, safe isolated file
publication/readback and staging removal. No service or cluster is contacted.
It refuses links, special/duplicate/unreviewed entries, oversized content, private age keys in the bundle, wrong digest/recipient and existing receipt destinations. Only a closed metadata receipt leaves the process. Current service credentials, independent/off-host custody, production acceptance and revocation remain separate owner evidence. Same-UID/process-memory isolation and forensic secure erasure are not provided by this local checker.
Tests use real age encryption/decryption with disposable keys and malicious archive fixtures, never production credentials:
python3 -m pytest -q tests/test_bootstrap_recovery_transport.py
inventory-storage-share.py is a silent read-only child of the existing
attended Platform Backup-account procedure. It checks the caller's OIDC/admin
scope, reuses the owner custody accessor, and exports only bounded DAV names and
sizes. Warden revokes and cleans up the session. It never reads an age key or
object content and contains provider errors/redirects/proxies.
inventory-scaleway.py lists bounded metadata in the fixed backup bucket/prefix
using the existing scoped runtime delivery and pinned primary cluster. It uses
neither the bootstrap API key nor object GET/write/delete operations. Run through
the already admitted caller with cached boto3 1.40.18; it emits only its safe
receipt. Both inventories explicitly state their bounds and cannot prove asset
absence outside them, ciphertext integrity, decryption or native recovery.
fetch-privacyidea-backup.py retrieves only the fixed reviewed PI ciphertext
under the same attended Platform Backup-account procedure. Strong ETag binding,
exact length, digest and private no-overwrite output are required. It reads no
age key. check-privacyidea-database.py then runs through temporary human custody,
checks the transfer lineage/digest and recipient, tests wrong-key refusal and
restores the decrypted custom-format dump into pinned local PostgreSQL with no
network, ports or host data mounts. Only three fixed table counts are exported.
It removes the disposable database and plaintext and records cleanup. It does
not prove token decryption, audit signing continuity or full identity recovery.
See the exact prepared command in the ceremony, rather than inventing new inputs.
NK49_RUN_DISPOSABLE_POSTGRES_TEST=1 python3 -m pytest -q \
tests/test_privacyidea_offhost_recovery.py \
tests/test_storage_share_inventory.py tests/test_scaleway_backup_inventory.py
identity-backup-profile.json prepares missing full service coverage and names
the exact current workload/PVC/credential dependencies, proposed provider paths
and remaining implementation/admission gates. It is a proposal, not a new live
publisher grant. prepare-identity-coverage.py --receipt NEW_PATH verifies the
primary cluster and current deployment/PVC/reference bindings using metadata
only. It refuses replacement resources, unobserved/unready deployments and new
unreviewed references. It has no Secret-data, pod-exec, mutation or upload route.
Its receipt says prepared_not_admitted; it cannot prove a backup exists.
kingpath-offsite.py --item NAME=PATH ... --receipt PATH (NK-WP-0055-T13) seals
break-glass access material that otherwise lives only on the founder workstation,
such as the attended admin kubeconfig and the static tunnel key, to the registered
railiance-backup recipient. It uploads the result under a new name to the
Storage Share Backup account (railiance-backups/net-kingdom-kingpath/) and
reads it back with a hash check. Run it in your own terminal: it refuses unsafe
inputs (symlinks, group/world-readable, foreign-owned) before prompting for the
app password. Plaintext is packed in memory and never written to disk. The
receipt (0600) holds item names, sizes and the ciphertext digest, never values.
Recovery needs only the age key and the Storage Share password (crisis custody
list items 1 and 2). Contained tests: tests/test_kingpath_offsite.py.