aboutsummaryrefslogtreecommitdiffstats

coinos v3

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 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 (installable as a PWA)
  • Android: Zapstore, 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

Wallet core (always included)

  • 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, 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.

Spending on Ark (feature: ark)

A native-JS client for bark/captaind Ark servers: gRPC-web and MuSig2 spoken directly, no WASM. coinos runs ark.coinos.io; other captaind servers can be chosen.

  • 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.

Payment addresses (feature: names)

  • [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: 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)

  • 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) and on Cordn (the MLS group chats in Amethyst 1.17 and the Cordn app, delivered through a coordinator that only ever sees sealed bytes), both on 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 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):

bun run build                                 # everything
COINOS_FEATURES=ark,gifts bun run build       # core + ark + gifts
bun run build:minimal                         # core on-chain wallet only

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)

Develop

Requires Bun.

bun install
bun run dev      # http://localhost:5173 (rebuilds on each refresh)

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

bun run build    # → dist/
  • 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.

Services in this repo

The app is static; these small services run beside it (Bun):

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.

Layout

Path Purpose
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 / src/cordn*.js / src/contextvm.js NIP-17 DMs, Concord communities, MLS, the White Noise layer, and Cordn over ContextVM
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: 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

MIT, see LICENSE.