Files
bitlogin/CHANGELOG.md
T
thePR0M3TH3ANandClaude Fable 5 a465e1d8fa Implement the Connection Vault protocol layer (phases A-C)
connection-vault.md 5-13 as code, with the implementation-time decisions
recorded in a new section 18 and vault-ux.md holding the agreed UX
walkthroughs as the authorization layer's acceptance criteria.

Capsules gain paired optional fields connection_vault_root and
vault_sudo_key (both or neither, validated); registration mints them,
login/change-password/recovery carry them forward, and
enableConnectionVault is the phrase-gated migration for existing
accounts -- recovery capsule written first, fail closed, idempotent by
reusing roots found in either capsule so a partial failure can never
mint twice and strand records.

src/vault/ holds the finalized key hierarchy (HKDF/ScalarExpand
constants pinned by test vectors that must only ever be reverted, never
updated), per-connection encrypted records signed by a derived vault
identity with the capsule JCS+padding+AES-GCM pipeline and shared
buckets so record types are indistinguishable by size, the NWC profile
with lossless URI parse/export, relay sync reusing the publish-and-
verify quorum bar, and per-record high-water marks so a stale replica
is withheld and reported rather than silently resurrecting a revoked
credential.

The two-tier model makes sudo mode cryptographically real: personal-
tier record keys require vault_sudo_key as HKDF input, VaultSession
holds it only inside an enableSudo/endSudo window (wiping on the way in
and out), and outside a window personal records are invisible -- the
same trial-decrypt returns null, not an error. The app-facing API is
structurally blind to the tier by construction.

118 tests passing, including four end-to-end scenarios against mock
relays: clean-device restore, password-change carry, legacy-account
migration (wrong phrase refused), and rollback detection.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CMr9Yt8SYmZcfgrg3CV2G1
2026-07-30 12:29:46 -04:00

5.2 KiB
Raw Blame History

Changelog

Tracks changes to @bitlogin/core and @bitlogin/widget that matter to a site with its own vendored copy (see "Updating your integration" in README.md). Not every commit needs an entry — only ones where a site sitting on an older vendored copy is missing a fix or behavior change it would plausibly want. Newest first.

2026-07-30

  • Connection Vault protocol layer in @bitlogin/core (src/vault/): encrypted per-connection records signed by a derived vault identity, the finalized §CV6 key hierarchy with pinned test vectors, the NWC credential profile (lossless URI parse/export), relay sync with quorum readback and per-record rollback detection, and the two-tier model — connectable records vs sudo-gated personal records (connection-vault.md §18).
  • Capsules gained paired optional fields connection_vault_root and vault_sudo_key (both or neither; validated). Registration mints them for new accounts; enableConnectionVault is the phrase-gated, fail-closed, idempotent migration for existing accounts. Login, password change, and phrase recovery all carry them forward. Integration caveat: a site on an OLDER vendored widget that performs a password change will rebuild the credential capsule WITHOUT these fields, stranding connectable records until a phrase ceremony repairs them — update vendored copies before enabling the vault for users.

2026-07-21

  • Fix: registration/login could fail quorum if a single built-in relay was down. BUILTIN_VAULT_RELAYS had only 3 relays and BUILTIN_DISCOVERY_RELAYS only 2, sharing relay.nostr.band between both lists. publishAndVerify() requires a fixed minAcks/minReadbacks of 2 regardless of relay count, so losing that one relay left zero margin — surfacing as "Registration did not reach the required relay acknowledgement and readback quorum." Added nostr.wine and relay.snort.social to the vault list, and nostr-pub.wellorder.net to discovery. (681f622) Re-vendor if: your site uses the default built-in relay list (i.e. you don't pass your own vault-relays/discovery-relays attributes). If you already pass your own relay list, this doesn't affect you.

  • Fix: widget completely unstyled (giant logo, unstyled buttons) under a strict style-src CSP. element.ts injected its CSS as an inline <style> tag inside the shadow root on every render. A host with style-src 'self' and no 'unsafe-inline' silently drops an inline <style>'s rules entirely — visible as the brand SVG rendering at its raw ~590×119px intrinsic size instead of the intended 20px-tall lockup, with every other shadow-DOM element similarly falling back to unstyled browser defaults. Switched to building the stylesheet once as a CSSStyleSheet and assigning it via adoptedStyleSheets, which isn't subject to style-src. (e8712f9) Re-vendor if: your site's CSP sets style-src without 'unsafe-inline' (check your CSP header/meta tag). If your style-src already includes 'unsafe-inline', or you have no CSP, this was invisible to you either way, but re-vendoring is still a good idea (this fix also drops needless per-render CSS reparsing).

  • Feature: NIP-04 support, for drop-in parity with a real NIP-07 extension. window.nostr.nip04 (and the element-scoped nip04Encrypt/nip04Decrypt) didn't exist at all before this — only NIP-44 was implemented. A site (or its own legacy DM code path) calling window.nostr.nip04.encrypt(...) while BitLogin was the active signer got Cannot read properties of undefined instead of a working call. (2838402) Re-vendor if: your site (or any third-party code running on it) uses window.nostr.nip04 — a real NIP-07 extension always has it, so anything written against "whatever window.nostr provides" may already assume it's there. No attribute/config changes needed; this is purely additive.

2026-07-22

  • Feature: sessions persist across a page reload. Previously every reload destroyed the crypto worker, requiring the full login-name + password flow again — re-running Argon2id and a relay quorum read on every navigation, not just the first sign-in. After a successful register/login/recover/ changePassword, the everyday private key and active capsule events are now cached in IndexedDB; connectedCallback() tries to restore that cache before ever showing the welcome screen, firing bitlogin-login exactly as a fresh login would if one is found. Logout clears it. (9e3787d) Security note: this means the decrypted everyday private key now sits in the browser's IndexedDB for that origin until logout — the same tradeoff every NIP-07 extension and crypto wallet already makes for the same "don't ask for the password every navigation" reason, scoped by IndexedDB's own per-origin isolation. The recovery phrase and login name/password are never cached. Re-vendor if: you want this — it's the reason to re-vendor this entry. No attribute/config or host-integration changes needed; a host page's existing bitlogin-login listener (see "Embedding BitLogin" below) fires the same way whether the sign-in was just typed or silently restored.