Files
bitvid/docs/bitcoin-connect-plan.md
T
thePR0M3TH3ANandClaude Opus 4.8 6dd40ce330 docs: add Bitcoin Connect (NWC connect UX) integration plan + TODO #61
Requested: integrate getAlby/bitcoin-connect as a smooth NWC "connect wallet"
front-end, keeping manual URI entry as backup. Plan (docs/bitcoin-connect-plan.md)
grounds it in bitvid's existing NWC stack: BC's NWC connector exposes the raw
pairing URI (provider.client.nostrWalletConnectUrl) which feeds the existing
validateWalletUri → per-npub store → zap-split/pay_invoice pipeline unchanged.
BC is a connect adapter, not a payment layer. Covers delivery (vendor+lazy import
vs esm.sh; CSP already allows esm.sh), persistence ownership, NWC-only connector
scope, least-privilege requestMethods, UI placement, FEATURE_BITCOIN_CONNECT flag,
and the security constraints (bearer spending secret, Alpha lib pin/vendor).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-09 14:19:17 -04:00

168 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Bitcoin Connect — NWC wallet connect UX — Dev Plan
Status: **DECISIONS NEEDED (D1–D6)** — no code yet. Adds Alby's
[Bitcoin Connect](https://bitcoin-connect.com/) as a smooth "connect your wallet"
front-end for NWC, feeding bitvid's **existing** NWC pipeline. Manual URI entry
stays as the backup/advanced path. Feature-flagged (off = no trace).
## Executive summary
bitvid already has a full NWC stack: manual URI entry (`#walletUri` in the Wallet
Connect pane → `ProfileWalletController`), validation
(`nwcSettingsService.validateWalletUri` — requires `nostr+walletconnect://`),
per-npub storage (`nwcSettings.js`, IndexedDB + localStorage), opt-in encrypted
cross-login sync, and the zap-split/pay_invoice payment pipeline
(`js/payments/*`). The friction is purely the *connect* step — pasting a URI.
Bitcoin Connect solves exactly that: a polished modal that walks the user through
connecting a wallet (CoinOS, Alby Hub, mutiny, etc.) and, for the NWC connector,
**exposes the raw NWC pairing URI**. So we bolt it on as an alternate connect
button that extracts the URI and hands it to bitvid's existing validate→store
pipeline. **No change to how bitvid pays or stores** — one new way to obtain the
same string. Manual entry stays for power users / unsupported wallets.
## How Bitcoin Connect works (verified against the v3 README)
- Package `@getalby/bitcoin-connect` (ESM; Lit web components + functions; bundles
`@getalby/js-sdk` internally — no separate peer dep). Also loadable from
`https://esm.sh/@getalby/bitcoin-connect@<pin>` (our CSP already allows esm.sh).
- `init({ appName, filters: ["nwc"], persistConnection, providerConfig: { nwc: {
authorizationUrlOptions: { requestMethods } } } })` — `filters:["nwc"]` restricts
the modal to NWC; `requestMethods` requests least-privilege scopes.
- `launchModal()` / `await requestProvider()` opens the connect flow.
- `onConnected((provider) => …)` → a WebLN provider. **NWC extraction:**
```js
import { onConnected } from "@getalby/bitcoin-connect";
import { WebLNProviders } from "@getalby/sdk"; // or the re-export
onConnected((provider) => {
if (provider instanceof WebLNProviders.NostrWebLNProvider) {
const uri = provider.client.nostrWalletConnectUrl; // ← the NWC secret
}
});
```
- `disconnect()` clears its connection; `getConnectorConfig()` returns the saved
connector config. Persistence to localStorage is automatic when
`persistConnection: true`.
- ⚠️ README says the lib is **Alpha** — pin an exact version.
## Decisions needed
> **DECISION 1 — Delivery. 🔵 NEEDED.**
> - **A — Vendor + lazy `import()`:** esbuild-bundle a pinned `@getalby/bitcoin-connect`
> into `vendor/bitcoin-connect.bundle.min.js` (same pattern as floating-ui in
> `scripts/build-beacon.mjs`), dynamically imported only when the user opens the
> Wallet pane / clicks Connect. Offline-safe, supply-chain-pinned, no main-bundle bloat.
> - **B — Lazy-load from esm.sh** (CSP already allows it) at a pinned version.
> Zero vendoring work, but a runtime CDN dependency + supply-chain trust.
> _Recommendation: **A** — bitvid vendors its other heavy deps; Bitcoin Connect is
> a Lit bundle (~hundreds of KB) so lazy-import is important either way._
> **DECISION 2 — Persistence ownership. 🔵 NEEDED.**
> - **A — `persistConnection: false`:** Bitcoin Connect is purely the connect UI;
> bitvid extracts the URI and is the ONLY store (its existing encrypted-sync /
> per-npub model). No duplicate plaintext copy in `bc:*` localStorage.
> - **B — let it persist** and rely on `disconnect()` on bitvid logout.
> _Recommendation: **A** — one owner of the spending secret; call `disconnect()`
> right after extracting to be safe._
> **DECISION 3 — Connector scope. 🔵 NEEDED.**
> - **A — NWC only** (`filters:["nwc"]`). Yields a URI that flows straight into the
> existing pipeline; zap-split/pay_invoice unchanged.
> - **B — also allow WebLN / Alby-extension / Alby Hub** connectors. Smoother for
> Alby users, but those give a WebLN *provider*, not an NWC URI — bitvid's raw-NWC
> payment pipeline can't consume them without a second WebLN payment path.
> _Recommendation: **A for v1** (matches the pipeline); a WebLN path is a separate
> later effort if there's demand._
> **DECISION 4 — Requested scopes. 🔵 NEEDED.**
> `requestMethods` to request from the wallet. bitvid needs **`pay_invoice`** (zaps);
> `get_balance` (balance display), `make_invoice` / `lookup_invoice` (receipts/tally).
> _Recommendation: `["pay_invoice","get_balance","make_invoice","lookup_invoice"]` —
> least privilege for what bitvid actually calls. (The wallet ultimately decides.)_
> **DECISION 5 — UI placement vs manual mode. 🔵 NEEDED.**
> _Recommendation: In the Wallet Connect pane, make **"Connect wallet"** (Bitcoin
> Connect) the primary CTA, and move the raw-URI field under a collapsible
> **"Advanced / paste a connection manually"** — manual stays a first-class backup,
> just de-emphasized. On success, populate the same status/disconnect UI._
> **DECISION 6 — Feature flag. 🔵 NEEDED.**
> _Recommendation: `FEATURE_BITCOIN_CONNECT` in `config/instance-config.js` (default
> off = no trace), wired exactly like `FEATURE_AUDIO_INGEST`. With it off, only the
> manual URI field shows — zero new code path or bundle load._
## Config flag (off = no trace)
- `FEATURE_BITCOIN_CONNECT` (default **false**) — gates the "Connect wallet" button
+ the lazy Bitcoin Connect import. Off ⇒ manual URI entry only (today's behavior).
## Integration points (small, contained)
- `components/profile-modal.html` — Wallet Connect pane: add the "Connect wallet"
button; wrap the existing `#walletUri` field as "Advanced/manual."
- `js/ui/profileModal/ProfileWalletController.js` — a `handleBitcoinConnect()` that
lazy-imports the lib, `init()`s once (NWC-only, least-privilege, no-persist),
`launchModal()`, and on `onConnected` extracts `nostrWalletConnectUrl` →
runs the SAME `validateWalletUri` + save path as manual "Save wallet" → then
`disconnect()`. Reuses all existing status/disconnect/sync UI.
- `config/instance-config.js` / `js/config.js` / `js/constants.js` — the flag.
- `scripts/` + `vendor/` — the esbuild vendor bundle (D1-A).
## Architecture
Bitcoin Connect is a **connect adapter**, not a payment layer:
```
[Connect wallet] → BC modal (NWC) → onConnected(provider)
→ provider.client.nostrWalletConnectUrl
→ validateWalletUri() (existing, scheme check)
→ nwcSettingsService save (existing: per-npub store + optional encrypted sync)
→ BC disconnect() (bitvid now owns the secret)
[zaps] → js/payments/nwcClient pay_invoice + zap-split (UNCHANGED)
```
The manual field writes to the exact same validate→store path, so the two entry
points converge immediately and everything downstream is untouched.
## Security (NWC URI = bearer SPENDING secret)
- **Never log** `nostrWalletConnectUrl` (or the provider) — extract → validate →
store via the existing (never-logged) pipeline; treat like the manual field.
- **`persistConnection: false` + `disconnect()`** after extract so the secret isn't
duplicated in Bitcoin Connect's own plaintext `bc:*` localStorage (D2).
- **Pin the version + vendor it** (Alpha lib) — no unpinned CDN `@latest`.
- **Least-privilege `requestMethods`** (D4) — request only what bitvid calls.
- Re-run bitvid's `validateWalletUri` on the extracted string; don't trust the lib
blindly. Keep the explicit "a wallet-connect URI can SPEND" warning on the
encrypted-sync toggle.
- Payment invariants unchanged: `PLATFORM_FEE_PERCENT=30` split, preimage
verification, and the "a pay_invoice timeout must NEVER auto-resend" rule all live
in `js/payments/*` and are not touched.
## Phases (flag-gated from day one)
- **Phase 0 — Flag + vendor bundle.** Add `FEATURE_BITCOIN_CONNECT` (off) and the
esbuild vendor step. No UX.
- **Phase 1 — Connect button (headline).** Wallet pane "Connect wallet" → BC NWC
modal → extract → existing save path → disconnect. Manual field → "Advanced."
- **Phase 2 — Polish.** Balance display (BC `showBalance` / get_balance), reconnect
affordance, and a "test connection" ping. Optional.
- **Phase 3 (only if wanted) — WebLN path** (D3-B): accept Alby-extension/Hub
connectors via a WebLN payment adapter alongside raw NWC. Separate effort.
## Risks / watch-items
- **Alpha library / breaking changes** — pin + vendor; wrap behind our own thin
adapter so an API change is a one-file fix.
- **Bundle weight** — lazy-import only on Wallet-pane open / button click.
- **Wallet grants fewer scopes than requested** — if `pay_invoice` isn't granted,
zaps fail; surface a clear error and fall back to manual.
- **Not all wallets support NWC** — manual mode + non-NWC connectors (D3-B) cover the gap.
- **esm.sh path (D1-B)** would add a runtime third-party origin to the trust surface.
## Sources
- getAlby/bitcoin-connect README (v3) — install, `init` filters/providerConfig,
`onConnected`, `NostrWebLNProvider.client.nostrWalletConnectUrl`, `disconnect`.
- bitcoin-connect.com. Related bitvid: `js/services/nwcSettingsService.js`,
`js/nwcSettings.js`, `js/ui/profileModal/ProfileWalletController.js`,
`js/payments/*`, `docs/audio-integration-plan.md` (flag-wiring pattern).