nsecbunkerd/docs/runbook-migrations.md
Padreug 87e99e487e
Some checks failed
Docker image / build-and-push-image (push) Has been cancelled
docs: correct prisma-engines + migrate-on-boot accuracy in runbook
- nix devShell uses nixos-25.05's prisma-engines 6.7.0 (has
  libquery_engine.node) — the 7.x problem is the system/unstable channel
  only, not the flake's devShell (corrects an earlier overstatement; #30
  closed as invalid).
- start.js migrate-on-boot is a no-op on nix but IS the migration path on
  docker (image ENTRYPOINT) — don't assume it's dead everywhere (#31).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:50:21 +02:00

3.5 KiB

nsecbunkerd migration & DB-maintenance runbook

Operational notes for applying schema migrations and ACL/pairing maintenance on deployed, LNbits-connected nsecbunkerd instances.

⚠️ Never full-wipe nsecbunker.db on an LNbits-connected instance

The nsecbunkerd ↔ LNbits pairing is split across both systems:

  • Bunker (nsecbunker.db): per-account KeyUser binding (keyed by LNbits's stable client pubkey) + redeemed Token + a shared Policy.
  • LNbits (accounts.signer_config, RemoteBunkerSigner): {token, client_nsec, policy_id}.

RemoteBunkerSigner.sign_event() signs directly with the stored client_nsec — it does NOT re-connect/re-redeem, and there is no auto-repair on restart or sign-failure. provision() runs only at new-account creation and mints a NEW npub (which changes the user's nostr identity).

Consequence of a full nsecbunker.db wipe: the KeyUser bindings are deleted → every LNbits account's stored config dangles → all signing fails, and the only standard "repair" (provision()) changes identities. Do not do it.

Correct way to strip the #24 materialized photocopies

Post-#27, token grants are evaluated live via the ACL step-4 Token → Policy → PolicyRule join, so the old materialized SigningCondition rows are redundant — and, written with expiresAt = NULL, they would keep granting past a token's expiry (silently re-opening #24 for already-paired clients). Strip them with a targeted delete that preserves the pairing:

-- Verify first.
SELECT COUNT(*) FROM Token WHERE redeemedAt IS NOT NULL;  -- bindings to preserve
SELECT COUNT(*) FROM SigningCondition;                    -- photocopies to strip

-- Keeps KeyUser + Token + Policy intact. Live-token clients keep working
-- untouched; only the stale photocopies are removed.
DELETE FROM SigningCondition;

Run against each instance's nsecbunker.db. If an instance was already full-wiped, recover by restoring the pre-wipe nsecbunker.db backup, then run the targeted delete.

Manual-override grants (add_signing_condition, web-approval) also live in SigningCondition. On an LNbits-only bunker there typically are none, so a blanket DELETE FROM SigningCondition is safe. If an instance uses manual overrides, delete only the policy-derived rows you intend to strip.

Keys are never in the DB

Key material lives in nsecbunker.json (keys), never in nsecbunker.db. A DB wipe loses ACL/pairing state, never keys. LNbits holds the bunker admin nsec (LNBITS_NSEC_BUNKER_ADMIN_NSEC) and is the sole admin client.

Schema migrations

On the nix deploy, migrations are applied by the deploy's prisma migrate deploy, not by the daemon — start.js's npm run prisma:migrate step fails in the read-only nix store (no npm on PATH) and is a redundant no-op there (#31). On docker, by contrast, start.js IS the migration path (it's the image ENTRYPOINT), so the step is not dead everywhere. After adding a migration, make sure the path that applies for your target actually runs it.

Prisma on NixOS: the flake pins nixos-25.05, whose prisma-engines is 6.7.0 (ships libquery_engine.node) — both the devShell and the deploy's package.nix use it, so prisma (and the npm run test:integration suite) work in nix develop out of the box. Heads-up: on nixos-unstable / a system <nixpkgs> channel, the bare prisma-engines attr is 7.x with no libquery_engine.node; don't run the repo's prisma against an unstable channel.