# coinosv3-server The server-side services behind [coinos v3](https://v3.coinos.io), the bitcoin, Ark, Lightning and nostr wallet whose app lives in [coinosv3](https://git.coinos.io/coinosv3.git). The wallet itself is self-custodial and runs in the browser. These services hold no keys to user funds; they give a wallet a payment address, wake it when a request arrives, and store its encrypted sync data. | Directory | Service | Public at | | ---------- | ------- | --------- | | `names/` | **Payment-name registrar.** Gives each wallet a `name@coinos.io` address: BIP-353 DNS records (written through Cloudflare's API), LNURL-pay, and a BOLT 12 offer per name on the Ark server's Lightning node. Settled payments are forwarded to the name's Ark address from a small float wallet. Also serves zap receipts, point-of-sale invoices, CLINK, payment webhooks, link previews and translation. Writes are authorized with NIP-98. | `names.coinos.io` | | `nwcpush/` | **NWC push notifier.** Watches relays for Nostr Wallet Connect requests addressed to registered wallet-service keys and wakes the device with a Web Push, so a closed wallet can answer. | (internal, behind the app) | | `blossom/` | **Blossom server for sync envelopes.** Stores only the wallet's encrypted cross-device state blobs, which can outgrow a relay's event size. Refuses anything else. | `blossom.coinos.io` | | `feed/` | **Public feed snapshot** (`/api/feed`) and server-rendered note and profile pages. | `v3.coinos.io` | | `live/` | **Live streams** sent from the app: the browser's recording over a WebSocket, out as HLS (a live window and the recording), for NIP-53 streams. Senders own a coinos name. | `v3.coinos.io/live/` | | `vmedia/` | **Media helper** for the feed: video posters, H.264 fallbacks and resized pictures. | `v3.coinos.io/vmedia/` | | `bridge/` | Ark to Lightning swap bridge. **Retired**: the app uses the Ark server's Lightning. `bridge/ln.js` (a minimal CLN RPC client) is still used by `names/`. | — | ## Shared code: the `coinosv3` submodule The services use the wallet's own code for Ark, BOLT 12 and nostr helpers, so the server speaks exactly the wire format and policy the app does. That code comes from the `coinosv3/` submodule, **pinned to a specific commit**. Moving the pin is a deliberate, reviewable commit of its own: ```sh git -C coinosv3 fetch && git -C coinosv3 checkout git add coinosv3 && git commit -m "coinosv3 → " ``` ## Running You need [Bun](https://bun.sh) (the deployment pins `oven/bun:1.3.14`) and, for `names/`, a Core Lightning node's RPC socket and a Cloudflare zone. ```sh git clone --recurse-submodules https://git.coinos.io/coinosv3-server.git cd coinosv3-server bun install cp names/config.example.json names/config.json # fill it in NAMES_CONFIG=names/config.json bun names/server.js ``` Each service reads a JSON config. `names` uses `NAMES_CONFIG` and `nwcpush` uses `NWCPUSH_CONFIG`; `blossom` reads `BLOSSOM_*` environment variables (see the header of `blossom/server.js`). Example configs: `names/config.example.json`, `nwcpush/config.example.json`, `bridge/config.example.json`. **Configs hold secrets and are never committed.** They include the DNS provider's API key, admin tokens, the forwarder float's mnemonic, VAPID keys and nostr secret keys. `.gitignore` excludes every `*config*.json` except the examples, and all runtime state. ### With Docker `compose.yml` runs `names`, `nwcpush` and `blossom` from the checkout, mounted read-only. Configs and state live outside it, under a directory you name in a `.env` file: ```sh # .env STATE=/srv/coinosv3-state CLN_RPC=/path/to/lightning-rpc ``` ``` $STATE/names/config.json $STATE/names/data/ (names.db) $STATE/nwcpush/config.json $STATE/nwcpush/data/ $STATE/blossom/data/ ``` ```sh docker network create net # once; the services share it with the reverse proxy docker compose up -d ``` ## State `names` keeps its state in SQLite (`names.db`, see `names/store.js`): one row per name, offer, invoice and so on. Each change is a single transaction that writes only the rows that changed. On its first start it imports the older JSON state file named by `stateFile`, and a corrupt file stops startup rather than starting empty. For a consistent backup while it runs, take a snapshot through SQLite, not by copying the file: ```sh sqlite3 names.db "VACUUM INTO 'names-backup.db'" ``` ## Deploying coinos deploys by pushing to a bare repository on the server. `deploy/post-receive` is its hook. A push to `prod` checks the commit out, installs dependencies, restarts the services whose files changed, and mirrors the commit to [git.coinos.io](https://git.coinos.io) and gittr. Don't copy files onto the server by hand: what runs should always be a commit. ```sh git push prod HEAD:prod && git push origin HEAD:master ``` ## Rolling back The previous deploy is the previous commit: `git revert` and push, or push an older commit to `prod` (`git push -f prod :prod`). `names` reads its SQLite state either way. Before the SQLite change, the JSON file it was imported from is still in the data directory, but it stops at the moment of the import. ## Tests ```sh bun test names/store.test.js ``` `tools/` holds the older end-to-end and unit scripts for the registrar, feed and translation. They are run directly with `bun tools/`. ## License MIT, see [LICENSE](LICENSE).