Authored by ChatGPT/Codex in a separate review pass. Reviewed and verified
here before committing rather than taken on trust; findings below.
The substantive change is that the vault sudo key now lives ONLY in the
phrase-gated recovery capsule. A newly written password credential capsule
carries just connection_vault_root, so an ordinary login restores
connectable-tier records without putting personal-tier material behind a
password capsule that is downloadable and therefore offline-guessable forever.
Pre-hardening capsules stay readable, password login does not return their
sudo key, and the next rotation scrubs it. That closes the gate this project
had explicitly set on personal-tier UI.
Legacy vault-enable migration now derives roots deterministically from
phrase-protected material and account_id, so two concurrent attempts cannot
mint divergent roots -- the failure mode the docs previously mitigated with
"enable the vault once, from one device".
Manual (user-chosen) passwords are REMOVED, not merely discouraged, and a new
securityPolicy.test.ts pins that along with several things tests do not
usually reach: the deployment CSP (default-src 'none', frame-ancestors
'none') plus six baseline headers, demo pages free of inline scripts and
remote fonts, and README documenting same-origin self-hosting only rather
than a remote demo embed. llms.txt was updated to match, which I checked
because that test only reads README.
What I verified, since a passing suite proves less than it appears to:
- All 12 fixes from the 2026-07-30 audit survived the rewrite of recover.ts,
changePassword.ts, validation.ts, relay.ts, session.ts and element.ts.
Checked semantically where grep cannot help: changePassword still checks
publish success BEFORE building the tombstone, and vaultSetBinding still
unbinds on origin === null rather than silently rebinding to the worker
global.
- No dependency changes; the package.json diff is scripts only.
- Argon2id and KDF parameters unchanged. The NIP-44 diff ADDS a base64 length
cap before decoding, a memory-DoS guard.
- Validation increased rather than decreased: 44 throws added, 33 removed.
- No disabled or skipped tests, no secrets in the diff.
typecheck clean, 165/165 tests, build green, and the account manager exercised
in a browser: creation correctly refused when relay quorum could not be met,
and on retry failed at the recovery capsule with "No login credential was
published" -- the §18.1 fail-closed ordering doing its job against a flaky
public relay set.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CMr9Yt8SYmZcfgrg3CV2G1
Preparing to open the project up beyond this repo's own use:
- LICENSE: MIT, and a "license": "MIT" field on the root and all three
workspace package.json files.
- CONTRIBUTING.md: dev workflow, the mock-relay testing setup (most
sandboxes/CI have no route to public Nostr relays, and registration
needs a 2-of-N ack quorum so a single mock relay always fails), the
spec-first process for anything touching wire format or crypto
(docs/spec.md is canonical; speculative ideas go in their own doc, not
the spec), and stricter expectations for crypto/key-handling changes.
- SECURITY.md: private vulnerability disclosure via GitHub's advisory
flow rather than public issues, given this handles private keys and
password-derived secrets client-side; states current audit status
plainly (none yet).
- README: short "Contributing" section pointing at all three.
Co-Authored-By: Claude <[email protected]>
Claude-Session: https://claude.ai/code/session_01GWJbXXhvTx9wo59jSz57J6
Builds out the v0.3 spec as an npm workspace monorepo any static site can
consume for login and Nostr signing:
- packages/core: framework-free protocol implementation — Argon2id/HKDF/
ScalarExpand/AES-256-GCM/JCS crypto primitives, NIP-01 event handling and a
quorum-aware relay pool, credential/recovery capsule construction and
validation, and the full account lifecycle (registration, any-device
login with rollback/relay-disagreement detection, phrase recovery with
recovery-capsule refresh, password rotation with mandatory tombstone +
NIP-09 deletion, replica repair, recovery export, NIP-19 encoding, a
NIP-44 v2 implementation, and a NIP-07-shaped signer). 49 tests cover the
spec's Phase 0 byte-level vectors plus full create/login/recover/rotate
end-to-end scenarios against an in-memory mock relay.
- packages/widget: a <bitlogin-auth> custom element with a dedicated crypto
Web Worker (private keys never touch the main thread) and a window.nostr
NIP-07-compatible provider, so existing Nostr web apps work against it
with zero code changes. Built with Vite into two relocatable static files.
- packages/demo: a plain static site (no build step of its own) consuming
the widget — a branding/landing page, an integration guide, and an
account-manager page covering create/login/recover, password rotation,
and identity export.
All flows were verified twice: protocol-level tests against a mock relay,
and the actual built widget driven end-to-end in real Chromium (registration,
phrase confirmation, event signing, clean-device login, window.nostr, and
phrase recovery all reproduce the identical everyday pubkey).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01GWJbXXhvTx9wo59jSz57J6