{"id":51462578,"url":"https://github.com/codenlighten/web3keys2026","last_synced_at":"2026-07-06T07:01:15.084Z","repository":{"id":361964998,"uuid":"1256620990","full_name":"codenlighten/web3keys2026","owner":"codenlighten","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-02T02:47:33.000Z","size":70,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-02T03:17:38.389Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codenlighten.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-02T00:25:18.000Z","updated_at":"2026-06-02T02:47:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/codenlighten/web3keys2026","commit_stats":null,"previous_names":["codenlighten/web3keys2026"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/codenlighten/web3keys2026","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codenlighten%2Fweb3keys2026","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codenlighten%2Fweb3keys2026/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codenlighten%2Fweb3keys2026/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codenlighten%2Fweb3keys2026/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codenlighten","download_url":"https://codeload.github.com/codenlighten/web3keys2026/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codenlighten%2Fweb3keys2026/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35180933,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-06T02:00:07.184Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-06T07:01:14.174Z","updated_at":"2026-07-06T07:01:15.080Z","avatar_url":"https://github.com/codenlighten.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# web3keys2\n\nA BSV HD wallet library built on [`@smartledger/bsv`](https://www.npmjs.com/package/@smartledger/bsv). It supports BSV payments, 1Sat Ordinals, and a full **BRC-100** wallet-to-application substrate (BRC-42/43 key derivation, certificates, baskets, actions).\n\n## Accounts \u0026 derivation\n\nThree HD accounts, deliberately split across coin types:\n\n| Account  | Path                  | Purpose                                            |\n|----------|-----------------------|----------------------------------------------------|\n| identity | `m/44'/236'/0'/0/0`   | Identity key, message signing, DID, BRC-100 auth   |\n| finance  | `m/44'/0'/0'/0/0`     | Spendable BSV funds (payments, change, fees)       |\n| tokens   | `m/44'/236'/2'/0/0`   | 1Sat Ordinals, inscriptions, tokens                |\n\n`236` is BSV's SLIP-44 coin type; `0` is the legacy Bitcoin coin type used historically for BSV funds.\n\n## Install\n\n```bash\nnpm install\n```\n\nRequires Node 18+ (global `fetch` for the WhatsOnChain provider).\n\n## Quick start\n\n```js\nconst { Wallet, WhatsOnChainProvider } = require('web3keys2');\n\n// Create or restore\nconst wallet = Wallet.generate();                       // new random wallet\n// const wallet = Wallet.fromMnemonic('abandon abandon ... about');\n\nconsole.log(wallet.mnemonic);\nconsole.log(wallet.addresses());        // { identity, finance, tokens }\n\n// Attach a chain data source (pluggable)\nwallet.setProvider(new WhatsOnChainProvider({ network: 'livenet' }));\n\n// Balance \u0026 send\nawait wallet.getBalance('finance');\nawait wallet.send([{ to: '1Dest...', satoshis: 25000 }]);\n\n// 1Sat Ordinals\nconst ins = await wallet.inscribe({ data: 'hello ordinal', contentType: 'text/plain' });\nconst ords = await wallet.listOrdinals();\nawait wallet.transferOrdinal({ ordinalUtxo: ords[0], toAddress: '1New...' });\n```\n\n## Identity (signing / messaging)\n\n```js\nconst sig = wallet.identity.sign('authenticate me');\nwallet.identity.verify('authenticate me', sig);          // true\nwallet.identity.did();                                   // { did, identityKey, address }\n\nconst ct = wallet.identity.encryptTo(recipientPubKey, 'secret');   // ECIES\nconst pt = otherWallet.identity.decryptFrom(senderPubKey, ct);\n```\n\n## BRC-100\n\n`wallet.brc100` implements the BRC-100 `WalletInterface`. Keys are derived per\n`(protocolID, keyID, counterparty)` via BRC-42/43.\n\n```js\nconst b = wallet.brc100;\nconst proto = [2, 'my app'];          // [securityLevel, protocolName]\n\nawait b.getPublicKey({ identityKey: true });\nawait b.getPublicKey({ protocolID: proto, keyID: '1', counterparty: 'self' });\n\n// Encryption (AES-256-GCM under derived symmetric key)\nconst { ciphertext } = await b.encrypt({ plaintext: 'hi', protocolID: proto, keyID: '1' });\nawait b.decrypt({ ciphertext, protocolID: proto, keyID: '1' });\n\n// Signatures / HMAC\nconst { signature } = await b.createSignature({ data: 'x', protocolID: proto, keyID: '2' });\nawait b.verifySignature({ data: 'x', signature, protocolID: proto, keyID: '2', forSelf: true });\n\n// Actions (transactions)\nawait b.createAction({\n  description: 'pay',\n  outputs: [{ lockingScript: scriptHex, satoshis: 1000, basket: 'payments' }],\n});\nawait b.listActions();\nawait b.listOutputs({ basket: 'payments' });\n\n// Deferred / offline / multi-party signing — inputs are PINNED at create time.\nconst { reference, signableTransaction } = await b.createAction({\n  outputs: [{ lockingScript: scriptHex, satoshis: 1000 }],\n  options: { signAndProcess: false },\n});\n// signableTransaction = { reference, tx: \u003cunsigned hex\u003e, inputs: [...pinned...], fee, changeAddress }\n// signAction rebuilds the IDENTICAL transaction from the pinned inputs — no UTXO\n// re-fetch, no re-selection — even if the wallet's UTXO set has changed since.\nawait b.signAction({ reference });                       // re-derives the signing key\nawait b.signAction({ reference, privateKeys: [signerKey], options: { noSend: true } });\n\n// Pending actions are persisted through WalletStorage (no private keys stored), so a\n// create→sign session survives a restart when backed by a durable storage. A separate\n// instance sharing the same storage + seed can complete the signature:\nawait b.listPendingActions();\n\n// Certificates (BRC-103 style)\nconst { certificate } = await b.acquireCertificate({ type: 'KYC', certifier: '02..', fields: { name: 'Greg' } });\nawait b.proveCertificate({ certificate, fieldsToReveal: ['name'], verifier: '03..' });\n```\n\n### Supported BRC-100 methods\n\n`isAuthenticated`, `waitForAuthentication`, `getNetwork`, `getVersion`, `getHeight`,\n`getHeaderForHeight`, `getPublicKey`, `revealSpecificKeyLinkage`, `encrypt`, `decrypt`,\n`createHmac`, `verifyHmac`, `createSignature`, `verifySignature`, `createAction`,\n`signAction`, `abortAction`, `listPendingActions`, `internalizeAction`, `listActions`, `listOutputs`,\n`relinquishOutput`, `acquireCertificate`, `listCertificates`, `proveCertificate`,\n`relinquishCertificate`, `discoverByIdentityKey`, `discoverByAttributes`.\n\n## Persistent storage (SQLite)\n\nBy default the wallet uses in-memory storage. For durability — so balances/baskets,\ncertificates, action history, and **deferred (pinned) pending actions** survive a\nrestart — pass a `SqliteStorage`. It's backed by the built-in `node:sqlite` module\n(no native dependency; Node 18.19+/20.6+/22+).\n\n```js\nconst { Wallet, SqliteStorage } = require('web3keys2');\n\nconst storage = new SqliteStorage('./wallet.db');   // or ':memory:'\nconst wallet = Wallet.fromMnemonic(mnemonic, { storage, provider });\n\n// ... create a deferred action now ...\nconst { reference } = await wallet.brc100.createAction({\n  outputs: [{ lockingScript, satoshis: 1000 }],\n  options: { signAndProcess: false },\n});\n\n// ... later, in a brand-new process: reopen the same file + seed and complete it.\nconst wallet2 = Wallet.fromMnemonic(mnemonic, { storage: new SqliteStorage('./wallet.db') });\nawait wallet2.brc100.signAction({ reference });      // signing key re-derived; no key on disk\n```\n\n`SqliteStorage` is a drop-in for `MemoryStorage` (identical async surface). Private keys\nare never written to the database — pending records store the funding *account*, and the\nsigning key is re-derived from the seed at sign time. `node:sqlite` is lazy-required, so\nimporting the package stays silent until you actually construct a `SqliteStorage`.\n\n## Wallet service (server)\n\n`server/` is a HandCash-style consumer wallet service that consumes this library:\nemail-OTP registration, encrypted server-side mnemonic custody, paymail, and a JSON API.\n\n```bash\ncp .env.example .env      # set JWT_SECRET + SMTP_* (and WALLET_DOMAIN)\nnpm start                 # node server/index.js\n```\n\n**Security model.** The mnemonic is sealed with AES-256-GCM under a key derived from\nthe user's password (scrypt); the server stores only ciphertext + the **public** account\nxpubs and identity key. It therefore cannot decrypt the seed without the user's password,\nyet can still derive receive addresses and serve paymail. The seed is decrypted only\ntransiently in an in-memory session vault (TTL, wiped on logout) for signing. The mnemonic\nis returned to the client exactly once at registration (copy-to-clipboard) and never logged.\n\n### API\n\n| Method | Path | Auth | Purpose |\n|--------|------|------|---------|\n| POST | `/api/auth/register` | – | create account, seal mnemonic, email OTP; returns mnemonic once |\n| POST | `/api/auth/verify` | – | confirm email with OTP code |\n| POST | `/api/auth/login` | – | unlock wallet, returns JWT |\n| POST | `/api/auth/logout` | JWT | wipe session vault entry |\n| GET | `/api/wallet/profile` | JWT | profile + paymail + address |\n| GET | `/api/wallet/balance` | JWT | confirmed/unconfirmed balance |\n| POST | `/api/wallet/send` | JWT | send BSV to address or local paymail |\n| GET | `/.well-known/bsvalias` | – | paymail capability discovery |\n| GET | `/api/paymail/id/{paymail}` | – | paymail PKI (identity pubkey) |\n| POST | `/api/paymail/address/{paymail}` | – | paymail payment destination (P2PKH script) |\n\n### Deployment (DigitalOcean + nginx + TLS)\n\n```bash\n# on the droplet, as root:\nbash deploy/provision.sh          # installs Node 22, nginx, certbot; clones repo; systemd + nginx\n# then create /etc/web3keys/web3keys.env (from .env.example), start, and issue TLS:\nsystemctl start web3keys\ncertbot --nginx -d web3keys.com -d www.web3keys.com --redirect\n```\n\n`deploy/deploy.sh` redeploys latest code on an already-provisioned box.\nTLS requires the domain's A records to point at the droplet first.\n\n### Backups\n\n`deploy/web3keys-backup.sh` (run daily by `web3keys-backup.timer`) takes a WAL-safe\nSQLite snapshot, integrity-checks it, optionally **encrypts it client-side with age**,\nand optionally syncs it **off-site to DigitalOcean Spaces** (private ACL). Retention is\n14 days, local and remote.\n\nEncryption is asymmetric: the server holds only the age **public** recipient\n(`BACKUP_AGE_RECIPIENT`) and cannot decrypt its own backups — the private identity lives\noff-box. Generate it on a trusted machine (not the server):\n\n```bash\nage-keygen -o backup-identity.txt          # keep this file OFF the server\nage-keygen -y backup-identity.txt          # prints the recipient -\u003e BACKUP_AGE_RECIPIENT\n```\n\nRestore (needs the off-box identity):\n\n```bash\n# fetch a blob (from the droplet or Spaces), then:\nage -d -i backup-identity.txt web3keys-\u003cTS\u003e.db.gz.age | gunzip \u003e web3keys.db\ninstall -o web3keys -g web3keys -m600 web3keys.db /var/lib/web3keys/web3keys.db\nsystemctl restart web3keys\n```\n\nIf `BACKUP_AGE_RECIPIENT` is unset the blob is plain gzip; if `DO_SPACES_*` is unset it\nstays local only.\n\n## Pluggable providers\n\nImplement `ChainProvider` to back the wallet with any data source (a node,\nGorillaPool/1Sat for ordinals, a mock for tests). `WhatsOnChainProvider` ships by default.\n\n```js\nconst { ChainProvider } = require('web3keys2');\nclass MyProvider extends ChainProvider { /* getBalance, getUtxos, broadcast, ... */ }\n```\n\n## CLI\n\n```bash\nnode bin/cli.js new\nnode bin/cli.js info        --mnemonic \"...\"\nnode bin/cli.js sign        --mnemonic \"...\" --message \"hi\"\nnode bin/cli.js balance     --mnemonic \"...\" --account finance\nnode bin/cli.js derive-pub  --mnemonic \"...\" --protocol \"message signing\" --keyid 1\n```\n\n## Tests\n\n```bash\nnpm test\n```\n\n## Architecture\n\n```\nsrc/\n  Wallet.js                top-level orchestrator\n  KeyManager.js            BIP-39 seed + HD derivation\n  paths.js                 derivation path definitions\n  identity/Identity.js     signing, verification, ECIES, DID\n  providers/               ChainProvider interface + WhatsOnChainProvider\n  tx/                      coinSelect + TxBuilder (selectInputs/buildFromInputs for\n                           pinned signing; uncheckedSerialize for 1-sat outputs)\n  ordinals/Ordinals.js     1Sat inscription build/parse/transfer\n  brc100/\n    KeyDeriver.js          BRC-42/43 key derivation (ECDH + point math)\n    cryptoOps.js           encrypt/decrypt/hmac/sign/verify\n    storage.js             pluggable WalletStorage — baskets, certs, actions,\n                           and persisted pending actions (MemoryStorage default)\n    SqliteStorage.js       durable WalletStorage on node:sqlite (restart-survival)\n    BRC100Wallet.js        full WalletInterface\n```\n\n## License\n\nMIT\n# web3keys2026\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodenlighten%2Fweb3keys2026","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodenlighten%2Fweb3keys2026","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodenlighten%2Fweb3keys2026/lists"}