Move the lifecycle predicate into lib/acl/lifecycle.ts (re-exported from
the ACL module) so it can be unit-tested without a database. Adds Node
built-in test-runner coverage for the boundary conditions that define
the fix: past expiry -> dead, expiry == now -> dead (exclusive), revoke
beats a future expiry, and liveWhere kept in lockstep with grantIsLive.
Runner is node:test via ts-node (no new dependency; pnpm add is blocked
by the nix-built node_modules hoist pattern). 'npm test' -> 7 passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Required to keep the nix package buildable: nixpkgs unstable no longer
ships prisma-engines 5.x — the unsuffixed `prisma-engines` attr now
aliases 7.x (no libquery_engine.node), and the only versioned attrs are
`prisma-engines_6` (6.19.3) and `prisma-engines_7`. Bump both
`@prisma/client` and `prisma` to ^6.19.0 so the client matches the only
engine we can pin to.
Also:
- package.nix takes `prisma-engines_6` directly. flake.nix passes
`pkgs.prisma-engines_6 or pkgs.prisma-engines` so the package still
builds on nixos-25.05 (where prisma-engines is 6.7.0 unsuffixed).
- Drop PRISMA_INTROSPECTION_ENGINE_BINARY — prisma 6 collapsed the
introspection engine into schema-engine, the binary no longer ships.
Schema is unchanged so existing fresh installs migrate identically.
Existing dev instances with a prisma_5-tracked _prisma_migrations table
will need a one-time `prisma migrate resolve` step on first boot under
the new client; deploy targets are all fresh installs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Caught during regtest dogfood after the previous three commits
landed. With `nostr-tools: ^2.17.2` pnpm resolved to 2.23.5, which
in turn pulls `@noble/curves@2.0.1` — ESM-only. The regtest
Dockerfile runs on Node 20.11.1, where CJS `require()` of pure-ESM
modules is hard-blocked:
Error [ERR_REQUIRE_ESM]: require() of ES Module
/app/node_modules/.pnpm/@noble+curves@2.0.1/.../secp256k1.js
from /app/node_modules/.pnpm/nostr-tools@2.23.5/.../index.js
not supported.
nostr-tools 2.21.0 was the cutover — that release flipped
`@noble/curves` from `1.2.0` to `2.0.1`. 2.20.0 is the last
nostr-tools 2.x release that's still CJS-friendly via @noble/curves
1.2.0. Capping our pin at `~2.20.0` keeps us within the
"nostr-tools >= 2.17.2" range NDK 3.0.3 asks for in its
peerDependency while sidestepping the ESM/CJS hazard.
This isn't a regression we introduce — it's a CJS-output footgun
unique to the regtest container's Node 20 + tsup-default-CJS
combination. Long-term fix paths (out of scope here):
* Bump the container's Node base image to >= 22 (where
`--experimental-require-module` is on by default for `.js`
files inside `package.json type: "commonjs"`)
* Switch tsup output to ESM (`tsup --format esm`) — wider
surface change across the daemon, the client CLI, and the
Dockerfile entrypoint
* Accept the cap forever (small downside: 2.21+ patch fixes
won't reach us until we fix one of the above)
The cap is intentionally tight (`~2.20.0` allows 2.20.x patches,
nothing newer) so a future `pnpm update` doesn't silently jump us
back over the 2.21 edge. Revisit when one of the long-term paths
above lands.
Refs aiolabs/nsecbunkerd#14, regtest dogfood 2026-05-31.
NDK 2.8.1 (Apr 2024) is 2 years old and predates NIP-46 backend-side
nip44 support. With aiolabs/lnbits#38's phase-2.4 client-side migration
to bunker-mediated nip44_*, the bunker's lack of a `nip44_decrypt`
strategy registration causes wire RPCs to fall through to
`sendResponse(id, remotePubkey, "error", undefined, "Not authorized")`
at NDK 2.8.1's backend/index.ts:179. Even nip04 was silently broken:
2.8.1 normalizes the wire method to `encrypt`/`decrypt` for
`pubkeyAllowed` while lnbits's policy stores wire names. The
encrypt/decrypt path through nsecbunkerd has never actually worked
end-to-end; it just hadn't been exercised until phase 2.4 landed.
3.0.3 (Feb 2026) is the current `latest` dist-tag and ships:
- `nip44_encrypt` / `nip44_decrypt` backend handlers registered
by default + wire-name `pubkeyAllowed` semantics (the immediate fix)
- `switch_relays` NIP-46 support for client-side relay migration
- Configurable NDKNip46Signer timeout (pairs with lnbits PR #38's
matching client-side config)
- NIP-44 default outgoing encryption with NIP-04 compat fallback
- Async error handling fix in backend dispatch — failed strategies
report errors instead of silent drop (deb7f93d)
- "Not enough relays received this event" race-condition fix on
publish (relevant to open #7 — may close that one too)
- Signature verification moved in-house (off legacy nostr-tools v1
path)
- 2 years of security/perf updates in transitive @noble/* crypto
primitives
`nostr-tools` bumped from ^1.17.0 to ^2.17.2 alongside because NDK
3.x's `NDKPrivateKeySigner` imports `finalizeEvent` / `generateSecretKey`
+ uses the `./nip49` subpath, none of which exist in nostr-tools v1.17.
With v1.17 installed, `require('@nostr-dev-kit/ndk')` fails with
"Package subpath './nip49' is not defined". Confirmed against the
post-install module graph.
Source migrations for NDK 3 / nostr-tools v2 API surface land in the
follow-up commit; this commit is intentionally just the dep bump so
the diff stays reviewable. Refs aiolabs/nsecbunkerd#14 +
coord-log 2026-05-31T09:55Z.
Upstream declares the dependency as workspace:*, but the repo has no
pnpm-workspace.yaml and no sibling @nostr-dev-kit/ndk package — so
pnpm install fails with ERR_PNPM_WORKSPACE_PKG_NOT_FOUND on a clean
clone. The shipped pnpm-lock.yaml was resolving to ndk 2.8.1, so pin
to that exact version to match what the lockfile already expects.
Fixes#3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>