mirror of
https://github.com/PR0M3TH3AN/bitlogin.git
synced 2026-10-28 00:51:23 +00:00
Outside review flagged BitLogin as highest-upside/highest-risk. Record the guidance: external cryptographic review as a gate before 'use for real' positioning; keep copy off impossibility claims; hold the passphrase-entropy and Argon2id/AES-GCM choices. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01BXQCvJPA6sVSQ5j8uBd9bB
456 lines
25 KiB
Markdown
456 lines
25 KiB
Markdown
# BitLogin — open items
|
||
|
||
Updated: 2026-08-04
|
||
|
||
## Login-surface expansion (2026-08-04) — shipped, with owner follow-ups
|
||
|
||
One login surface, four methods (`login-methods.md`, `passkey-login.md`):
|
||
password (primary), NIP-07 extension, NIP-46 remote signer (memory-only),
|
||
and passkey via WebAuthn PRF. OAuth on-ramps were built, then removed the
|
||
same day for requiring per-site provider registration
|
||
(`centralized-onramps.md`, superseded, kept on the record). Also shipped:
|
||
the deployment-CSP `'wasm-unsafe-eval'` fix (live password flows were
|
||
broken since 2026-07-31), the service-worker `cache: "reload"` precache
|
||
fix, `window.bitlogin.activeMethod()/activeSession()`, and welcome/sign-in
|
||
UI polish.
|
||
|
||
## Security audit response (2026-08-04, same day)
|
||
|
||
All confirmed findings fixed and regression-tested: BL-16 (auth_url now
|
||
https-only via `sanitizeAuthUrl`, hostname shown on the link, hostile-scheme
|
||
tests), BL-17 (post-creation PRF fallback pinned via `allowCredentials` to
|
||
the created credential), BL-18 (dead-socket CLOSE can no longer strand a
|
||
query promise), BL-19 (local `filter.limit` enforcement + duplicate-id drop
|
||
+ 1000-event ceiling per buffered query), BL-20 (.semgrep.yml try/catch
|
||
pattern was unparseable — fixed and verified firing on hostile code under
|
||
the pinned Semgrep 1.171.0). Lows: NIP-07 and NIP-46 now verify returned
|
||
signed events against the request (signature, identity, field equality);
|
||
`Nip46Client.close()` wipes the client and conversation keys; passkey
|
||
labels are distinguishable; postcss ≥8.5.25; Actions pinned to commit SHAs.
|
||
|
||
Second-pass response (same day): BL-21 — bunker relays capped at 8 and
|
||
deduplicated; relay frames over 256K chars dropped before JSON.parse; all
|
||
cheap checks (id dedup, capacity) run before Schnorr verification, plus a
|
||
per-query verification budget. BL-22 — superseded/abandoned nostrconnect
|
||
attempts are cancelled via AbortSignal and their ephemeral keys wiped
|
||
(ownership transfers, not dangles, on success); per-candidate conversation
|
||
keys wiped in a finally. BL-23 — `security:ci` now runs the custom Semgrep
|
||
rules (`security:semgrep`; requires a local semgrep install, same pin as
|
||
CI). BL-24 — the courtesy protocol `logout` (current NIP-46 defines it,
|
||
explicitly not a security boundary) is now sent on teardown; this corrects
|
||
the earlier "no stable method name" note. Still deferred, interop only:
|
||
`switch_relays`.
|
||
|
||
Third-pass response (same day): BL-25 — live subscriptions now carry a
|
||
bounded already-seen id cache (2048, FIFO eviction; a replayed retained
|
||
event costs one verification and one delivery, ever) and a fixed-window
|
||
verification budget (100 per 10s; distinct-event spray is quarantined for
|
||
the window). BL-26 — teardown order inverted: the courtesy logout event is
|
||
pre-signed, then close() IMMEDIATELY rejects every in-flight request and
|
||
wipes keys, then the pre-signed event is delivered asynchronously and
|
||
bounded — nothing can resolve after logout begins (regression-tested).
|
||
BL-27 — a nostrconnect listen failing naturally (timeout, unreachable
|
||
relays) now discards the pending ephemeral key in a guarded catch instead
|
||
of orphaning it. BL-28 — the hosted verify job installs the pinned Semgrep
|
||
before running security:ci (it was red at the previous HEAD, skipping SBOM
|
||
and checksums). Informational — bunker URIs are bounded: 4096 chars total,
|
||
256-char secret, 512-char relay URLs (oversized relays filtered, oversized
|
||
URI/secret rejected).
|
||
|
||
Owner action items:
|
||
|
||
1. **Live-fire the NIP-46 QR with Amber** on bitlogin.network (flow was
|
||
verified against a fake bunker in tests, not yet against a real phone
|
||
signer end-to-end).
|
||
2. **Live-fire passkey sign-in on a real device.** *Partly discharged
|
||
2026-08-04:* `packages/widget/src/e2e/passkey.e2e.test.ts` drives real
|
||
headless Chromium through the real WebAuthn ceremonies with a CDP
|
||
virtual authenticator (`hasPrf: true`), against the built demo and
|
||
in-process mock relays — create passkey → register → dashboard npub →
|
||
capsules verified on the relays → logout → sign in with the same passkey
|
||
→ same npub; plus a second passkey deriving a different account. Runs in
|
||
`npm test`, skips automatically without `npx playwright install
|
||
chromium`. What that still does NOT cover: a real platform authenticator
|
||
(Google Password Manager, iCloud Keychain, Bitwarden, a hardware key)
|
||
and the create-time-PRF-absent fallback path, which Chromium's virtual
|
||
authenticator does not exercise (it returns PRF results at creation).
|
||
3. **Passkey derivation is a new compatibility contract** —
|
||
`packages/widget/src/passkey.test.ts` known-answer vectors carry the
|
||
same never-update-the-expected-value rule as `knownAnswer.test.ts`.
|
||
4. NIP-46 credential *persistence* (vault record) stays gated on the
|
||
external security review, as before.
|
||
|
||
## Entropy audit (2026-07-31)
|
||
|
||
[`entropy-audit-2026-07-31.md`](./entropy-audit-2026-07-31.md) — RNG and
|
||
randomness-integration audit. **No predictable-fallback defect:** `random.ts`
|
||
is the single chokepoint, throws rather than degrading, and rejection-samples to
|
||
avoid modulo bias. Three hardening items, in order:
|
||
|
||
- **M2 — done.** Nine tests in `packages/core/src/crypto/crypto.test.ts` pin
|
||
fail-closed behaviour and `randomUniformInt`'s uniformity. Mutation-verified:
|
||
a zero-filling stub in place of the throw turns five of them red, and
|
||
swapping rejection sampling for bare modulo gives chi-square 5614 against a
|
||
threshold of 147.
|
||
- **M1 — done.** `nip44Encrypt` / `nip04Encrypt` no longer take a nonce or IV;
|
||
the capability lives in the non-exported `crypto/testing.ts`, and
|
||
`crypto/index.ts` re-exports explicitly instead of `export *`. Only one test
|
||
ever used an override and `ivOverride` had no callers, so nothing was lost.
|
||
Verified from a real consumer against the built package: all three deep-import
|
||
routes fail with `ERR_PACKAGE_PATH_NOT_EXPORTED`. `knownAnswer.test.ts` green
|
||
throughout — the derivation chain is untouched.
|
||
- **L1 — done.** Both `Math.random` calls (backup-quiz picker, relay subscription
|
||
id) now use the CSPRNG, so the ban has no standing exception. `.semgrep.yml`
|
||
enforces it in CI alongside two more rules: no fallback around
|
||
`getRandomValues`, and no timestamp-seeded secrets. Verified the rules fire —
|
||
reintroducing the old call gives 1 blocking finding and exit 1.
|
||
|
||
**The entropy audit is closed for this repo.** The only gate left is an
|
||
independent review of the RNG path by someone other than the author, which is
|
||
not something a coding agent can sign off.
|
||
|
||
## Owner action items
|
||
|
||
1. **Close the Dependabot PR on
|
||
`dependabot/npm_and_yarn/npm-security-and-maintenance-83024bc549`.** It
|
||
bundles seven major bumps into one change (`@noble/hashes` 1.8→2.2,
|
||
`@noble/curves` 1.9→2.2, `@scure/base` 1.2→2.2, `@scure/bip39` 1.6→2.2,
|
||
TypeScript 5→7, Vite 6→8, Vitest 3→4) and fails the build: noble v2 dropped
|
||
the extensionless subpath exports this code imports. It should be closed,
|
||
not merged or fixed — `.github/dependabot.yml` now groups only minor/patch
|
||
and ignores majors of the derivation chain, so this PR will not recur.
|
||
|
||
The four `dependabot/github_actions/*` PRs are unaffected and fine to merge.
|
||
|
||
2. **Decide whether the manual-password removal is final.** User-chosen
|
||
passwords were removed entirely (not merely discouraged) in the 2026-07-31
|
||
hardening, and `packages/widget/src/securityPolicy.test.ts` pins that. The
|
||
security argument is sound — a user-chosen password against a downloadable
|
||
capsule is offline-guessable forever — but it is a product decision that
|
||
changes what users can do, and it is now enforced by a test rather than by
|
||
preference.
|
||
|
||
## Standing rule: the derivation chain is a compatibility contract
|
||
|
||
Account capsules live on public relays. The login name plus password derive the
|
||
locator, signing scalar and capsule key deterministically, so those outputs are
|
||
a contract with every account that already exists — change them and users are
|
||
locked out permanently, with no server-side record to migrate from.
|
||
|
||
`packages/core/src/crypto/knownAnswer.test.ts` pins HKDF-Extract, HKDF-Expand,
|
||
ScalarExpand (scalar, counter, and resulting public key), BIP-39 phrase and
|
||
seed, base64url framing, and JCS ordering.
|
||
|
||
**A failure there is never "update the expected value".** It means the change
|
||
being attempted is account-breaking and needs a migration plan or rejection.
|
||
The rest of the crypto suite only checks self-consistency — same input, same
|
||
output within one run — which stays green through exactly the kind of upgrade
|
||
that would lock everyone out.
|
||
|
||
## Deferred hardening
|
||
|
||
Carried from the 2026-07-30 audit; none are live defects.
|
||
|
||
- **Tier 2** — `packages/widget/src/element.ts` is ~1,600 lines with no direct
|
||
test coverage. `securityPolicy.test.ts` now asserts a few properties of its
|
||
source text, which is a start, not coverage.
|
||
- **Tier 3** — compare-and-swap on capsule writes (relays offer no atomic
|
||
primitive, so this needs a protocol-level answer), and an encrypted
|
||
high-water-mark store.
|
||
- **Tier 4** — moving high-value authorization into a cross-origin or extension
|
||
boundary. An embedded widget is same-origin with its host page, so a
|
||
compromised host can reach the worker directly. Documented honestly in
|
||
`llms.txt` and the README rather than papered over.
|
||
|
||
## Gates still in force
|
||
|
||
- **External security review before the vault holds anything beyond a
|
||
budget-capped NWC connection.** The S3 and password profiles stay designs
|
||
until then.
|
||
- Personal-tier UI was gated on the sudo key leaving the credential capsule.
|
||
That gate is now **met** — the sudo key lives only in the phrase-gated
|
||
recovery capsule — but the UI still needs a separately reviewed key
|
||
ceremony before it ships.
|
||
|
||
## Additive browser distribution and integrator API roadmap
|
||
|
||
These are additive compatibility goals. The existing multi-file build,
|
||
`<bitlogin-auth>` behavior, `window.nostr` provider, derivation chain, session
|
||
formats, and current consuming applications are compatibility contracts. Do
|
||
not replace or silently alter them while adding standalone distribution or a
|
||
new public API. Every release must continue to produce and test the standard
|
||
build before the standalone build is considered successful.
|
||
|
||
### Priority 1 — Distribution
|
||
|
||
- [ ] Add a standalone browser build target: `npm run build:standalone`.
|
||
- [ ] Produce one self-contained `bitlogin.js` for standalone consumers.
|
||
- [ ] Keep the standard multi-file build unchanged and available alongside it:
|
||
`dist/standard/` and `dist/standalone/`.
|
||
- [ ] Remove standalone runtime dependence on external `cryptoWorker.js`,
|
||
hashed shared chunks, or other sibling files; preserve Web Worker
|
||
isolation internally where possible using a Blob/module worker.
|
||
- [ ] Support this minimal static-site integration:
|
||
`<script type="module" src="./bitlogin.js"></script>` and
|
||
`<bitlogin-auth></bitlogin-auth>`.
|
||
- [ ] Verify standalone hosting from arbitrary static HTTPS origins, including
|
||
GitHub Pages, Cloudflare Pages, Netlify, Vercel, and ordinary nginx.
|
||
- [ ] Verify localhost development usage.
|
||
- [ ] Document `file://` limitations clearly, including the recommended local
|
||
HTTPS or local development-server path.
|
||
- [ ] Define CSP requirements for standalone Blob/module workers and report
|
||
incompatible policies instead of silently degrading crypto isolation.
|
||
|
||
### Priority 2 — Developer API
|
||
|
||
- [ ] Define and document one canonical signer interface with capability-based
|
||
methods: `getPublicKey()`, `signEvent()`, `getRelays()`, and supported
|
||
`nip04.encrypt/decrypt`, `nip44.encrypt/decrypt`, and NWC operations.
|
||
- [ ] Keep `window.nostr` compatibility, including its existing NIP-07-shaped
|
||
behavior, as an adapter over the canonical signer rather than a breaking
|
||
replacement.
|
||
- [ ] Expose a clean additive `window.bitlogin` API, including
|
||
`activeSession()`, `login()`, `logout()`, and `getSigner()`.
|
||
- [ ] Add and document stable lifecycle event aliases: `bitlogin:ready`,
|
||
`bitlogin:login`, `bitlogin:logout`, and `bitlogin:error`. Preserve the
|
||
existing hyphenated events (`bitlogin-login`, `bitlogin-logout`, and
|
||
related current event names) for compatibility; the colon-named events
|
||
must be additive and must not replace them silently.
|
||
- [ ] Version and test event payloads; preserve existing event names and
|
||
payload fields used by current apps while adding new fields additively.
|
||
- [ ] Expose capability detection, for example `signer.capabilities()`, for
|
||
`signEvent`, `nip04`, `nip44`, `nip46`, `nwc`, and `passkey`.
|
||
- [ ] Ensure capability reporting describes the active session and never
|
||
implies support for a method that the active signer cannot perform.
|
||
|
||
### Priority 3 — Embedding and UI
|
||
|
||
- [ ] Support a headless mode so applications can use BitLogin without
|
||
rendering `<bitlogin-auth>`.
|
||
- [ ] Keep `<bitlogin-auth>` as the default easy-to-integrate UI.
|
||
- [ ] Allow applications to open the login dialog programmatically, including
|
||
`await bitlogin.login()`.
|
||
- [ ] Allow configurable login methods, for example
|
||
`<bitlogin-auth methods="passkey,password,nip07,nip46"></bitlogin-auth>`.
|
||
- [ ] Add basic theming hooks: CSS custom properties, light/dark support,
|
||
accent color, and compact mode.
|
||
- [ ] Expose supported configuration through documented attributes/options;
|
||
do not require integrators to reach into widget internals or private DOM.
|
||
- [ ] Preserve current login-method defaults and existing method ordering unless
|
||
an explicit versioned compatibility decision says otherwise.
|
||
|
||
### Priority 4 — Build, versioning, and releases
|
||
|
||
- [ ] Give browser artifacts predictable filenames: `bitlogin.js` and,
|
||
if useful, `bitlogin.min.js`.
|
||
- [ ] Expose version information through `window.bitlogin.version` and the
|
||
module/API surface where applicable.
|
||
- [ ] Emit integrity/hash manifests for releases and document exactly which
|
||
artifact each hash covers.
|
||
- [ ] Create downloadable release artifacts: `bitlogin-standard.zip` and
|
||
`bitlogin-standalone.js`.
|
||
- [ ] Publish release notes specifically for integrators, including changed
|
||
browser requirements, CSP requirements, API additions, and compatibility
|
||
notes.
|
||
- [ ] Evaluate public package exports for the widget, standalone entry point,
|
||
and core package (for example `@bitlogin/widget`,
|
||
`@bitlogin/widget/standalone`, and `@bitlogin/core`) without exposing
|
||
unsupported private paths or weakening package encapsulation.
|
||
|
||
### Priority 5 — Integration and compatibility testing
|
||
|
||
- [ ] Add a minimal vanilla HTML integration test for both standard and
|
||
standalone builds.
|
||
- [ ] Test standalone flows for password account, passkey account, recovery
|
||
phrase, NIP-07 extension, and NIP-46 bunker.
|
||
- [ ] Test `getPublicKey()`, `signEvent()`, `logout()`, and session restore
|
||
through both the canonical signer and the compatibility adapter.
|
||
- [ ] Test signed kind-1 publication against real or mock relays.
|
||
- [ ] Test multiple BitLogin-enabled applications on separate origins and
|
||
confirm origin-scoped storage/session behavior remains isolated.
|
||
- [ ] Test CSP compatibility, including the documented Blob/module-worker
|
||
requirements and a fail-closed result when a required policy is absent.
|
||
- [ ] Test Blob Worker behavior across Chromium, Firefox, and Safari.
|
||
- [ ] Test passkeys on real platform authenticators in addition to the existing
|
||
virtual-authenticator coverage.
|
||
- [ ] Run the existing derivation-chain known-answer tests and regression suite
|
||
unchanged; never update expected values to make a compatibility failure
|
||
pass.
|
||
- [ ] Verify current consuming applications (including AdZap, Satisfied,
|
||
BitUnlock, BitRoad, and Bitvid where their checkouts are available) still
|
||
load the standard artifact and retain login, signing, logout, and session
|
||
restore behavior.
|
||
- [ ] Add a release gate that builds/tests the standard artifact before and
|
||
after standalone bundling, proving standalone work cannot replace or
|
||
mutate the standard output accidentally.
|
||
|
||
### Priority 6 — Documentation
|
||
|
||
- [ ] Put a "30-second integration" section at the top of the README.
|
||
- [ ] Provide a complete single-page standard and standalone example.
|
||
- [ ] Provide a headless example.
|
||
- [ ] Provide a Nostr publishing example:
|
||
`const signer = await bitlogin.getSigner();` followed by
|
||
`const event = await signer.signEvent(...);`.
|
||
- [ ] Provide a migration guide from the standard build to the standalone
|
||
build, with a rollback path that requires no application-code rewrite.
|
||
- [ ] Clearly explain the security tradeoff between the standard worker/chunk
|
||
build and the standalone bundled build.
|
||
- [ ] Clearly state that standalone is a distribution convenience, not a
|
||
stronger security boundary; an embedding host remains trusted and
|
||
same-origin with the widget unless a separate isolation architecture is
|
||
used.
|
||
|
||
### Definition of done
|
||
|
||
A developer can download one JavaScript file, add approximately 3–5 lines of
|
||
HTML/JavaScript, and obtain a real BitLogin-backed Nostr signer without npm,
|
||
Vite, React, or a backend. Existing standard-build consumers continue to work
|
||
without source changes, and the compatibility and security claims above are
|
||
backed by automated tests and release artifacts.
|
||
|
||
### Context warnings to resolve
|
||
|
||
- [ ] Replace unresolved package references such as `@file:bitlogin/widget`,
|
||
`@file:bitlogin/widget/standalone`, and `@file:bitlogin/core` with actual
|
||
documented package paths or mark them as examples only; do not publish
|
||
links to files or exports that do not exist.
|
||
|
||
## BitBlocks App Kit compatibility layer
|
||
|
||
Do this only after BitLogin, BitGate, and BitFeed each have clean standalone
|
||
APIs. BitBlocks is an optional integration layer, not a replacement for any of
|
||
the three libraries. It must not merge their implementations or make an app
|
||
depend on the kit when it only needs BitLogin.
|
||
|
||
- [ ] Create a tiny optional BitBlocks integration layer.
|
||
- [ ] Keep BitLogin, BitGate, and BitFeed as separate replaceable libraries;
|
||
the App Kit must compose them rather than absorb them.
|
||
- [ ] Provide shared initialization, for example:
|
||
|
||
const app = await BitBlocks.create({
|
||
login: {},
|
||
gate: {},
|
||
feed: {},
|
||
relays: [...]
|
||
});
|
||
|
||
- [ ] Standardize the viewer context exchanged between the components without
|
||
changing BitLogin's signer/session contract.
|
||
- [ ] Centralize optional common Nostr plumbing in the kit: relay connections,
|
||
subscriptions, EOSE handling, reconnects, deduplication, timeouts,
|
||
publishing, OK responses, profile lookup, follow-list lookup, and
|
||
mute-list lookup.
|
||
- [ ] Expose `app.viewer`, `app.signer`, `app.relays`, `app.gate`, and
|
||
`app.feed` through documented interfaces.
|
||
- [ ] Keep each underlying library independently usable and independently
|
||
versioned.
|
||
- [ ] Do not make applications depend on BitBlocks when they use only
|
||
BitLogin's identity/signing capabilities.
|
||
- [ ] Add integration tests proving the kit is an adapter over the standalone
|
||
APIs, not a second implementation of identity, policy, or feed logic.
|
||
|
||
### BitBlocks definition of done
|
||
|
||
A developer or AI agent can take `bitlogin.js`, `bitgate.js`, `bitfeed.js`, and
|
||
`bitblocks.js` and build a functional static Nostr application with almost no
|
||
infrastructure or integration boilerplate, while each individual library
|
||
remains usable without BitBlocks.
|
||
|
||
## Feedback from integrating into Tessera (2026-08-11)
|
||
|
||
Notes from embedding `@bitlogin/widget` in the Tessera static site. Grouped by
|
||
how much they cost.
|
||
|
||
### High-value
|
||
|
||
- **[DONE 2026-08-11] Lightweight session-state read without the full widget.**
|
||
The single biggest cost: to know *whether a user is signed in* on a page, you
|
||
used to have to mount the whole widget (bitlogin.js + crypto worker + wasm,
|
||
~1.6MB) because it was the only thing that restored the per-origin session.
|
||
**Shipped:** the widget now writes a cleartext, public-only record
|
||
(`{ pubkey, npub, name? }` — never key material) beside its encrypted session
|
||
in IndexedDB and clears it on logout, and a new `@bitlogin/widget/session`
|
||
entry point (`bitlogin-session.js`, ~1 KB gzip, zero deps, no worker/wasm)
|
||
exposes `readCachedSession(): Promise<{ pubkey, npub, name? } | null>`. The
|
||
same call is mirrored on `window.bitlogin.readCachedSession()` for pages that
|
||
already load the full widget. See `src/publicSession.ts`, `src/session.ts`,
|
||
and the README "Reading who's signed in without loading the widget" section.
|
||
Remaining polish: have the widget also record the profile display `name` once
|
||
fetched (currently `name` is only set when the caller supplies it), and add an
|
||
`@bitlogin/widget/session` `.d.ts` (tracked under the Small item below).
|
||
- **[DONE 2026-08-11] A headless session-restore API.** Shipped
|
||
`@bitlogin/widget/headless` (`bitlogin-headless.js`): `createHeadlessSigner()`
|
||
→ `restore()` / `getPublicKey()` / `signEvent()` / `nip44Encrypt|Decrypt` /
|
||
`isUnlocked()` / `logout()` / `destroy()`, a DOM-free wrapper over the crypto
|
||
worker. It restores the cached session and signs with no custom element and no
|
||
`window.nostr`; it deliberately can't interactively log in (`restore()`
|
||
returns null when nothing is cached → mount `<bitlogin-auth>` once). Covered by
|
||
`headless.test.ts` (scripted-worker); also re-exported from `@bitlogin/widget`;
|
||
README "Signing without the widget (headless)". Deploy alongside
|
||
`cryptoWorker.js` + shared chunks.
|
||
|
||
### Medium
|
||
|
||
- **[DONE 2026-08-11] The unmount-kills-the-worker footgun now has a guardrail.**
|
||
The docs warned that removing `<bitlogin-auth>` tears down the crypto worker
|
||
and hangs every later signing call with no error. **Shipped both asks:**
|
||
(a) `disconnectedCallback` now logs a `console.error` when the element is
|
||
removed while a session is active, naming the unmount and pointing at the
|
||
README "Integration gotcha"; and (b) `WorkerClient.terminate(reason)` records
|
||
the reason and `signEvent`/`getPublicKey`/etc. — whether in-flight at teardown
|
||
or issued afterward — reject with that message instead of hanging (the
|
||
`dead`-flag path already prevented the infinite hang; this makes the *cause*
|
||
legible and unifies the message). Covered by `worker/workerClient.test.ts`;
|
||
README gotcha paragraphs updated to say "rejects loudly," not "hangs forever."
|
||
- **[DONE 2026-08-11] Styling hooks / CSS parts.** The widget renders in a
|
||
shadow root, so a host could recolor it via the `:host` custom properties but
|
||
couldn't restyle structure. **Shipped:** a stable set of `::part()` hooks —
|
||
`card`, `button` (+`button-primary`/`button-secondary`), `link`, `input`,
|
||
`checkbox`, `heading`, `text`, `notice` (+`notice-warn`/`-info`/`-error`) —
|
||
stamped centrally per render in `element.ts` `exposeParts()` (mapped by class
|
||
so new elements stay exposed without drift). Documented as a contract in the
|
||
README "Theming" section. Verified in-browser that
|
||
`bitlogin-auth::part(...)` matches through the shadow boundary. Custom
|
||
properties remain the first tool for color/radius/font; parts are the escape
|
||
hatch for structure (padding/border/shadow/shape).
|
||
- **[RESOLVED — already the behavior] Session-change event on restore.**
|
||
Confirmed: `bitlogin-login` already fires on silent session restore across a
|
||
page load, not just interactive login, so a host listener needn't distinguish
|
||
the two (README "Sessions persist across a page reload"). No new event needed.
|
||
For pages that don't mount the widget at all, use `readCachedSession()` above.
|
||
|
||
### Small
|
||
|
||
- **[DONE 2026-08-11]** Ship a `.d.ts` (or documented event/method surface) for
|
||
the element. **Shipped:** the widget now emits type declarations from source
|
||
(`tsconfig.types.json` → `dist-types/`, wired via `build:types` and the
|
||
package `types`/`exports` conditions for both `@bitlogin/widget` and
|
||
`@bitlogin/widget/session`) — zero-drift because they're generated, not
|
||
hand-written. Added the type surface integrators actually want: a
|
||
`HTMLElementTagNameMap["bitlogin-auth"]` augmentation (typed
|
||
`querySelector`/`createElement`), typed `addEventListener` overloads via a
|
||
declaration-merged interface, and exported event-detail types
|
||
(`BitLoginLoginEventDetail` etc. + `BitLoginEventMap`) in `src/events.ts`.
|
||
Verified with a consumer conformance typecheck; README "TypeScript" section
|
||
added.
|
||
|
||
## Design review — external critique (2026-08-11)
|
||
|
||
From an outside review of the BitBlocks stack. BitLogin was called the highest
|
||
upside AND highest risk in the stack.
|
||
|
||
- **[agree] Highest assurance bar.** A BitLogin failure is identity compromise,
|
||
not "the feed looked weird." Keep promotion conservative and the
|
||
alpha/unaudited labeling prominent until an outside cryptographic/security
|
||
review. Treat an external audit as a **gate** before any "use this for real"
|
||
positioning — this is the one block where marketing must trail assurance.
|
||
- **[agree] No impossibility claims in copy.** Relay retention is best-effort;
|
||
our own docs already say to keep the recovery export. Keep the site's language
|
||
on "portable / no single operator controls," never "cannot be deleted." (See
|
||
the umbrella claim-precision note.)
|
||
- The choices the review praised — high-entropy passphrase (not raw human
|
||
passwords), Argon2id + AES-256-GCM, `window.nostr` compatibility so Nostr
|
||
"disappears into the background" — are the right ones. Hold that line; don't
|
||
soften the passphrase-entropy stance for UX.
|