import type { PluginManifest } from '@@/plugins/manifest'; /** * Wallet — a self-custodial bitcoin wallet, with the keys on the server rather than on a device. * * ── Read `docs/wallet-key-custody.md` before changing anything here ── * * The seed is sealed under a key derived from the OWNER OF THAT WALLET's passphrase by scrypt * (N=2¹⁷), then encrypted again at rest. The server cannot open it without the passphrase, which it * never stores. That is not incidental — it is what makes the next paragraph safe. * * ── This permission is GRANTABLE, and that is deliberate ── * * It was `kind: 'admin'` — owner only — until this extraction, which was single-user thinking. Every * wallet query is scoped by `user_id`, and the id comes from `X-Officer-User`, injected by the platform * from the authenticated session. A caller cannot name someone else's. * * So granting this to a role gives that person THEIR OWN wallet. It does not give them yours, and it * does not give you theirs: not through the API, and not with root either, because their seed is sealed * under a passphrase you do not have. * * ── The gap that is real, and deferred on purpose ── * * Watch-only data — xpubs, and the balances, addresses and history derived from them — is stored in the * CLEAR, so a locked wallet still shows a balance without anyone typing a passphrase. That ergonomics * choice is why the wallet spends its life locked. The cost: anyone with the Postgres database or a * backup of it can read any user's balances and history. They can never move a coin. * * Encrypting it per-user is a future option, and it costs balance-while-locked. Recorded here rather * than discovered later. * * ── No `osDependencies` ── * * Everything is JS. Chain data comes from Esplora or NBXplorer over `fetch`. */ export const manifest: PluginManifest = { publisher: 'officerdev', version: '1.0.0', platform: '>=1.0.0', label: 'Bitcoin Wallet', summary: 'A self-custodial bitcoin wallet — keys sealed on the server, never on a device', icon: 'Bitcoin', color: '#f7931a', permissions: [ { key: 'bitcoin-wallet', label: 'Bitcoin Wallet', // Written to be read in the grants list, where it sits beside Photos and Music and must not look // like them. Granting gives that account its own wallet; it never exposes anyone else's. description: "Bitcoin wallet — each account gets its own, sealed under that account's own passphrase", }, ], };