Implements login-methods.md §LM5 phase one. Core gains nostr/nip46.ts -- a
Nip46Client speaking kind-24133 RPC with NIP-44 transport encryption,
bunker:// parsing, nostrconnect listen (secret echo is proof-of-scan),
auth_url surfacing, and typed deadline errors -- plus subscribeLive on
RelayConnection with re-REQ on reconnect, and live post-EOSE delivery in
the MockRelay so tests exercise real round trips against a fake bunker.
The ephemeral client key and Nip46Client live in the crypto worker
(§LM5.4); nip46* actions bypass the serial queue so a signer waiting
minutes for human approval cannot block other worker calls, and the
signer's auth_url reaches the UI as the worker's first notification frame.
The widget's connect screen runs both legs at once: a nostrconnect QR --
rendered as dot-style inline SVG with rounded finder eyes, per owner
preference -- for phone signers, and a bunker:// paste box (password
input; the URI carries a token). window.nostr is now an element-routed
provider, so a NIP-46 session claims the slot and hosts reading it get
the remote signer transparently; NIP-07 still leaves the slot to the
extension. Sessions stay thin per §LM7: no vault, no persistence.
Persistence remains gated on the external vault review (§LM5.3).
14 new tests (core fake-bunker e2e, QR geometry, signer delegation) and
two policy pins: bunker option below the password path, element stays
web-storage-free.
Co-Authored-By: Claude Fable 5 <[email protected]>
Implements login-methods.md §LM4. New signers/ layer in the widget:
detectForeignNip07Provider never mistakes BitLogin's own provider for an
extension, and Nip07Signer wraps the snapshotted provider with capability
probing (typed SignerUnsupportedError instead of TypeErrors off missing
nip44), deadlines on every call (SignerTimeoutError -- no silent hangs),
pubkey validation, and unsigned-event normalization.
The element routes its public API through the active signer, so hosts get
the extension backend transparently. Extension sessions are thin (§LM7):
no capsule, no vault (requestNwcConnection resolves null), no persistence,
and window.nostr is left to the extension -- claimSigner stays reserved
for BitLogin-account sessions. bitlogin-login now carries method and
capabilities; publicKey is unchanged for existing hosts.
Password-first pinned (owner decision, §LM9.1): the extension option
renders as a secondary link under the primary sign-in, enforced in
securityPolicy.test.ts alongside a pin that the extension confirm path
never claims window.nostr or writes storage. 14 new signer tests.
Co-Authored-By: Claude Fable 5 <[email protected]>
Proposes aggregating sign-in methods behind the existing provider facade
instead of being one option in a host's login menu. NIP-07 delegates to an
installed extension; NIP-46 connects to a remote signer, memory-only until
the vault review gate clears. Rejects nsec paste as a login method on the
record — §SF10 import is the sanctioned path for raw keys. Non-BitLogin
sessions are signer-only: no capsule, no vault, no portable state.
Co-Authored-By: Claude Fable 5 <[email protected]>