mirror of
https://github.com/PR0M3TH3AN/bitlogin.git
synced 2026-10-27 08:31:21 +00:00
Signer interop: - <bitlogin-auth> gains claimSigner()/releaseSigner() instance methods, and window.bitlogin gains a matching releaseSigner(). Before this, once the widget claimed window.nostr it held it forever (even after logout), so a site offering multiple signing methods (an extension, a NIP-46 bunker, BitLogin) had no way to let a user switch away from BitLogin without a page reload. claimSigner() is now also called automatically on every successful sign-in/sign-up/recovery, and releaseSigner() on logout and DOM removal. Both fire bitlogin-signer-claimed/-released events. docs.html and llms.txt document the new API and a switch-signer example. Manual password parity + fixes: - The opt-in custom-password path (added earlier per spec §9.3) was only available at initial account creation; it's now offered on password rotation and phrase recovery too, via a shared renderManualPasswordFields()/ readManualPassword() pair. - Added a required confirm-password field with live match feedback, wherever a manual password can be set. - Found and fixed a real bug while testing this: a validation failure (mismatched confirm, weak password) on any manual-password screen fully re-rendered the form, silently wiping the typed password and un-checking "I saved it" -- forcing a full retype. Fixed via draft fields that repopulate the inputs' value attribute across the failure re-render, and by reading the checkbox before validation instead of after. Recovery-phrase display split (§anti-cheat): - The 12-word phrase and its 3-word verification quiz were shown on the same screen, defeating the point of the quiz. Split into a display-only "confirm-phrase" screen and a separate "verify-phrase" screen where the phrase is hidden while the quiz is answered. Offline recovery-export import (§19.5): - recoverWithPhrase() now accepts optional offlineRecoveryCapsuleEvents, merged with whatever the live relay read returns; recovery still requires the phrase either way, since the export file never contains it or a phrase-derived key. Covered by a new core e2e test that recovers successfully against genuinely unreachable relay URLs using only the offline candidate. - The recover-phrase screen gets an optional file input for a previously downloaded recovery export, parsed and validated client-side before being passed through to the worker. Landing page copy updated to reflect that manual passwords are allowed (with a real entropy floor and common-password check), not disallowed. 60/60 tests pass; core and widget typecheck clean. Verified live in Chromium via Playwright against a 3-relay in-memory mock quorum: account creation (generated and manual, including the mismatch/retry-preserves- draft path), phrase display/quiz split, signer claim/release/reclaim across logout and re-login, password rotation and phrase recovery with manual passwords (including logging back in with the result), and the offline recovery-export file path. Zero page or console errors across every run. Co-Authored-By: Claude <[email protected]> Claude-Session: https://claude.ai/code/session_01GWJbXXhvTx9wo59jSz57J6
116 lines
5.3 KiB
HTML
116 lines
5.3 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>BitLogin — a portable login for the open web</title>
|
|
<meta name="description" content="A familiar login name and password that unlocks a portable Nostr identity, stored as encrypted capsules across public relays. No account server required." />
|
|
<link rel="icon" href="./assets/icon.svg" type="image/svg+xml" />
|
|
<link rel="manifest" href="./manifest.webmanifest" />
|
|
<link rel="stylesheet" href="./assets/site.css" />
|
|
</head>
|
|
<body>
|
|
|
|
<nav class="nav">
|
|
<a class="brand" href="./index.html"><span class="dot"></span>BitLogin</a>
|
|
<div class="links">
|
|
<a class="active" href="./index.html">Overview</a>
|
|
<a href="./docs.html">Integrate</a>
|
|
<a href="./account.html">Try it</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<header class="hero">
|
|
<h1>A login your users<br />actually take with them.</h1>
|
|
<p class="lead">
|
|
Login name and password on the outside. A portable Nostr identity underneath —
|
|
encrypted and stored on public relays, with no account server, no password-reset
|
|
email, and no vendor lock-in.
|
|
</p>
|
|
<div class="cta-row">
|
|
<a class="btn primary" href="./account.html">Try the demo →</a>
|
|
<a class="btn secondary" href="./docs.html">Add it to your site</a>
|
|
</div>
|
|
|
|
<div class="diagram">
|
|
<div class="step"><div class="k">Everyday login</div>Login name + password</div>
|
|
<div class="arrow">→</div>
|
|
<div class="step"><div class="k">Locally derived</div>Password-locked locator key</div>
|
|
<div class="arrow">→</div>
|
|
<div class="step"><div class="k">Public relays</div>Encrypted capsule lookup</div>
|
|
<div class="arrow">→</div>
|
|
<div class="step"><div class="k">Result</div>Portable Nostr identity</div>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="container">
|
|
<h2>Two independent ways in, one identity</h2>
|
|
<p class="section-sub">Forgetting a password and losing a device are different emergencies. BitLogin keeps them cryptographically separate.</p>
|
|
<div class="grid">
|
|
<div class="feature">
|
|
<h3>🔑 Password path</h3>
|
|
<p>Login name + password deterministically derive a private "locator" key and an encryption key. That key finds and decrypts a small credential capsule on public relays — no lookup table, no server, no email.</p>
|
|
</div>
|
|
<div class="feature">
|
|
<h3>🌱 Recovery phrase</h3>
|
|
<p>A 12-word phrase derives a completely independent recovery identity. A cracked password can never sign, replace, or delete the recovery capsule — it has no authority over that path at all.</p>
|
|
</div>
|
|
<div class="feature">
|
|
<h3>🪪 One everyday identity</h3>
|
|
<p>Both paths unlock the exact same everyday Nostr keypair — your real, canonical, portable identity. Change your password as often as you like; the identity never moves.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="container">
|
|
<h2>Why this is different</h2>
|
|
<p class="section-sub">Every design choice traces back to one constraint: it has to work with nothing but static files and public relays.</p>
|
|
<div class="grid">
|
|
<div class="feature">
|
|
<h3>Static-first</h3>
|
|
<p>The whole client — crypto, UI, worker — ships as files. Host it anywhere: Pages, IPFS, a folder on a CDN. No backend to run or pay for.</p>
|
|
</div>
|
|
<div class="feature">
|
|
<h3>Relay-redundant storage</h3>
|
|
<p>Encrypted capsules are published to multiple Nostr relays with quorum reads and readback verification, so no single relay is a single point of failure.</p>
|
|
</div>
|
|
<div class="feature">
|
|
<h3>Generated by default, never weak</h3>
|
|
<p>A downloadable encrypted capsule can be attacked offline forever, so BitLogin generates a high-entropy passphrase for you by default. Prefer your own? It's allowed, but it has to clear a real entropy floor and a common-password check — no "password123", ever.</p>
|
|
</div>
|
|
<div class="feature">
|
|
<h3>Argon2id + AES-256-GCM</h3>
|
|
<p>Memory-hard key derivation, authenticated encryption, canonical JSON serialization, and fixed padding buckets so ciphertext size doesn't leak contents.</p>
|
|
</div>
|
|
<div class="feature">
|
|
<h3>Rollback-resistant</h3>
|
|
<p>A local generation high-water mark and monotonic timestamps mean a stale or malicious relay can't quietly roll a logged-in device back to an older capsule.</p>
|
|
</div>
|
|
<div class="feature">
|
|
<h3>NIP-07-compatible signing</h3>
|
|
<p>Once unlocked, BitLogin exposes a <code>window.nostr</code> provider — the same shape browser extensions use. Existing Nostr apps work with zero code changes.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="container narrow">
|
|
<h2>Drop it into any static page</h2>
|
|
<p class="section-sub">One script tag, one custom element. No build step, no framework required.</p>
|
|
<pre><code><script type="module" src="/vendor/bitlogin/bitlogin.js"></script>
|
|
<bitlogin-auth vault-relays="wss://relay.example,wss://relay2.example"></bitlogin-auth></code></pre>
|
|
<p><a href="./docs.html">Read the full integration guide →</a></p>
|
|
</section>
|
|
|
|
<footer>
|
|
BitLogin is an experimental alpha protocol. This demo is a reference implementation, not a production account service.
|
|
</footer>
|
|
|
|
|
|
<script>
|
|
if ("serviceWorker" in navigator) {
|
|
navigator.serviceWorker.register("./sw.js").catch(() => {});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|