Files
bitlogin/packages/demo/public/llms.txt
T
thePR0M3TH3ANandClaude Fable 5 49682dd75c NIP-46 sign-in: remote-signer sessions behind the same facade, memory-only
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]>
2026-08-04 10:31:36 -04:00

135 lines
7.7 KiB
Plaintext

# BitLogin
> A static, relay-backed portable Nostr account protocol. A familiar login
> name and password (or an existing `nsec`) unlocks a portable Nostr identity
> stored as encrypted "capsules" on public Nostr relays — no account server,
> no password-reset email, no database. Ships as an embeddable custom element
> plus a NIP-07-compatible `window.nostr` provider that any static site can
> drop in with one script tag.
## Integrate in one snippet
```html
<script type="module" src="/vendor/bitlogin/bitlogin.js"></script>
<bitlogin-auth
vault-relays="wss://relay-one.example,wss://relay-two.example,wss://relay-three.example"
discovery-relays="wss://discovery-one.example,wss://discovery-two.example">
</bitlogin-auth>
```
`bitlogin.js` and `cryptoWorker.js` (plus the shared chunk they both import)
must be deployed side by side — the main script resolves the worker's URL at
runtime relative to its own location, so this works from any path. For
production, self-host these files rather than depending on the demo
deployment above.
## What it gives your app
- A `<bitlogin-auth>` custom element (Shadow DOM, no CSS collisions) handling
create account, sign in, forgot-password recovery via a 12-word phrase, and
password rotation.
- One login surface, several methods: if the visitor's browser has a NIP-07
signer extension, the welcome screen offers "Use your Nostr extension
instead" below the primary username/password path, and every visitor gets
"Use a remote signer (bunker)" -- a NIP-46 connect screen with a scannable
nostrconnect QR for phone signers (Amber, nsec.app, ...) and a bunker://
paste box. The element routes `getPublicKey`/`signEvent`/`nip44*`/`nip04*`
through whichever signer backs the session. Alternative-method sessions are
signer-only: no vault (`requestNwcConnection` resolves null), no
persistence (a reload ends the session). NIP-07 sessions leave
`window.nostr` to the extension; NIP-46 sessions claim it, backed by the
worker-held remote-signer client. `bitlogin-login`'s `detail.method`
(`'bitlogin' | 'nip07' | 'nip46'`) and `detail.capabilities`
(`{ nip44, nip04, getRelays }`) tell your app which backend is active and
what it supports.
- A `window.nostr` provider shaped like the de facto NIP-07 interface
(`getPublicKey`, `signEvent`, `getRelays`, `nip44.encrypt`/`decrypt`) —
existing Nostr app code that already calls `window.nostr` works unmodified,
same as it would against a browser extension (Alby, nos2x, …).
- `window.bitlogin` — installed as soon as the script loads, independent of
login state: `{ version, isActiveSigner(), releaseSigner() }`. Use
`window.bitlogin.isActiveSigner()` to tell whether BitLogin specifically
(not another extension) currently backs `window.nostr`, and
`releaseSigner()` to hand `window.nostr` back (a safe no-op if BitLogin
doesn't hold it) when a user switches to a different signing method in
your own UI.
- The `<bitlogin-auth>` element also exposes instance-scoped
`claimSigner()` / `releaseSigner()` methods for the same purpose;
`claimSigner()` is called automatically on every successful sign-in,
sign-up, or recovery, and `releaseSigner()` on logout and on removal
from the DOM — so by default a page with a single widget never needs to
call either manually. Without this, `window.nostr` would stay pointed at
a signed-out BitLogin provider forever, blocking a switch to another
method without a page reload.
- `requestNwcConnection({ appName, reason })` on the element — asks the user
to share an NWC wallet connection with the page's origin and resolves the
full `nostr+walletconnect://` URI (or null if declined). Handles sign-in,
one-tap approval for an origin-bound stored connection (connections are
encrypted Connection Vault records that follow the user across devices),
or first-time import via Bitcoin Connect's chooser / a pasted URI. REVEAL
mode: the origin receives the full bearer credential — wallet-side budgets
are the real security boundary. Requires `style-src-attr 'unsafe-inline'`
in the host CSP. Fires `bitlogin-connection-granted` after each share.
- `offerNwcConnection(uri, { appName, label })` on the element — the inverse:
the app obtained an NWC URI by its own means and offers the user a portable
copy. Consent screen in the widget's own UI, never a silent save; resolves
'saved' | 'declined' | 'already-saved' (same wallet+secret exists; origin
binding refreshed, no UI) | 'unavailable' (no session/vault — treat as
no-op). Safe to fire after every successful connect.
- Events on the element: `bitlogin-login` (`detail.publicKey`,
`detail.method`, `detail.capabilities`),
`bitlogin-logout`, `bitlogin-signer-claimed`, `bitlogin-signer-released`
(the last also fires on `window` when triggered via
`window.bitlogin.releaseSigner()`), `bitlogin-connection-granted`
(`detail.origin`).
- Private keys live in a dedicated Web Worker and the signing APIs return only
public keys, signed events, and ciphertext. LIMIT: an embedded widget is
same-origin with its host page, so a malicious or XSS-compromised host can
reach the worker directly (including exportIdentity) and can overlay the
consent UI. Treat the host page as trusted; embed only in sites you control.
- Removing a wallet connection from BitLogin deletes the stored copy only. It
is NOT revocation -- spending authority is revoked at the wallet.
- Losing BOTH the password and the recovery phrase is permanent: there is no
custodial recovery, by design. An account also depends on relays retaining
its encrypted capsule; download the recovery export as an offline fallback.
## Key facts for integration
- Build or obtain a reviewed release, then self-host its complete module graph
on the application's own origin and load it with `<script type="module">`.
- Don't give the element `id="bitlogin"` — browsers auto-expose elements with
a matching `id`/`name` as a same-named global, which shadows
`window.bitlogin`. Use `id="bitlogin-widget"` or similar.
- Configure relays via the `vault-relays` and `discovery-relays` attributes
(comma-separated `wss://` URLs); omit to use BitLogin's small built-in
bootstrap list.
- Human-chosen passwords are disabled. The widget always generates a
high-entropy passphrase because a downloadable encrypted capsule can be
guessed against offline forever.
- Recovery is a 12-word BIP-39-style phrase, independent of the password;
losing the password never locks the user out if they still have the
phrase.
## Reference docs
- [Integration guide](/docs.html) — full walkthrough with code samples.
- [Live account manager demo](/account.html) — create/login/recover, sign a
test event, rotate password, export identity.
- [Protocol specification](https://github.com/PR0M3TH3AN/bitlogin/blob/main/docs/spec.md) —
the full BitLogin protocol (crypto construction, capsule formats,
relay architecture, threat model).
- [Second-factor / recovery options design notes](https://github.com/PR0M3TH3AN/bitlogin/blob/main/docs/second-factor.md)
and [future recovery-path options](https://github.com/PR0M3TH3AN/bitlogin/blob/main/docs/future-recovery-options.md) —
proposals, not all implemented; each notes its own status.
- [Source repository](https://github.com/PR0M3TH3AN/bitlogin) — monorepo:
`packages/core` (protocol implementation), `packages/widget` (the embeddable
element + provider), `packages/demo` (this site).
## Status
Experimental alpha, NOT independently audited. Verified with an in-memory
mock-relay test suite, real-browser end-to-end runs, and an internal
adversarial audit (2026-07-31) whose findings are recorded in
docs/connection-vault.md §19 and the CHANGELOG. See the README
in the source repository for the full implemented/deferred feature list.