diff options
| author | Adam Soltys <[email protected]> | 2026-10-05 22:35:56 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-05 22:35:56 -0700 |
| commit | f571e46937d423d9ba44154a92aeb4136466daaa (patch) | |
| tree | 658553eb48d4d85e632499f6711eda76071c9ba1 /README.md | |
| parent | a08f48d67ad61eba5077fdb1a01d19fe5eea0fdb (diff) | |
| download | coinosv3-f571e46937d423d9ba44154a92aeb4136466daaa.tar.gz coinosv3-f571e46937d423d9ba44154a92aeb4136466daaa.zip | |
README rewritten for today's coinos v3; build switches are COINOS_* (HAL_* still accepted); silent-payment leftovers removed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YRC6aKhwAud231ot9FPmhi
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 350 |
1 files changed, 229 insertions, 121 deletions
@@ -1,102 +1,196 @@ # coinos v3 -A self-contained, **single-file** Bitcoin wallet that runs entirely in the -browser. Think bitaddress.org, but a modern BIP84 HD wallet from a seed phrase -that scans history, watches for payments, spends on-chain, pays Lightning -invoices, and holds an off-chain balance on **Ark**. The whole wallet is one -static `index.html` you can save and run offline, forever. +A self-custodial Bitcoin wallet and Nostr client that runs entirely in the +browser. Your keys are made and kept on your device; there is no account, +email or password. One app holds an on-chain **Savings** balance, an +off-chain **Spending** balance on [Ark](https://ark-protocol.org) with +Lightning built in, a `[email protected]` payment address, and a full Nostr +social client: feeds, zaps, private messages and group chats. -Live at **https://v3.coinos.io** (the built `index.html` also runs straight -from the filesystem with no server). +- Live at **https://v3.coinos.io** (installable as a PWA) +- Android: [Zapstore](https://zapstore.dev), a direct APK at + `v3.coinos.io/coinos.apk`, and an F-Droid build (`io.coinos.fdroid`, in review) +- Offline: `standalone.html` is the whole wallet in one file you can save and + open from disk ## Features -### Core (always included) +### Wallet core (always included) -- **BIP84 / native SegWit (p2wpkh)** HD wallet from a 12-word BIP39 seed - (imports any valid BIP39 phrase; optional passphrase). -- **Per-wallet networks**: each wallet is stamped with its network (mainnet, - testnet, signet, mutinynet, regtest); switching wallets switches networks, - and wallets on different networks coexist in one session. -- **One fresh address at a time**: a new receive address is only handed out - after the current one is paid, so used addresses stay contiguous and scans - stay tiny (no 20-address gap probing). -- **Choose your data source**: Electrum-over-WebSocket servers or Esplora - REST explorers, per network, with silent failover, or point it at your own - node. -- **Real-time** incoming-payment push over WebSocket where the source supports - it, with polling + periodic reconcile as the fallback. Replaced (RBF) - transactions are detected and pruned from history automatically. -- **Spending** with proper coin selection + fee estimation - (`@scure/btc-signer`), multiple recipients, a fee-rate picker, send-max, - manual coin control, and a QR scanner (address / BIP21 / signed tx). +- **BIP84 / native SegWit** HD wallet from a 12-word BIP39 seed (any valid + phrase imports; optional passphrase). +- **Watch-only wallets** from an account xpub/zpub: receive and view history + without the seed; load the seed later to spend. - **Offline / air-gapped signing**: export a keyless snapshot on an online - device, sign on an offline one, broadcast anywhere. -- **Installable PWA**, global sats/BTC toggle, 19 languages. + device, sign on an offline one, and carry the signed transaction back by QR + to broadcast. +- **Per-wallet networks**: mainnet, testnet, signet, mutinynet or regtest, + stamped on each wallet, so wallets on different networks coexist. +- **Choose your data source**: Electrum-over-WebSocket servers + (`electrum.coinos.io` by default) or Esplora REST explorers, with failover, + or your own node. +- **Real-time receives** over WebSocket where the source supports it, polling + as the fallback; replaced (RBF) transactions are pruned automatically. +- **Sending** with coin selection, fee estimation, a fee-rate picker, + send-max, multiple recipients and a camera QR scanner (addresses, BIP21, + invoices, signed transactions). +- **One fresh address at a time**, so scans stay small (no gap-limit probing). +- **Fiat** balances and history values in your currency, with the rate at the + time of each payment. +- 20 languages. -### Ark: instant off-chain payments (feature: `ark`) +### Spending on Ark (feature: `ark`) -A **native-JS Ark client** (speaks gRPC-web + MuSig2 directly to -[bark/captaind](https://github.com/ark-bitcoin/bark) servers: no WASM, no new -dependencies, ~2k lines): +A native-JS client for [bark/captaind](https://gitlab.com/ark-bitcoin/bark) +Ark servers: gRPC-web and MuSig2 spoken directly, no WASM. coinos runs +`ark.coinos.io`; other captaind servers can be chosen. -- **Board** on-chain coins into an Ark server; **send and receive instantly** - off-chain with zero mining fees; server fees shown before anything moves. -- **Refresh / consolidate** vtxos (the round-based maintenance that resets - expiry and chain depth). -- **Collaborative offboard**: move the whole Ark balance back to your own - on-chain address in one server-cosigned transaction. -- **Unilateral exit**: the trustless backstop: publish the vtxo's pre-signed - transaction chain on-chain (CPFP-bumped, TRUC-aware, one confirmed hop at a - time), wait out the CSV timelock, and claim with your key alone. Works even - if the server is gone; live progress (hop counter, blocks-left countdown) in - Settings. -- **Ark gifts**: bearer gift links whose secret *is* the link: claimed - instantly and free, even into a brand-new empty wallet with zero on-chain - footprint. Revocable until claimed. -- Crash-safe by construction: every multi-step ceremony checkpoints to storage - around each server round-trip, and local state reconciles against the - server's authoritative vtxo status before money moves. +- **Instant off-chain payments** between Ark users, with fees shown before + anything moves (free between coinos wallets). +- **Lightning in and out of the Ark balance** through the Ark server's own + Lightning node (HTLC-locked VTXOs), with no swap service. Routing fees are + quoted before you pay. +- **Board** on-chain coins into Spending and **offboard** back to Savings or + any on-chain address in one cooperative transaction. +- **Automatic renewal** before coins expire, plus a server-side scheduled + backstop that renews them even while the app is closed. +- **Unilateral exit**: publish the coins' pre-signed transaction chain + (CPFP-bumped, TRUC-aware) and claim after the timelock with your key alone, + even if the server is gone. Progress is shown live. +- **Full receive-side validation** of every VTXO's transaction chain, and + crash-safe ceremonies that checkpoint around every server round-trip. +- Ark state lives in IndexedDB, fronted by memory, and is reconciled against + the server before money moves. -### Lightning (feature: `swaps`) +### Payment addresses (feature: `names`) -- Pay bolt11 invoices and receive over Lightning via **Boltz-compatible - submarine swaps**, non-custodial either way: a provider can fail a swap but - never take funds (claims/refunds are enforced on-chain). Pick a provider per - network or point at your own. +- **`[email protected]`**: a name claimed on first run that works as a + Lightning address (LNURL-pay) and as a **BIP-353** DNS payment address + (`₿[email protected]`, a DNSSEC-signed TXT record). +- Paying a BIP-353 name validates its **DNSSEC chain in the browser**. +- **BOLT12 offers**: pay `lno1…` offers (invoice signatures checked + client-side) and publish your own offer in your DNS record. +- **CLINK** offer codes (`noffer1…`): request invoices over Nostr. +- **Moving from coinos.io**: existing coinos.io users can bring their + username over; payments to the legacy account are forwarded to the new + wallet. + +### Zaps and Nostr Wallet Connect (features: `zaps`, `nwc`) + +- **NIP-57 zaps** to any npub, Lightning address or LNURL, with one-tap zaps + in the feed and a custom-amount screen on long press. +- **coinos as an NWC wallet service (NIP-47)**: connect Damus, Amethyst, + Primal and others, with per-connection keys and budgets. +- **Background answering**: a Web Push from `nwcpush` wakes the service + worker, which pays and replies from the browser even with the app closed + (keys never leave the device). Multi-device safe: one device answers each + request. ### Gifts (feature: `gifts`) -- **Gift links**: presigned bearer transactions claimable by whoever opens - the link, straight into a fresh wallet (with seed backup flow) or an - existing one. Reclaim or revoke unclaimed gifts. Optionally **lock a gift to - a nostr account** (the claim code is DM'd to them; the link alone is not - enough). +- **Gift links**: bitcoin you send as a link, claimable straight into a + brand-new wallet or an existing one. On-chain gifts use a one-time key so + they can't be front-run; Ark gifts are claimed instantly and free. +- Reclaim or revoke unclaimed gifts; optionally lock a gift to a Nostr + account (the claim code is DM'd to them). + +### Point of sale (feature: `pos`) + +- Enter an amount, the customer picks a tip, and one Lightning invoice + settles it; bills and tips are kept apart in history. + +### Sign-in (feature: `nostrlogin`) + +- **Seed phrase**, created or imported. +- **Passkeys**: the WebAuthn PRF extension derives the wallet's secret. +- **Google**, via pomegranate: the Nostr key is FROST-sharded 2-of-3 across + independent operators. +- **Nostr signers**: browser extensions (NIP-07), remote signers + (`bunker://` and `nostrconnect://`, NIP-46) and Amber on Android (NIP-55). + A pasted or signer key becomes a recovery key for the wallet. +- Several identities side by side, each with its own wallet; switch or sign + out per identity. ### Cross-device sync (feature: `sync`) -- Optional **encrypted state sync over Nostr**: wallet state NIP-44-encrypted - to yourself as a replaceable event, per network, on relays you choose. The - nostr identity derives from the same seed (NIP-06). +- Wallet state, read receipts and custom feeds are **NIP-44 encrypted to + yourself** and published as replaceable Nostr events, one per domain (core + wallet, Ark, NWC, …), so only what changed republishes. +- Domains too big for a relay event go to **Blossom** as an encrypted blob + (`blossom.coinos.io` by default, or any Blossom server); the event carries + its hash. +- A durable outbox holds signed snapshots until a relay accepts them. + +### Social: a Nostr client (feature: `messages`) + +**Feeds** +- **Following**, a curated **Popular** feed (prose in your language that + several people engaged with), and **custom feeds** from people, hashtags, + relays, follow packs or NIP-51 lists. Custom feeds sync across devices; + people feeds publish as NIP-51 follow sets. +- Shareable feed links (`/feed/<npub>`, `/feed/t/<tag>`, packs and lists). +- **Views**: All, an **Images** grid, **Videos** (full-screen vertical clips + with autoplay), and long-form **Articles** (NIP-23). +- Cached posts paint first and new ones arrive without moving what you're + reading; long feeds are windowed. + +**Posts** +- Replies, reposts, quotes, reactions, polls (NIP-88) and zaps on one + action bar, with who-reacted panels. +- Link previews, YouTube embeds, audio players for podcast links, article + cards for `naddr` references, and inline quoted notes. +- Compose with pictures and video (compressed on device, uploaded to + Blossom media hosts). +- Linkable notes and profiles (`/nevent1…`, `/npub1…`), with server-rendered + pages for link previews elsewhere. + +**Moderation** +- Your NIP-51 mute list (people, words, hashtags, threads, including its + private half) applied everywhere; reports (NIP-56) from people you follow + fold posts; duplicate and hashtag-spam filtering. + +**Messages** +- **Private DMs** (NIP-17 gift-wrapped), with replies, reactions and + encrypted attachments. +- **Communities** on Concord: end-to-end encrypted channels compatible with + Vector, including Vector's custom emoji packs (NIP-30) and community icons. + Every user joins the coinos community; anyone can create or join others. +- **Group chats** on White Noise (Marmot), with a native-JS MLS (RFC 9420) + implementation. +- **Notifications** for mentions, replies, reactions and zaps, with push + delivery to your devices. + +### Extras + +- **Hats** (feature: `hats`): supporter hats worn above your avatar. + ## Modular builds -Every feature above is a **plugin behind a build seam**. `HAL_FEATURES` -selects what ships; excluded features never enter the bundle: their code, -their network endpoints, their crypto: +Every optional feature above is a **plugin behind a build seam**. +`COINOS_FEATURES` selects what ships; excluded features never enter the +bundle (their code, their network endpoints, their crypto): ```bash -bun run build # everything (~610 KB) -HAL_FEATURES=ark,gifts bun run build # core + ark + gifts -bun run build:minimal # core on-chain wallet only (~320 KB) +bun run build # everything +COINOS_FEATURES=ark,gifts bun run build # core + ark + gifts +bun run build:minimal # core on-chain wallet only ``` -`tools/minimal-smoke.js` verifies the minimal profile is a fully working -on-chain wallet with zero feature code in the bundle. Features integrate -through fixed seams on the core (`registerCoinLock`, `registerInputSigner`, -cache extensions, UI hooks), so a stripped build isn't a crippled build, it's -the same core wallet, smaller. +Feature names: `gifts`, `ark`, `nostrlogin`, `names`, `zaps`, `nwc`, `hats`, +`sync`, `messages`, `pos`. `tools/minimal-smoke.js` checks the minimal +profile is a working on-chain wallet with no feature code in the bundle. + +Other build switches: + +| Variable | Effect | +| --- | --- | +| `COINOS_TARGET=staging` | mutinynet-first defaults and a visible staging stamp | +| `COINOS_NO_SW=1` | no service worker (the bundled Android/F-Droid build) | + +The old `HAL_*` names still work. + ## Develop @@ -107,72 +201,86 @@ bun install bun run dev # http://localhost:5173 (rebuilds on each refresh) ``` -The dev server also proxies local regtest backends (electrum, esplora, boltz) -so the app works unchanged from a phone on the LAN. End-to-end -suites in `tools/` drive a headless Chrome through the real flows (boarding, -ark sends, offboard, unilateral exit, gifts, swaps) against a -local regtest stack. +The dev server proxies local regtest backends (Electrum, Esplora) so the app +works unchanged from a phone on the LAN. The end-to-end suites in `tools/` +drive headless Chrome through real flows (boarding, Ark sends, Lightning, +offboard, exit, gifts, feeds, chat) against a regtest stack or live relays. + ## Build ```bash -bun run build # → dist/ (index.html + PWA sidecars) +bun run build # → dist/ ``` -`dist/index.html` inlines all code, the crypto/QR/Nostr libraries, and the CSS, -save that one file and open it directly in a browser, no server or internet -needed. The build also emits PWA extras (`manifest.webmanifest`, `sw.js`, -icons, and a lazy-loaded `jsqr.js`) for the hosted site; they're optional and -simply 404 when `index.html` is opened on its own. +- `dist/index.html` is a small shell; the app loads as `app-<hash>.js` plus + lazily loaded `chunk-<hash>.js` features. +- `sw.js`, `manifest.webmanifest` and icons make it an installable PWA that + works offline. +- `dist/standalone.html` inlines everything (code, libraries, CSS, fonts): + save it and open it from disk with no server. -## How state is kept -Three layers, fastest first: +## Services in this repo + +The app is static; these small services run beside it (Bun): -1. **sessionStorage**: keeps the wallet open across a refresh (cleared on - logout / tab close). -2. **localStorage**: caches scanned state per wallet *and per network*, so a - reload shows balances instantly. -3. **Nostr**: encrypted, replaceable cross-device state per network (when - sync is enabled). +| Directory | Service | +| --- | --- | +| `names/` | Payment-name registrar (`names.coinos.io`): BIP-353 TXT records via the DNS provider's API, LNURL-pay for `@coinos.io` names, NIP-98 auth, link previews | +| `feed/` | Public feed snapshot (`/api/feed`) and server-rendered note pages | +| `nwcpush/` | Watches relays for NWC requests and push notifications, and wakes devices with Web Push | +| `blossom/` | `blossom.coinos.io`: a Blossom server that only stores encrypted sync envelopes | +| `bridge/` | Ark→Lightning swap bridge (retired; the app uses the Ark server's Lightning) | + + +## How state is kept + +1. **sessionStorage** keeps the wallet open across a refresh (cleared on + lock or tab close). +2. **localStorage** caches scanned state, feeds and profiles per wallet and + network, so a reload paints instantly. +3. **IndexedDB** holds Ark coins, the feed media index and White Noise MLS + state. +4. **Nostr** (and Blossom for large domains) holds the encrypted + cross-device copy. -On load and on a timer the wallet reconciles against its data source; a manual -**Settings → Rescan** forces a full re-scan on demand. ## Layout -| File | Purpose | +| Path | Purpose | | --- | --- | -| `src/wallet.js` | BIP84 core: derivation, scanning, coin selection, signing, realtime, cache, plus the feature seams | -| `src/api.js` | Data-source selection per network (electrum/esplora presets, throttle/cooldown/failover) | -| `src/ark/` | Native-JS Ark protocol: gRPC-web codec, arkoor sends, boarding, refresh, offboard, unilateral exit, validation, crash-safe manager | -| `src/features/` | Feature plugins: `ark`, `swaps`, `gifts`(+`gifts-wallet`), `sp`(+`sp-wallet`), `sync`, and `index.js`, rewritten at build time by `HAL_FEATURES` | -| `src/swap.js` | Boltz submarine/reverse swap engine | -| `src/silentpay.js` / `src/sp-worker.js` | BIP-352 math + its Web Worker | -| `src/nostr.js` | NIP-44 state sync, NIP-17 DMs, profiles | -| `src/mls.js` / `src/marmot.js` / `src/marmot-client.js` | White Noise group chats: native-JS MLS (RFC 9420, one ciphersuite), the Marmot layer over it, and the per-device client (IndexedDB state, relay traffic) | -| `src/electrum.js` / `src/scan.js` / `src/qr.js` | Electrum transport, camera QR scanner, QR rendering | -| `src/app.js` | UI controller (vanilla DOM) + feature registry | -| `build.js` / `dev.js` | Bun bundler → inlined `index.html` + PWA sidecars; dev server with regtest proxies | +| `src/wallet.js` | BIP84 core: derivation, scanning, coin selection, signing, realtime, cache, and the feature seams | +| `src/api.js` / `src/electrum.js` | Data sources: Esplora REST and Electrum-over-WebSocket, presets and failover | +| `src/ark/` | Native-JS Ark: gRPC-web codec, arkoor sends, board, refresh rounds, offboard, Lightning, exit, validation, crash-safe manager, IndexedDB store | +| `src/features/` | Feature plugins (see Modular builds); `index.js` is rewritten at build time by `COINOS_FEATURES` | +| `src/nostr.js` / `src/sync-*.js` | Encrypted state sync, relay pool, Blossom envelopes, the sync outbox | +| `src/nostr-login.js` / `src/passkey.js` / `src/pomegranate.js` | Nostr signers, passkey PRF sign-in, Google via FROST | +| `src/dm.js` / `src/concord.js` / `src/mls.js` / `src/marmot*.js` | NIP-17 DMs, Concord communities, MLS and the White Noise layer | +| `src/bip353.js` / `src/dnssec.js` / `src/bolt12.js` / `src/noffer.js` / `src/clink.js` / `src/lnurl.js` | Payment-name resolution with DNSSEC validation, BOLT12, CLINK, LNURL and zaps | +| `src/sw-nwc.js` / `src/nwc-*.js` | Background NWC answering in the service worker | +| `src/app.js` | UI controller (vanilla DOM, no framework) and feature registry | +| `build.js` / `dev.js` | Bun bundler (split build, PWA, standalone file); dev server with regtest proxies | +| `android/` | Android wrapper: a Trusted Web Activity build and a bundled F-Droid flavor | + ## Security notes -- Self-custody software handling real keys. Review the code before trusting - it with funds. It's a hot wallet (keys live in the browser); for large - amounts use a hardware signer or run it air-gapped. -- The seed/passphrase live in memory while the page is open and in - sessionStorage for the tab session; locking or closing the tab clears them. -- **Ark trust model**: off-chain sends are co-signed by the Ark server, which - is the arbiter of off-chain spent/unspent state, but it can never spend - your coins, and the pre-signed exit path means you can always return - on-chain without its cooperation. Ark coins expire (weeks) if never - refreshed; the wallet surfaces this, but don't park long-term savings in an - Ark. -- Swap providers are non-custodial by protocol: worst case a swap fails and - refunds on-chain. -- Whichever data source you query sees your addresses and IP. Point it at - your own node for full privacy. The localStorage cache and the Nostr event - hold public chain data; the Nostr copy is encrypted end-to-end. +- Self-custody software handling real keys: a hot wallet whose keys live in + the browser. Review the code before trusting it with funds; for large + amounts use the offline-signing flow or don't keep them in a hot wallet. +- **Ark trust model**: the Ark server co-signs off-chain spends and decides + off-chain spent/unspent state, but it can never spend your coins, and the + pre-signed exit path returns them on-chain without its cooperation. Ark + coins expire if never renewed; the wallet renews them automatically. +- **Lightning via the Ark server**: payments are HTLC-locked, so the server + can fail a payment but not take the funds. +- **NWC background answering** means `nwcpush` learns your wallet-service + pubkeys and when requests arrive (not their contents). +- Your data source sees your addresses and IP; point it at your own node + for privacy. Synced state is end-to-end encrypted; relays see only + ciphertext and timing. + ## License |
