diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c6dd240..a3f4863 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,7 +51,7 @@ reachable from most contributor sandboxes either. ## The spec is canonical -`docs/spec.md` (currently v0.3) is the source of truth for the protocol +`docs/spec.md` (currently v0.4) is the source of truth for the protocol itself — wire formats, KDF parameters, capsule structure, derivation paths, threat model. Code comments reference it by section (`§11.2`, `§17.4`, …). diff --git a/README.md b/README.md index 9f1211b..d355125 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ password that unlocks a portable Nostr identity, plus an independent 12-word recovery phrase. Encrypted account material lives on public Nostr relays — no account server, no password-reset email, no database. -This repo implements the protocol from `docs/spec.md` (v0.3) as a modular +This repo implements the protocol from `docs/spec.md` (v0.4) as a modular system any static site can embed for login and Nostr signing. ## Layout diff --git a/packages/demo/public/docs.html b/packages/demo/public/docs.html index bbefb1c..362454f 100644 --- a/packages/demo/public/docs.html +++ b/packages/demo/public/docs.html @@ -39,6 +39,20 @@ discovery-relays="wss://discovery-one.example,wss://discovery-two.example"> </bitlogin-auth>
That's it — the element renders its own sign-in/create/recover UI inside a shadow root, so it won't collide with your site's CSS.
+<bitlogin-auth> from the DOM (including indirectly, by resetting an
+ ancestor's innerHTML — a common pattern in hand-rolled UI without a
+ framework's reconciler) tears down its crypto Web Worker. If that happens mid-session,
+ every signing call made afterward just hangs forever with no error, because the worker
+ is gone and nothing ever replies — a call already in flight when it happens can even
+ look like it succeeded. If your app re-renders the container this element lives in,
+ give it a container that's never replaced wholesale and toggle the hidden
+ attribute instead of removing the element. See the account manager demo's source for a
+ single-page example, or BitRoad's src/nostr/bitloginAdapter.mjs /
+ #bitloginMount (a sibling Nostr commerce app that embeds BitLogin) for a
+ worked example of keeping the element alive across a host app's own re-renders.
+ window.nostr contention<bitlogin-auth> mirrors the same four calls as instance methods, scoped to that specific
+ element rather than the single shared window.nostr slot:
const bl = document.querySelector('bitlogin-auth');
+
+const pubkey = await bl.getPublicKey();
+const signed = await bl.signEvent({ kind: 1, content: 'hello', tags: [] });
+const cipher = await bl.nip44Encrypt(peerPubkeyHex, 'hi there');
+const plain = await bl.nip44Decrypt(peerPubkeyHex, cipher);
+ Prefer this if your app already holds a reference to the element — e.g. you built the sign-in UI yourself
+ instead of relying on window.nostr-reading app code. It keeps working even if something else
+ currently owns window.nostr (another extension, a different widget instance), and it's what a host
+ app should use if it wants a specific, known signer rather than "whichever provider is active this moment."
+ BitRoad's src/nostr/bitloginAdapter.mjs is a real example: its signer adapter calls these four
+ methods directly and never touches window.nostr at all.
window.nostr is a single global slot — only one provider can occupy it at a time. If a user also
@@ -120,6 +151,16 @@ if (window.bitlogin?.releaseSigner()) {
window.bitlogin.releaseSigner() also fire bitlogin-signer-claimed /
bitlogin-signer-released events (on the element and on window, respectively) if you'd
rather react to the change than check a return value.
claimSigner() can fail without breaking anything. Some NIP-07 extensions
+ install window.nostr as a non-configurable, non-writable property specifically to stop another
+ script from overwriting it. When that happens, claimSigner() returns false instead
+ of throwing (and bitlogin-signer-claimed's event.detail carries
+ { windowNostrClaimed: false, error }), and every BitLogin sign-in flow completes normally
+ regardless — the failure only means window.nostr still points at that other extension. If your
+ app talks to the element directly (previous step) this never matters at all; only code that reads
+ window.nostr itself is affected, and only for as long as the other extension holds the slot.
+ discovery-relays at relays you trust for your users. Use at least three vault relays for
redundancy — capsule publication and reads use a quorum, so no single relay is a single point of failure.
The widget renders in a shadow root, but every color, radius, font, and its max-width are all read from CSS
+ custom properties on :host — and custom properties inherit through the shadow boundary, so your
+ page's own CSS can override every one of them without touching the widget's internals:
bitlogin-auth {
+ --bl-accent: #3d9bff; /* primary button / link / focus ring color */
+ --bl-accent-hover: #2f86e0; /* primary button hover */
+ --bl-accent-fg: #04101f; /* text color on top of --bl-accent */
+ --bl-bg: #12151f; /* card background */
+ --bl-fg: #eaeef6; /* body text */
+ --bl-muted: #98a2b6; /* secondary text */
+ --bl-border: rgba(255, 255, 255, 0.10);
+ --bl-input-bg: #1a1e2b; /* input / credential-box background */
+ --bl-danger: #ff6b6b;
+ --bl-danger-bg: rgba(255, 107, 107, 0.16);
+ --bl-radius: 14px;
+ --bl-font-family: Inter, ui-sans-serif, system-ui, sans-serif;
+ --bl-max-width: none; /* default 380px keeps it a fixed-width card;
+ override to fill a wider container */
+}
+ Overriding none of these is a valid choice — the widget falls
+ back to its own light theme, a prefers-color-scheme: dark media query, and a
+ data-theme="dark"|"light" attribute override for pages with their own theme toggle. See BitRoad's
+ bitlogin-auth { ... } rule in its src/styles.css for a real integration that maps
+ every one of these onto its own design tokens.
No account database, no password-reset email, no server-side session. The account manager demo