# How to do things — Silent Mode runbook Operational companion to [`SESSION-CONTEXT.md`](SESSION-CONTEXT.md). That file is *what the product is*; this one is *how to do a thing*. Every command below was run on 2026-07-30 and works. All commands run from `D:\Dev\SilentMode\Argus` unless stated. Prefix with `rtk` per project convention. **Everything is chipnet.** > **Before you touch a `.js` in `Argus/src/lib/`, grep for it first.** Two > sessions have written here concurrently and `registry-lifecycle.js`, > `portfolio.js` and `ElectrumPool` were each independently rebuilt once. If a > test file exists for a module you are about to create, implement to the test — > the test is the contract. --- ## 1. Read the registry ```bash node src/resolve.js # list every name, with records and category node src/resolve.js demo.p2p # resolve one ``` Real output today (abridged): ``` silentmode.bch -> {"s3":"bns/silentmode/"} theseus.bch -> {"h":"

Theseus

…"} coinspectrum.bch-> {"ip":"195.184.247.106","p":"https://…","tls":"84e4cd…"} demo.p2p -> {"h":"

demo.p2p

…"} syskypo.de -> {"s3":"bns/syskypo.de/"} ``` Zero-dependency alternative (the resolver clients actually ship): ```bash node --input-type=module -e " import WebSocket from 'ws'; import { resolveName } from './src/lib/resolver-web.js'; console.log(await resolveName('silentmode.bch', { WebSocket })); process.exit(0);" ``` --- ## 2. Register a name ```bash node src/register.js ``` ```bash node src/register.js mysite https://example.org # shorthand → {"u": …} node src/register.js mysite '{"h":"

Hello

"}' # explicit records node src/register.js mysite.nav '{"s3":"bns/mysite/"}' # any TLD, same command ``` Registers from the project wallet (`wallets.json`). It mints the certificate, publishes the records and pays the beacon in **one transaction**. Verify: ```bash node src/resolve.js mysite.nav ``` Records can be omitted at registration and set later — see §4. **Buyer-facing registration** (built-in wallet, pay-by-QR) goes through `site/register.html`, not the CLI. The CLI is for operator-held names and testing. --- ## 3. Add a new TLD **Superseded 2026-08-29.** The old "TLDs are just a convention, no registry change needed" model is retired. The per-TLD registry ([`Decentralized.DNS/DESIGN-tld-registry.md`](Decentralized.DNS/DESIGN-tld-registry.md)) is now the source of truth: **a name REG under a TLD that has no on-chain TLD certificate will be rejected by conforming clients once enforcement is on** (the code is in `buildIndex({enforceTld:true})` in both `bns.js` and `resolver-web.js`; default is still off during the mainnet migration, but the registrar treats the on-chain list as authoritative today). So adding a TLD is now **three steps: TREG on the beacon, UI, deploy.** **1. Register the TLD on the TLD beacon (chipnet).** The idempotent way — the operator wallet holds the seed for the TLD-beacon address and the seed script skips anything already present: ```bash cd Argus # Edit src/seed-tld-beacon.mjs SEED_TLDS[] — append your new TLD label node src/seed-tld-beacon.mjs ``` Or a one-off from a Node REPL if you don't want to touch the script: ```bash node --input-type=module -e " import { loadWallet } from './src/lib/wallet.js'; import { registerTld } from './src/lib/bns.js'; const w = await loadWallet('main'); console.log(await registerTld(w, 'newtld', {})); process.exit(0);" ``` Either way you get `{tld, txid, category}`. The TLD certificate is minted to the operator wallet — that address becomes the TLD's owner and can UPD the records with `updateTld(wallet, tld, records, category)` later. Cost: ~1,300 chipnet sats per TREG. Empty records `{}` mean "defaults apply" per the design doc §3. **2. Add the TLD to the registrar UI.** The dropdown is gone. `site/sirius/register.html` now uses a `TLDS` array (around line 275). Append your new TLD with `onchain: true`: ```js const TLDS = [ ..., { tld: "newtld", onchain: true }, ]; ``` If you registered the cert but want to *hold* the TLD without offering it in the registrar (the `.com` pattern — cert on-chain so nobody else claims it, but no second-level names offered), just leave it out of the `TLDS` array. The cert stays valid on the beacon. **3. Deploy `site/sirius/register.html` to the VPS.** silentmode.st is nginx-served from `/opt/silent-mode/site/`, NOT Sia (see [`DEPLOY-split-st-from-bch.md`](DEPLOY-split-st-from-bch.md)): ```bash scp D:/Dev/SilentMode/site/sirius/register.html silentmode:/opt/silent-mode/site/sirius/register.html ``` (silentmode.bch — the Sia mirror — takes a full `sia-upload.js` of `site/`. Only touch that if you also want the on-chain-hosted copy refreshed.) **4. Republish the TLD snapshot (optional, updates mirrors).** If a downstream is reading from the Sia mirror or the Nostr replaceable event rather than doing its own chain scan, refresh the snapshot: ```bash cd Argus node src/publish-tld-mirror.mjs ``` Uploads `bns/tld-list.json` (floating) and `bns/tld-list-.json` (immutable content-addressed) to Sia, and publishes NIP-33 kind 30078 (d-tag `bns-tld-list`) to the configured relays. Dry-run: `BNS_MIRROR_DRY_RUN=1`. ### The thing to think about before adding one Under the per-TLD registry the operator's key is the countersignature gate — so **you're the registrar** for any TLD you mint. On mainnet the covenant will enforce a price floor and an operator-countersig for public mints ([`Decentralized.DNS/DESIGN-tld-registry.md`](Decentralized.DNS/DESIGN-tld-registry.md) §5). On chipnet nothing enforces this; a rogue script could TREG anything under a different key. Only the operator's key produces TLD certs that clients trust once the enforcement rules ship. `.bch` is special in one way only: its on-chain commitment is the **bare label** (`silentmode`, not `silentmode.bch`) for backward compatibility. Every other TLD stores the full `label.tld`. `normalizeName` handles this; don't hand-roll it. ### Current chipnet TLD set (2026-08-29) Bench-checkable via `node src/seed-tld-beacon.mjs` (idempotent verify) or by fetching the mirror at `https://s3.silentmode.st:8600/bns/tld-list.json` (403 to anon today; the Nostr replaceable event `kind:30078` `d:bns-tld-list` from pubkey `f2c925194c531c7c398017e35b2396df64a61a613aa5fadebdf1f4990f2267f4` on `wss://nos.lol` / `wss://relay.damus.io` is publicly readable). Live: `bch p2p bit nav test x asm neo gt sc sia com dex cex nt` (15 total). `.com` is **held** by the operator — cert on-chain, excluded from the public registrar UI so nobody else can claim it. --- ## 4. Set or change records ```bash node src/update.js ``` ```bash node src/update.js mysite '{"ip":"203.0.113.9"}' node src/update.js mysite.nav '{"s3":"bns/mysite/","tls":"a1b2c3…"}' ``` Proves ownership by moving the certificate — only the holder can do it. ### Record types | Key | Meaning | Example | |---|---|---| | `h` | inline HTML, stored on-chain | `{"h":"

Hi

"}` | | `u` | redirect to a URL | `{"u":"https://example.org"}` | | `ip` | IPv4 of your server | `{"ip":"203.0.113.9"}` | | `p` | reverse-proxy upstream (address bar stays on the name) | `{"p":"https://backend.example"}` | | `s3` | Sia object or prefix | `{"s3":"bns/mysite/"}` | | `tls` | SHA-256 fingerprint of the name's TLS cert (hex, over DER) | `{"tls":"84e4cd…"}` | ### The 200-byte ceiling The whole payload — `"BNS1"` + the JSON — must be **≤ 200 bytes**. For `sirius.nav` that leaves about 161 bytes for records. An oversized payload throws at build time rather than producing a broken registration. Check before you commit to a long `h`: ```bash node --input-type=module -e " import { payloadBudget } from './src/lib/register-tx.js'; console.log('bytes free for records:', payloadBudget('sirius.nav')); process.exit(0);" ``` `h` is for a page, a profile, a link hub. Anything real goes on Sia or a server. --- ## 5. Put a site on Sia storage Two steps: upload the bytes, then point the name at them. ```bash # from D:\Dev\SilentMode node Argus/src/lib/sia-upload.js ``` ```bash node Argus/src/lib/sia-upload.js D:/Dev/SilentMode/site/mysite bns/mysite node Argus/src/lib/sia-upload.js ./page.html bns/mysite ``` Then the record — a **trailing slash means "site root"**, no slash means a single object: ```bash cd Argus node src/update.js mysite '{"s3":"bns/mysite/"}' # directory → site node src/update.js mysite '{"s3":"bns/page.html"}' # one file ``` Verify through the public gateway: ```bash curl -s -o /dev/null -w "%{http_code}\n" https://navigate.st/bns/mysite.bch/ ``` Upload the **whole directory** if the page has assets — `sia-upload.js` walks it and preserves relative paths, which is what makes `/js/…` and `/vendor/…` resolve. **`sia-s3.json` is a secret** (SECURITY.md rule 0). Never print it, commit it, or copy its values into a bundle. The build refuses to emit a bundle containing them. --- ## 6. Build and deploy the site ```bash cd Argus && node build-web.mjs # bundles cd .. && node Argus/src/lib/sia-upload.js D:/Dev/SilentMode/site bns/silentmode ``` Two rules that will bite otherwise: - **Run the upload from the repo root**, not from `site/`. The path is relative to cwd and `site/Argus/...` does not exist. - **Upload the whole `site/` directory.** `register.html` depends on `site/js/` and `site/vendor/`; a page-only upload leaves a broken live page. `build-web.mjs` refuses to write a bundle that contains a live secret **value** (it reads `wallets.json`/`sia-s3.json` and greps for their contents) or a build-machine path. Do not weaken those checks. libauth is **one shared module behind an import map** (`site/js/libauth.js`). Do not bundle it per-file: its WASM init uses top-level await, and a second copy hands out `undefined` for `secp256k1` on cold load. The build verifies every import the other bundles emit is satisfied. Verify what is actually live: ```bash curl -s https://navigate.st/bns/silentmode.bch/register.html | grep -c 'bns-register.js' ``` --- ## 7. Tests ```bash cd Argus node --test test/*.test.mjs # offline; no network, no coins ``` Live scripts spend real chipnet coins and are run deliberately: ```bash node test/chipnet-acceptance.mjs # built-in wallet, full registration node test/chipnet-paybyqr.mjs # registrar mints to a buyer node test/chipnet-escrow-refund.mjs # unsigned order → refund, name never minted ``` They **exit 2** when the chain is unreachable, so an environment failure is distinguishable from a product failure. Add `--keep` to the acceptance test to print the throwaway wallet's phrase. --- ## 8. Gotchas that have actually cost time **Node cannot resolve some hostnames on the dev machine.** `getaddrinfo` returns `EAI_FAIL` for `coinspectrum.duckdns.org` and the chipnet electrum hosts while `nslookup` and c-ares resolve them fine. Symptom: uploads and live tests fail instantly with a DNS error. Workaround is a `--require` shim that falls back to `dns.resolve4`; the real fix is on the machine. **If a network command fails instantly, suspect this before suspecting the code.** **Electrum uses ports 50004/62002**, which corporate networks and some VPNs block outright — and every configured server shares those ports, so one firewall rule takes the whole resolver down. `REGISTRAR.selfHosted` puts your own Fulcrum on 443 first; see `Argus/OPERATIONS-electrum.md`. **Two resolvers must stay in lockstep.** `resolver-web.js` (zero-dep, shipped in clients) and `bns.js` (mainnet-js). Change one, change both. A disagreement is a consensus split between clients. **Terminology is enforced.** It is a **certificate / registration record / title** — never an "NFT" in code, comments or UI. `NFTCapability` and `.nft` are mainnet-js API names and stay. **The certificate lives in a 1,000-sat UTXO.** A wallet that does not understand CashTokens sees dust; a "sweep all funds" would destroy the name. This is why destinations are normalised to the token-aware `z…` form. **`h` records are HTML you are publishing permanently.** There is no edit — only an update transaction, and the old payload stays on-chain forever. **Never register a name you cannot spell twice.** Registration is irreversible and there is no expiry on chipnet today, so a typo is a permanently burned name. --- ## 9. What not to do - Do not touch `D:\Dev\NameCoin` — separate live system. - Do not flip anything to mainnet. It is a deliberate switch (new electrum servers, `bitcoincash:` prefix, a new beacon, real money), not a config change. - Do not commit or print `wallets.json`, `sia-s3.json`, `ca/root-ca.key`, or any seed phrase. - Do not put the operator's name or email in any file (SECURITY.md rule 3). - Do not add a TLD to the dropdown without registering a test name on it first. - Do not weaken the build's secret scan or the LGPL licence checks.