aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-05 22:35:56 -0700
committerAdam Soltys <[email protected]>2026-10-05 22:35:56 -0700
commitf571e46937d423d9ba44154a92aeb4136466daaa (patch)
tree658553eb48d4d85e632499f6711eda76071c9ba1 /README.md
parenta08f48d67ad61eba5077fdb1a01d19fe5eea0fdb (diff)
downloadcoinosv3-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.md350
1 files changed, 229 insertions, 121 deletions
diff --git a/README.md b/README.md
index 2b6fa25..3ef7ae0 100644
--- a/README.md
+++ b/README.md
@@ -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