commit a45320db7dc17750fd1045a69aeaf2efd0f44513 Author: Local Dev Date: Wed Jul 29 13:54:34 2026 +0200 Initial commit — Silent Mode baseline (2026-07-29) Snapshot of the decentralized-web stack at the point of the resolver+Theseus rebuild deploy. Includes: - Argus (BNS engine + resolver daemon + Sia gateway) - AriadneResolver (Windows Inno installer bundle + Android APK sources + Firefox extension) - TheseusNavigator (Electron browser) - site/ (silentmode.st content, deployed to Sia at bns/silentmode/) - design docs, roadmap, protocol spec Secrets excluded via .gitignore: Argus/sia-s3.json, Argus/wallets.json, Argus/ca/*.key,*.crt. Build outputs, node_modules, and bundled runtimes also excluded. Shipped hashes on dl.silentmode.st at this commit: AriadneResolver-Setup-0.1.0.exe 5bcb216eef31ea28ed767e4134ab74bd5ac69dfbd365fd249e9e6938e55c986a TheseusNavigator-Setup-0.0.1.exe 7c735e88bad2da3347145adba3016c8f626a18b8422289c8c6ba471972e2952b TheseusNavigator-0.0.1-portable.exe 008fd84445babeabb401b2bca40ea9466b24b0e6d6c85104da7640c5c5c84521 ariadne-v0.2.apk 635c8f04d44ef855a8390b9eeddb8cd2d50622e81cc4e5004e8daffc1bb0425c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..560625b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +wallets.json +faucet.html +ca/ +setup/install-state.json +sia-s3.json +tools/ +SiaHosting/ diff --git a/BROWSER-PROMPT.md b/BROWSER-PROMPT.md new file mode 100644 index 0000000..aa85b54 --- /dev/null +++ b/BROWSER-PROMPT.md @@ -0,0 +1,76 @@ +# Prompt for a new session: build Theseus, the browser + +Copy everything below the line into a fresh Claude Code session (suggested +working directory: `D:\Dev\BnsBrowser`). + +--- + +Build **Theseus Navigator** ("Theseus" for short) — a standalone desktop web +browser with native support for decentralized `.bch` domain names, resolved +from the Bitcoin Cash blockchain. Windows 10 first; Electron is the expected +shell (Chromium engine, no forking). + +Branding: *Theseus Navigator, by Silent Mode — a Deviant project*. The name refers to the +thread through the labyrinth: the blockchain is the thread. Read +`D:\Dev\SilentMode\README.md` first for the whole picture. `theseus.bch` is +already registered on chipnet and can serve the browser's own homepage. + +## Context — a working stack already exists + +`D:\Dev\NameCoin` contains a complete, tested prototype (read its README.md, +PROTOCOL.md, and ROADMAP.md first): + +- `src/lib/bns.js` — the resolver core: reads the "BNS1" protocol from BCH + chipnet via electrum (mainnet-js). Names are CashTokens name certificates; records are + JSON in OP_RETURN (`ip`, `u`, `p`, `h`, `s3`, `tls` types); discovery via a + beacon address. Subdomains map through the parent name (see bnsd.js). +- `src/daemon/bnsd.js` — reference gateway: serves `h` (on-chain HTML), `p` + (reverse proxy), `s3` (Sia storage via an S3 endpoint configured in + `sia-s3.json`), redirects `u`, and resolves `ip` records at DNS level. +- `ca/` — a local root CA ("BNS Local Root CA") that signs per-name certs; + fingerprints can be pinned via on-chain `tls` records. +- Live test names on chipnet right now: `hello.bch` (on-chain HTML), + `coinspectrum.bch` (ip → real nginx server), `siatest.bch` and + `coinspectrum.deviant.bch` (Sia-backed, subdomain namespace), + `theseus.bch` and `silentmode.bch` (on-chain HTML, brand pages). + +Reuse `bns.js` as a library (npm file: dependency or copied module) — do not +reimplement the protocol. The browser must NOT require the system daemon, +NRPT rules, or the OS trust store: everything in-process. + +## Requirements + +1. **Chrome-like shell**: tabs, address bar, back/forward/reload, history. + Electron BrowserViews/WebContentsViews per tab. +2. **Native .bch resolution**: typing `hello.bch` or `x.deviant.bch` resolves + via the BNS core inside the app (no OS DNS involvement). Everything else + browses the normal web unchanged. +3. **Record handling**: `h` render directly; `ip` connect to the host (http); + `p`/`s3` fetch via an in-app gateway on a loopback port; `u` redirect. +4. **TLS for .bch without touching the OS store**: intercept at the session + level (Electron `setCertificateVerifyProc`) — accept certs for .bch hosts + when they chain to the BNS root or match the name's on-chain `tls` + fingerprint; normal CA validation for everything else. +5. **UI truthfulness**: a visible indicator when a page came from the chain / + Sia / a direct ip record, showing the certificate category and record type — the + "padlock" for decentralized provenance. +6. **Fallback honesty**: unregistered .bch → a clean NXDOMAIN page naming the + chain; electrum outages → readable error, not a hang. +7. Package with electron-builder into a Windows installer (unsigned is fine). + +## Definition of done + +All four live names above load correctly in tabs alongside normal websites +(e.g. wikipedia.org), with provenance indicators, from a packaged .exe on +Windows 10 — no admin rights, no system daemon, no OS DNS or cert changes. + +## Constraints & notes + +- BCH chipnet; electrum servers as in mainnet-js defaults (see bns.js usage). +- `sia-s3.json` in D:\Dev\NameCoin holds the S3 endpoint + keys for `s3` + records (server-side s3d at https://coinspectrum.duckdns.org:8600) — copy + it into the browser's config directory; do not commit it. +- Wallet/registration UI is OUT of scope (exists in the NameCoin repo's + wallet app); this session is the *viewing* side only. +- Test names may be unconfirmed on chipnet — the resolver handles mempool + entries; height -1 is normal. diff --git a/DESIGN-hosting-pact.md b/DESIGN-hosting-pact.md new file mode 100644 index 0000000..fdb2538 --- /dev/null +++ b/DESIGN-hosting-pact.md @@ -0,0 +1,90 @@ +# Argo — the hosting pact: dilution-pool incentives for serving the decentralized web + +*Argo is the host network; the hosts who crew it are **Argonauts**. Part of the +Silent Mode stack (hub: `D:\Dev\SilentMode\README.md`).* + +Status: design idea (July 2026). Not yet implemented. Companion to +[PROTOCOL.md](PROTOCOL.md); assumes BNS names on BCH and content on a storage +network (Sia, IPFS, or on-chain `h` records). + +## The idea in one paragraph + +A site owner funds a **pool** tied to their name's content (monthly / quarterly +/ yearly). Hosts **join permissionlessly** — no one selects them — by bonding a +small stake and proving they store/serve the content. Each epoch the pool pays +out, **split among all current provers**. More hosts → thinner slices; fewer +hosts → fatter slices. Replication count is not configured anywhere: it is the +*equilibrium* of pool size vs. hosting cost, discovered continuously by the +market. Paying more is the owner's only knob — and it directly buys more +redundancy and serving capacity. Client software suggests a price from the +median of on-chain host asks. + +## How Sia works, for contrast + +Sia is **renter-driven, closed-membership**: the customer's agent (`renterd`) +scans the host market, *selects* ~30 specific hosts, forms bilateral fixed-term +contracts with each, monitors them, and migrates shards when hosts die. Hosts +advertise general capacity and wait to be picked; they cannot volunteer for a +specific dataset. Repair requires the renter's agent to be online and funded. + +## Why Sia's model is not enough (for websites) + +1. **A manager must exist.** Sia heals because *renterd* notices dead hosts + and re-uploads. If the renter agent is offline or unfunded, redundancy + silently decays. The pool heals **without any manager**: a dead host stops + proving → its share flows to the survivors → each slice fattens → the pool + becomes more attractive → new hosts join. The noticing is done by + economics, not by an agent. This is the core automation the idea adds. +2. **Membership is closed.** In Sia, a host that *wants* to help serve a + popular site cannot join its contract set. In the pool, joining is the + default; the algorithm that "decides who hosts" is replaced by + self-selection under price pressure (the same open-entry mechanism as + Bitcoin mining: reward fixed, participants join until marginal profit ~ 0). +3. **No serving incentive.** Sia pays for *storage possession*, never for + *serving the public* — the gap that killed Skynet (decentralized storage, + centralized unpaid portals). The pool's payout can cover gateways/seeders, + the actually-scarce resource for websites. +4. **Designed for private data.** Sia encrypts client-side and hides data from + hosts — right for backups, unnecessary for websites, which are public + goods. Public content is exactly the case where open membership is safe: + there is nothing to leak, and anyone may legitimately serve it. +5. **Lifetime is administrative, not economic.** Sia contracts expire per + contract term. A pool's site lives exactly as long as someone values it + enough to fund it — a market-driven expiry that doubles as the namespace + garbage collection Namecoin lacked (cf. Kalodner et al. 2015). + +## Known hard problems (and mitigations) + +- **Sybil splitting** — one disk posing as N hosts to collect N shares. + Mitigate: per-host-unique erasure-coded shards (k-of-n), so each identity + must store distinct data; stake per identity; diminishing returns per new + host as slices thin. +- **Proof of possession** — challenge from a future block hash → Merkle proof + over the host's shard. Verifiable in a BCH covenant post-May-2025 limits + (clunky but plausible); v0 can verify in an auditable coordinator. +- **Proof of serving** — unprovable on-chain (true for Filecoin/Sia too). + Layered approach: watchtower spot-checks, client-side reputation, coordinator + attestation; shrink the trusted surface over versions. +- **Pool exhaustion** — funding dries up → hosts leave → site dark. Feature, + not bug (see 5 above), but resolvers should surface "pool low" warnings so + owners/fans can top up (anyone may pay into a pool — patronage built in). + +## Sketch on BCH + +1. Site → storage network → per-host shards (off-chain tooling). +2. Name's chain record: `{"cid"|"s3": ..., "pact": }`. +3. Hosts post asks via beacon txs; wallets suggest the median (owner's knob). +4. Pool = covenant UTXO funded by owner (or anyone); hosts join by bonding + stake in a registration tx naming their shard commitment. +5. Per epoch: block-hash challenge → hosts submit proofs → covenant releases + the epoch tranche split among provers; missed proof = no pay (+ stake slash + on repeated failure). +6. Resolvers discover hosts from registrations and fetch from any of them. + +## Version ladder + +- **v0** — coordinator does audits + payouts openly on chipnet (trusted but + fully auditable; all money flows on-chain). +- **v1** — possession proofs verified by CashScript covenant; coordinator only + attests serving quality. +- **v2** — multi-watchtower serving attestation, coordinator eliminated. diff --git a/ENGINE.md b/ENGINE.md new file mode 100644 index 0000000..1175df7 --- /dev/null +++ b/ENGINE.md @@ -0,0 +1,47 @@ +# Argus — the engine + +**Argus built the Argo.** This folder is the shared core of the Silent Mode +stack — the protocol implementation and reference tooling that every product +(Ariadne the resolver, Theseus the browser, Argo the host network) is built +from. Named for the shipwright, guided by Athena, who built the ship the +Argonauts sailed. + +It is a **clone of `D:\Dev\NameCoin`**, brought into the SilentMode tree. + +## ⚠ Authority during the transition (read this) + +`D:\Dev\NameCoin` is still **LIVE / canonical** right now: +- the installed Windows resolver task points at `NameCoin\src\daemon\bnsd.js`; +- the VPS Sia auto-sync and `sia-s3.json` endpoint are tied to it; +- its `wallets.json` and `ca/` are the authoritative keys. + +**Rules until migration is finished:** +- Use **Argus for development** of new updates (safe: reads are harmless). +- Do **not** run `setup/install.ps1` from Argus yet (it would re-point the live + task at this copy before you intend it). +- Do **not** register/update names from Argus while NameCoin is also live — both + copies share the same chipnet wallet UTXOs; concurrent spends conflict. +- Treat NameCoin's `wallets.json` / `ca/root-ca.key` as the single source of + truth for keys. The copies here are a snapshot and may go stale. + +## Migration plan (step by step, later — after shipping updates) + +1. Develop and test new features here in Argus. +2. When ready to flip: stop using NameCoin, refresh Argus's `wallets.json`, + `ca/`, `sia-s3.json` from NameCoin (so keys are current), then run + `setup/install.ps1` **from Argus** (elevated) to re-point the resolver task. +3. Verify all live names still resolve + serve (padlock intact). +4. Update the VPS: no change needed unless the S3 endpoint moves. +5. Retire `D:\Dev\NameCoin` (archive, don't delete, until confident). + +## What's inside +Same layout as the reference README (`README.md` in this folder). Key files: +- `src/lib/bns.js` — protocol core (codec, indexer/resolver, tx builders) +- `src/daemon/bnsd.js` — DNS + HTTP/HTTPS gateway (h/p/s3/ip, subdomains, TLS) +- `src/lib/ca.js`, `src/lib/issue-cert.js` — the BNS local CA +- `setup/install.ps1`, `setup/uninstall.ps1` — Windows integration +- `src/register.js` / `update.js` / `resolve.js` — CLI +- `src/wallet-app.js` — wallet with embedded browser +- `PROTOCOL.md`, `ROADMAP.md`, `DESIGN-hosting-pact.md`, `BROWSER-PROMPT.md` + +Verified: `npm install` + `node src/resolve.js` lists all live chipnet names. diff --git a/OPERATIONS-electrum.md b/OPERATIONS-electrum.md new file mode 100644 index 0000000..ab589af --- /dev/null +++ b/OPERATIONS-electrum.md @@ -0,0 +1,122 @@ +# Running your own Fulcrum — and why the resolver wants you to + +Referenced from `src/registrar-config.js` (`REGISTRAR.selfHosted`). + +## The problem this solves + +Public electrum servers listen on **non-standard ports**: 50004 and 62002. On +2026-07-27 all three configured chipnet servers went TCP-closed from one machine +at the same instant, while `:443` stayed perfectly reachable. That is the +signature of a firewall or VPN policy, not an outage — and it is the normal +condition on corporate networks, on some mobile carriers, and behind many +consumer VPNs. + +The consequence is worse than it sounds. Every server in the list shares the same +two ports, so **one rule takes down the entire resolver**: no availability +search, no registration, no record updates, for that user, everywhere the +software runs — website, extension, Theseus, Ariadne. + +A server on 443 is indistinguishable from ordinary HTTPS traffic and is +essentially never blocked. **One server on 443 is worth more than three more on +50004.** + +## The second reason: not trusting one server + +A single electrum server is a trusted third party. It cannot forge a +registration — that needs a real CashTokens genesis whose commitment matches the +name — but it can **omit** one. Because the protocol rule is *"first valid REG +wins"*, a hidden REG makes a taken name look available: the buyer pays, mints a +duplicate certificate, and every other resolver rejects it. + +`ElectrumPool.unionHistory` defends against exactly this by taking the **union** +of beacon history across every server that answers, and reporting which servers +were short. Union rather than quorum, because omission is the attack: a server +showing you *more* history is giving you more truth, and two stale servers must +not be able to outvote one honest one. + +**So running your own server is not a centralisation trade.** Your server is the +fast, unblockable path; the public ones stay in the list as cross-checks. The +union means your own server cannot quietly hide a registration from you either. + +## Configuration + +```js +// src/registrar-config.js +selfHosted: "wss://bns.example.st:443", +``` + +That is all. The pool connects in parallel, prefers earlier entries, fails over +automatically, and unions the history regardless of order. + +## Running Fulcrum + +Fulcrum indexes a full node; you need both. + +1. **Bitcoin Cash Node** with the indexes Fulcrum needs: + ``` + txindex=1 + server=1 + rpcuser=… + rpcpassword=… + chipnet=1 # or omit for mainnet + ``` + +2. **Fulcrum**, pointed at it. The part that matters here is the **WSS port on + 443**: + ``` + bitcoind = 127.0.0.1:8332 + rpcuser = … + rpcpassword = … + ssl = 0.0.0.0:50002 + wss = 0.0.0.0:443 # <- the one that survives restrictive networks + cert = /etc/letsencrypt/live/bns.example.st/fullchain.pem + key = /etc/letsencrypt/live/bns.example.st/privkey.pem + ``` + +3. **A real certificate.** Browsers will not open a `wss://` connection with a + self-signed cert, and the failure is opaque — it surfaces as a generic + WebSocket error with no indication that the certificate is the cause. Use + Let's Encrypt. + +4. **Binding 443 needs privilege.** Either `setcap + 'cap_net_bind_service=+ep'` on the Fulcrum binary, or terminate TLS at nginx + and proxy to Fulcrum's plain WS port. The nginx route is usually easier + because it shares 443 with the rest of the site: + ```nginx + location /electrum { + proxy_pass http://127.0.0.1:50003; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_read_timeout 3600s; # electrum connections are long-lived + } + ``` + Then `selfHosted: "wss://bns.example.st:443/electrum"`. + +## Verifying it + +```bash +# reachable at all? +curl -sk -o /dev/null -w "%{http_code}\n" https://bns.example.st:443 + +# does it speak electrum? +cd Argus && node --input-type=module -e " +import WebSocket from 'ws'; +import { Electrum } from './src/lib/electrum.js'; +const el = await new Electrum('wss://bns.example.st:443', WebSocket).connect(); +console.log(await el.call('server.version', ['probe','1.4.2'])); +console.log('height:', (await el.call('blockchain.headers.subscribe')).height); +el.close(); process.exit(0);" +``` + +The second command is the one that matters — a server can answer HTTPS and still +not be a working electrum endpoint. + +## Note for the browser + +The register page can only reach electrum over **WSS**. It cannot open raw TCP, +so it can never talk to a P2P node directly, and it cannot use a plain-TCP +electrum port. Every entry in the list must be `wss://`. This is also why +SPV-grade *verification* (headers + merkle proofs over electrum) is the right +target for the web client rather than direct-to-node SPV — see +`Decentralized.DNS/DESIGN-trustless-purchase.md`. diff --git a/PROTOCOL.md b/PROTOCOL.md new file mode 100644 index 0000000..b41e99f --- /dev/null +++ b/PROTOCOL.md @@ -0,0 +1,95 @@ +# BNS1 — a minimal Namecoin-style name protocol on Bitcoin Cash (chipnet prototype) + +Names are anchored by **CashTokens name certificates** on the BCH chain; records travel in +**OP_RETURN** payloads; discovery uses a **beacon address** so a plain electrum +server is the only infrastructure a resolver needs. No fork, no sidechain. + +## Roles of the three mechanisms + +| Mechanism | Role | +|---|---| +| CashTokens certificate (immutable NFT) | *Ownership.* The certificate's commitment is the UTF-8 name. Whoever holds the certificate owns the name. Consensus guarantees nobody can mint an output of an existing category without spending it. | +| OP_RETURN payload | *Data.* `BNS1` prefix + JSON: the name's records (URL, inline HTML page, payment address). | +| Beacon dust output | *Discovery.* Every protocol tx pays 600 sat to the well-known beacon address, so `blockchain.address.get_history(beacon)` enumerates every registration/update ever made. | + +**Beacon (chipnet):** `bchtest:qp8njaa9a3ahg7259s5qajnce526n0rttsq8qwx4jl` + +## Transactions + +**REG** — one tx that simultaneously: +1. Mints an immutable certificate, commitment = UTF-8 name (≤ 40 bytes). Its category + (= token ID) becomes the name's permanent identity. +2. Carries `OP_RETURN "BNS1{...json...}"` with `{"o":"REG","n":,"r":}`. +3. Pays 600 sat to the beacon. + +**UPD** — one tx that: +1. Spends (moves) the name's certificate back to the owner — this is the ownership proof. + A vout carrying the name's category is only possible if a vin held it (consensus). +2. Carries `OP_RETURN "BNS1{...}"` with `{"o":"UPD","n":,"r":}`. +3. Pays 600 sat to the beacon. + +Transferring a name = sending the certificate to a new owner (no beacon tx needed). + +## Records (JSON, keep total payload ≤ 200 bytes) + +- `u` — https URL the name points to (gateway redirects) +- `h` — inline HTML: a tiny website stored fully on-chain +- `a` — cashaddr for payments (the "send to alice.bch" use case) +- `ip` — IPv4 of a server that answers for the .bch host directly (DNS answers + this instead of the local gateway) +- `p` — upstream URL the local gateway reverse-proxies (address bar stays .bch) +- `s3` — `bucket/key` (single object) or `bucket/prefix/` (site root) stored on + the Sia network, served by the gateway through a local s3d daemon + (credentials in sia-s3.json, not on-chain). Storage is decentralized and + encrypted; the retrieval door is the local daemon. +- `tls` — SHA-256 fingerprint (hex, over DER) of the name's TLS certificate. + The chain replaces the CA: identity comes from the certificate holder publishing + this record, encryption from ordinary TLS. Resolvers/extensions can pin it; + the prototype's trust path is the BNS Local Root CA (see ca/) which signs + per-name certs and is installed as a trust anchor by setup/install.ps1. + +## Resolver rules (indexer-enforced) + +1. Fetch beacon history, sort by `(height, txid)`; unconfirmed last. +2. **Then reorder causally:** never index a transaction before one it spends + from. Independent transactions keep the `txid` tiebreak. *(See "Ordering" + below — this rule is load-bearing, added 2026-07-26.)* +3. `REG`: valid if the same tx mints a certificate whose commitment matches the name + and the name is not yet taken. **First valid REG wins.** +4. `UPD`: valid if some output of the tx carries the name's category. +5. Names: `[a-z0-9-]{1,32}`, rendered as `.bch`. + +### Ordering (why rule 2 exists) + +`(height, txid)` alone is arbitrary within a block and within the mempool, and +that breaks the most ordinary sequence in the protocol: **register a name, then +immediately point it somewhere.** If the `UPD`'s txid happens to sort below its +own `REG`'s, the `UPD` is evaluated against an index that does not contain the +name yet, fails rule 4, and is silently dropped — *permanently*, if both land in +the same block, since the ordering never changes afterwards. + +Observed on chipnet 2026-07-26: `REG ca243c06…` for `smtest-fbe8cd.p2p` and its +`UPD 8da2cc4a…` were both unconfirmed; `8da2…` sorted first, and the record +never appeared even though the update transaction was entirely valid. + +The fix is a stable topological pass over the history: keep the `(height, txid)` +baseline, but hold back any transaction whose parents (within the beacon set) +have not been emitted yet. Cost is O(n²) over the beacon history, which is read +once per index build. Implemented identically in `src/lib/resolver-web.js` and +`src/lib/bns.js` — **the two must stay in lockstep**, since a disagreement +between them is a consensus split between clients. + +## Honest limitations (prototype vs production) + +- Uniqueness is **indexer policy**, not consensus. Production: a covenant-run + registry (CashScript) that consensus-enforces one certificate per name, or full + BCMR-style client-side validation of the genesis chain. +- Intra-block ordering uses txid as tiebreak among *independent* transactions + (electrum history has no tx index). Dependent transactions are ordered + causally — see "Ordering" above. +- The beacon is a keyed address (prototype convenience); production would use a + keyless constant script so no one can be pressured over it. +- No expiry/renewal: names live forever once minted (Namecoin used ~8-month + renewals to garbage-collect dead names). +- Front-running: a miner/mempool observer could see a REG and race it + (Namecoin's two-step `name_new` commit-reveal solved this; omitted here). diff --git a/README.md b/README.md new file mode 100644 index 0000000..704e263 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# BNS — Namecoin-style decentralized names on Bitcoin Cash (chipnet) + +A working prototype of "Option 3": names are **CashTokens name certificates** on the BCH +chain (no fork, no sidechain), records ride in OP_RETURN, discovery via a +beacon address, and the resolver is **embedded in a wallet app** with a +built-in browser pane — the Brave/MetaMask-browser architecture in miniature. + +See [PROTOCOL.md](PROTOCOL.md) for the on-chain design and its honest limitations. + +## Layout + +- `src/lib/bns.js` — protocol core: payload codec, chain indexer/resolver, tx builders +- `src/wallet-app.js` — wallet + embedded resolver + browser UI (`npm run wallet` → http://localhost:4626) +- `src/register.js` / `src/update.js` / `src/resolve.js` — CLI equivalents +- `wallets.json` — chipnet seed phrases (TESTNET ONLY — never reuse for real funds) +- `src/lib/gen-wallets.js` — one-time wallet generation (already run) + +## Setup + +1. `npm install` +2. Fund the wallet with free chipnet tBCH: go to https://tbch.googol.cash/, + paste the wallet address (shown in the app sidebar or by + `node src/lib/gen-wallets.js`), pick **chipnet**, solve the captcha. + ~0.001 tBCH funds hundreds of registrations. +3. `npm run wallet` and open http://localhost:4626 + +## Standalone resolver daemon (make EVERY browser resolve .bch) + +- `src/daemon/bnsd.js` — DNS server (answers `*.bch` from the chain, forwards + everything else upstream) + HTTP gateway that serves on-chain sites by Host + header. Test unprivileged: `node src/daemon/bnsd.js --dns-port 5354 --http-port 8081`, + then `node src/daemon/dig.js hello.bch`. +- `setup/install.ps1` — run from an **elevated** PowerShell. Installs the daemon + as a boot-time scheduled task (SYSTEM, auto-restart) and wires the OS to it: + - default (`-Mode nrpt`): adds an NRPT rule so only `.bch` queries reach the + daemon; all other DNS is untouched. + - `-Mode full`: points every adapter's DNS at 127.0.0.1 (daemon forwards + non-.bch upstream, ncdns-style); original servers are saved and restored + on uninstall. +- `setup/uninstall.ps1` — reverses everything (rule/adapters/task). + +HTTPS for .bch: the "BNS Local Root CA" (created on demand in `ca/`, keep +`root-ca.key` private) signs per-name certs. The daemon's :443 gateway mints +them on the fly for locally-served names; `node src/lib/issue-cert.js ` +issues one for a real server (see the coinspectrum.bch nginx vhost). The +installer trusts the root machine-wide and enables Firefox's +enterprise-roots policy; cert fingerprints are published in "tls" chain +records for pinning. + +Caveats: browsers with "secure DNS" (DoH) enabled bypass the OS resolver — +disable it or add an exception; ports 53/80/443 must be free. + +## Try it + +- Register `hello` with inline HTML (a website stored entirely on-chain), + then type `hello.bch` in the address bar — the app resolves it from the + blockchain (not DNS) and renders it. +- Or CLI: `node src/register.js hello '{"h":"

Served from the BCH chain

"}'` + then `node src/resolve.js hello` diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..a013fda --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,80 @@ +# BNS Roadmap — from prototype to a permissionless web stack + +*Silent Mode (a Deviant division). Hub: `D:\Dev\SilentMode\README.md`. +Products: **Theseus Navigator** (browser), publisher app, resolver daemon, +**Argo** (host network, crewed by **Argonauts**), **Argonautica** (discovery).* + +The four moving parts (see [DESIGN-hosting-pact.md](DESIGN-hosting-pact.md), +[PROTOCOL.md](PROTOCOL.md)): **chain** for ownership, **storage** for bulk, +**gossip** for liveness, **pool** for incentives. Phases are ordered by +dependency, not calendar; tracks 5 (browser) and 2 can run in parallel with +everything. + +## Phase 0 — Prototype (DONE, July 2026) + +Names as CashTokens name certificates on BCH chipnet; OP_RETURN records; beacon discovery; +resolver daemon + NRPT/adapter installer; HTTPS via local root CA with +on-chain fingerprint pinning; wallet app with embedded browser; real sites +served three ways (own server via ip record, reverse proxy, Sia storage via +s3d with server-side authority + 6h auto-sync); subdomain namespaces +(deviant.bch); dilution-pool design written. + +## Phase 1 — Harden the name layer (protocol v2) + +- Expiry + renewals (anti-squatting garbage collection; Kalodner 2015) +- Graduated pricing: short/dictionary names cost more (ENS lesson) +- Commit-reveal registration (front-running protection, Namecoin's name_new) +- CashScript covenant registry: consensus-enforced uniqueness (novel on BCH) +- Constrained CA (X.509 name constraints limited to .bch) + short-lived leaf certs +- Mainnet port (config only: network + electrum servers + real BCH) + +## Phase 2 — Distribution (reach other machines) + +- Firefox extension (Mozilla-signed, self-distributed): intercepts .bch, + routes via resolver/gateway — no DNS surgery, no DoH conflict +- Chromium extension (store review) +- Signed Windows installer; macOS/Linux daemon ports +- LAN mode: daemon answers with host-LAN IP so phones/TVs resolve .bch +- Android: resolver+gateway as an app (sideload APK first) + +## Phase 3 — Publisher app ("the missing UX") + +One window: pick folder → pick name → pay → site is live. Under the hood: +- Sia Storage SDK (or s3d) for storage; BNS txs for the name +- Auto-sync scheduling (generalize the coinspectrum systemd timer) +- BCH-denominated plans (Architecture A): user pays BCH; app funds storage + via Sia's market; BCH surplus funds the serving layer +- This is where ordinary users enter; everything before it is plumbing + +## Phase 4 — Argonautica: dynamic discovery (live sites without servers) + +- Chain record gains the owner's signing pubkey +- Site updates = feed of author-signed records (ZeroNet/Nostr pattern); + resolvers verify signatures and apply updates — no chain tx per update +- Multi-author records (comments, reactions) as CRDT merges (add-wins sets) +- Law to respect: gossip only for conflict-free facts; anything "exactly-one" + stays on the chain + +## Phase 5 — Argo: the hosting pact (dilution pool, Architecture B) + +- v0: coordinator audits + pays openly on chipnet; hosts run our host agent; + per-host-unique erasure shards (sybil resistance); stake bonding +- v1: possession proofs (block-hash challenge → Merkle proof) verified by a + CashScript covenant; coordinator only attests serving quality +- v2: watchtower attestation replaces coordinator; open-join equilibrium + replication fully market-driven +- Result: BCH-native host network; Sia becomes one backend among several + +## Track 5 (parallel) — Theseus Navigator, the independent browser + +See BROWSER-PROMPT.md — built in its own session/repo (`D:\Dev\BnsBrowser`), +consuming this repo's resolver core as a library. `theseus.bch` is registered +and should eventually serve the browser's own homepage over BNS itself. + +## Standing risks + +- Chromium ignores NRPT → extension/full-mode/browser are the mitigations +- s3d instances don't share object indexes → one authority instance per account +- .bch collision with a future ICANN gTLD → long-term: IETF special-use + reservation (the .onion path) +- Root CA key custody until name constraints land diff --git a/ariadne.config.json b/ariadne.config.json new file mode 100644 index 0000000..bf8b167 --- /dev/null +++ b/ariadne.config.json @@ -0,0 +1,17 @@ +{ + "_comment": "Ariadne / BCNR resolver settings. CLI flags override these values. Edit and restart the daemon (or the 'BNS Resolver Daemon' service) to apply. A settings UI will edit this file.", + "upstream": "1.1.1.1", + "dnsPort": 53, + "httpPort": 80, + "httpsPort": 443, + "gateway": "https://navigate.st", + + "_bind": "Listen address for the HTTP/HTTPS gateway. Loopback by default: on an end-user machine this is a LOCAL resolver, and binding every interface exposes it to the whole LAN. A public gateway sets this to 0.0.0.0 deliberately.", + "bind": "127.0.0.1", + + "_dnsBind": "Listen address for the DNS server, kept separate from 'bind' on purpose. A recursive resolver reachable from the internet is a DDoS amplification relay, so opening the gateway must never open DNS as a side effect. Leave this on loopback.", + "dnsBind": "127.0.0.1", + + "_tlds": "TLDs the local CA may issue TLS certificates for. Resolution stays TLD-agnostic; only issuance is enumerated, because the root CA is name-constrained and X.509 constraints need a finite list. CHANGING THIS REQUIRES REGENERATING THE ROOT CA and re-importing it on every machine (uninstall.ps1 -> delete ca/ -> install.ps1); until then clients reject certificates for the added TLDs. Never add an ICANN-delegated TLD. Omit this key to use the built-in default in src/lib/tlds.js.", + "tlds": ["bch", "p2p", "bit", "deviant", "nav", "test", "bcnr"] +} diff --git a/build-web.mjs b/build-web.mjs new file mode 100644 index 0000000..a10227c --- /dev/null +++ b/build-web.mjs @@ -0,0 +1,186 @@ +// Build the browser bundles for the public register page. +// +// SECURITY.md rule 3 applies: no source maps, no build-machine paths, no +// identity strings in anything shipped. This script bundles, then greps the +// output for the things that must never leave the machine (rule 0) and for +// home-directory fingerprints, and refuses to write a bundle that trips either. +// +// Run: node build-web.mjs + +import { build } from "esbuild"; +import { readFileSync, writeFileSync, mkdirSync, statSync } from "node:fs"; +import { createHash } from "node:crypto"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const root = dirname(fileURLToPath(import.meta.url)); +const siteJs = join(root, "..", "site", "js"); +mkdirSync(siteJs, { recursive: true }); + +// Structural fingerprints that must never appear in a shipped artifact +// (SECURITY.md rule 3). Deliberately NOT matching bare words like "secretKey": +// every crypto library in the tree uses that as a local identifier, and a check +// that cries wolf on nostr-tools is a check people learn to ignore. +const FORBIDDEN = [ + /C:\\Users\\[A-Za-z0-9._-]+/i, + /\/home\/[A-Za-z0-9._-]+\//, + /\/Users\/[A-Za-z0-9._-]+\//, + /\bsia-s3\.json\b/i, + /\bwallets\.json\b/i, + /\broot-ca\.key\b/i, + /BEGIN [A-Z ]*PRIVATE KEY/, +]; + +// The check that actually matters (rule 0): read the real secrets and prove +// none of their VALUES made it into the bundle. Values, not variable names. +// Nothing is ever printed — only whether a match occurred. +function realSecretValues() { + const values = []; + const collect = (v) => { + if (typeof v === "string" && v.length >= 8) values.push(v); + else if (v && typeof v === "object") Object.values(v).forEach(collect); + }; + for (const f of ["sia-s3.json", "wallets.json", "setup/install-state.json"]) { + try { collect(JSON.parse(readFileSync(join(root, f), "utf8"))); } catch { /* absent is fine */ } + } + try { + const ca = readFileSync(join(root, "ca", "root-ca.key"), "utf8"); + values.push(...ca.split("\n").filter((l) => l.length > 20)); + } catch { /* absent is fine */ } + return values; +} + +const SECRETS = realSecretValues(); + +// libauth is shared by every bundle via the page's import map — see +// src/web/libauth-entry.js for why this must not be duplicated. +const SHARED = ["@bitauth/libauth"]; + +const targets = [ + { + entry: join(root, "src", "web", "libauth-entry.js"), + out: join(siteJs, "libauth.js"), + minify: true, + note: "libauth — MIT, shared by every bundle (import map target)", + }, + { + entry: join(root, "src", "web", "register-entry.js"), + out: join(siteJs, "bns-register.js"), + minify: true, + external: SHARED, + note: "registration core — MIT, safe to minify", + }, + { + entry: join(root, "src", "lib", "connect", "wizardconnect.js"), + out: join(siteJs, "wizardconnect.js"), + // NOT MINIFIED, ON PURPOSE. @wizardconnect/* is LGPL-3.0-or-later; §4(d) + // requires that a user be able to swap in their own build of the library. + // Shipping it as a separate, readable ES module satisfies §4(d)(1) ("a + // suitable shared library mechanism") with no relink paperwork. Minifying + // or folding this into the main bundle would break that. See + // Decentralized.DNS/WIZARDCONNECT-LICENSE-FINDING.md. + minify: false, + external: SHARED, + note: "WizardConnect — LGPL-3.0-or-later, kept separate and unminified (§4d1)", + licences: ["lgpl-3.0", "gpl-3.0"], + }, +]; + +let failed = false; + +for (const t of targets) { + await build({ + entryPoints: [t.entry], + outfile: t.out, + bundle: true, + format: "esm", + platform: "browser", + target: ["es2022"], + minify: t.minify, + external: t.external ?? [], + sourcemap: false, + legalComments: "none", + // Keep the build reproducible and path-free. + absWorkingDir: root, + define: { "process.env.NODE_ENV": '"production"' }, + }); + + const code = readFileSync(t.out, "utf8"); + for (const re of FORBIDDEN) { + const hit = code.match(re); + if (hit) { + console.error(`REFUSING ${t.out}: contains "${hit[0]}" (SECURITY.md rule 3)`); + failed = true; + } + } + for (const secret of SECRETS) { + if (code.includes(secret)) { + // Never echo the value. + console.error(`REFUSING ${t.out}: contains a live secret value (SECURITY.md rule 0)`); + failed = true; + break; + } + } + // LGPL §4(b)/(c): the licence texts must travel with the distribution. + for (const l of t.licences ?? []) { + const path = join(root, "..", "site", "vendor", `LICENSE.${l}.txt`); + try { + if (statSync(path).size < 1000) throw new Error("too small"); + } catch { + console.error(`REFUSING ${t.out}: site/vendor/LICENSE.${l}.txt is missing — LGPL §4(b) requires it`); + failed = true; + } + } + + const size = statSync(t.out).size; + const sha = createHash("sha256").update(readFileSync(t.out)).digest("hex"); + console.log(`${t.out.replace(root, ".")} ${(size / 1024).toFixed(1)} kB`); + console.log(` sha256 ${sha}`); + console.log(` ${t.note}`); +} + +// --------------------------------------------------------------------------- +// Verify the shared-libauth contract. +// +// libauth-entry.js re-exports a hand-picked list so tree-shaking still works. +// The risk is shipping a bundle (or a dependency inside one) that imports a name +// the shared module does not export — a runtime failure in a path unit tests +// cannot reach. So: read the imports esbuild actually emitted and prove every +// one of them is satisfied. +// --------------------------------------------------------------------------- +{ + const shared = readFileSync(join(siteJs, "libauth.js"), "utf8"); + // The final `export{a as b,...}` of the emitted ESM module. + const provided = new Set( + [...shared.matchAll(/export\s*\{([^}]*)\}/g)] + .flatMap((m) => m[1].split(",")) + .map((part) => part.trim().split(/\s+as\s+/).pop()) + .filter(Boolean), + ); + + const required = new Map(); // name -> which bundle wants it + for (const t of targets) { + if (t.out.endsWith("libauth.js")) continue; + const code = readFileSync(t.out, "utf8"); + for (const m of code.matchAll(/import\s*\{([^}]*)\}\s*from\s*["']@bitauth\/libauth["']/g)) { + for (const part of m[1].split(",")) { + // `import {a as b}` — `a` is the name the shared module must export. + const name = part.trim().split(/\s+as\s+/)[0].trim(); + if (name) required.set(name, t.out); + } + } + } + + const missing = [...required].filter(([name]) => !provided.has(name)); + if (missing.length) { + for (const [name, who] of missing) { + console.error(`MISSING from src/web/libauth-entry.js: "${name}" (needed by ${who.replace(root, ".")})`); + } + failed = true; + } else { + console.log(`\nshared libauth: ${provided.size} exports, all ${required.size} imports satisfied`); + } +} + +if (failed) process.exit(1); +console.log("bundles clean (no secrets, no build-path fingerprints)"); diff --git a/namespaces.pdf b/namespaces.pdf new file mode 100644 index 0000000..8e1d6f7 Binary files /dev/null and b/namespaces.pdf differ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..fed70df --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2004 @@ +{ + "name": "namecoin", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "namecoin", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-s3": "^3.1093.0", + "@bitauth/libauth": "^3.1.0-next.8", + "@wizardconnect/core": "^0.2.4", + "@wizardconnect/dapp": "^0.2.2", + "express": "^5.2.1", + "mainnet-js": "^4.0.0-next.13", + "node-forge": "^1.4.0" + }, + "devDependencies": { + "esbuild": "^0.28.1" + } + }, + "node_modules/@aws-sdk/checksums": { + "version": "3.1000.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1000.19.tgz", + "integrity": "sha512-Hc4N100RdkuWshKBnhPzmpdftfi9mCLz+OHFELHM1QIgMH4QRUUWyWgfiebta/YX2Bd62wTcm3EqAP8TeXv0gA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.1093.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1093.0.tgz", + "integrity": "sha512-7452vEdp/nihIBWijnmcTBujXEFfbs4F02wyBDGqmNr6pwyo5GmQorx0zQIVg8QGFLXiBvsWKXBhCdiBcxNnGA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/checksums": "^3.1000.19", + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/credential-provider-node": "^3.972.71", + "@aws-sdk/middleware-sdk-s3": "^3.972.65", + "@aws-sdk/signature-v4-multi-region": "^3.996.41", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/fetch-http-handler": "^5.6.6", + "@smithy/node-http-handler": "^4.9.6", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.976.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.976.0.tgz", + "integrity": "sha512-0cjRaEdlVoOrsNb9pP5q1Syyc8pXw5xSj2Np2ryReRTr9FppIIRVSdZK4lbnfmc2Hvgux/xBOUU6baB7z8//uA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.2", + "@aws-sdk/xml-builder": "^3.972.36", + "@aws/lambda-invoke-store": "^0.3.0", + "@smithy/core": "^3.29.4", + "@smithy/signature-v4": "^5.6.5", + "@smithy/types": "^4.16.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.60", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.60.tgz", + "integrity": "sha512-BAkxdoe7tpDDqCghGpuOeHQRbm/2znVvOQm0AvpQbA2tbfMN46doN4zx65fv85ImP3KADwc2zQPmbrlI9MPfMg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.62", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.62.tgz", + "integrity": "sha512-g/0fGqKTb9xpKdd9AtpmV5Eo3DFKbnkpA2+w0peISSlu7NfAoWOuYBFxsu+yWBtxU89ka55ezoZBCbFaS8pjYQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/fetch-http-handler": "^5.6.6", + "@smithy/node-http-handler": "^4.9.6", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.973.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.5.tgz", + "integrity": "sha512-ylubazcRfq2TVus/qXucSXeC42Qdjp5HQxTu68K/BsdMiZlcSLD1zkpoCgApXZX1Y6YJhtGGs7ZHhO/GuIgBlw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/credential-provider-env": "^3.972.60", + "@aws-sdk/credential-provider-http": "^3.972.62", + "@aws-sdk/credential-provider-login": "^3.972.67", + "@aws-sdk/credential-provider-process": "^3.972.60", + "@aws-sdk/credential-provider-sso": "^3.973.4", + "@aws-sdk/credential-provider-web-identity": "^3.972.66", + "@aws-sdk/nested-clients": "^3.997.34", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/credential-provider-imds": "^4.4.9", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.67", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.67.tgz", + "integrity": "sha512-CCygIKJ9YbI3n84OClSaSppkgKKHVj2TGT33c6FRORZrYNZQ1POmD+ip0FLYokiJAK7sSdc3YVkOsBm90oxWMQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/nested-clients": "^3.997.34", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.71", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.71.tgz", + "integrity": "sha512-HIg7Q2osBzajQwL+1Vkyh2E7Gim3eTNb9RHIsOxDGjW0eZg4oEKtRs5sioCnc73ilhaOm4gX2lHVF8J7+nt2rg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.60", + "@aws-sdk/credential-provider-http": "^3.972.62", + "@aws-sdk/credential-provider-ini": "^3.973.5", + "@aws-sdk/credential-provider-process": "^3.972.60", + "@aws-sdk/credential-provider-sso": "^3.973.4", + "@aws-sdk/credential-provider-web-identity": "^3.972.66", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/credential-provider-imds": "^4.4.9", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.60", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.60.tgz", + "integrity": "sha512-YIo3f99hM43QdYG8hDzwGemnR/pU95b0kramqSJUTleCqaB7+HwKf7YZFHqvOgTqZTPx/mRmNIqoDRr3U0Z3Tw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.973.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.4.tgz", + "integrity": "sha512-BPdmL8sSBOCv4ngZ+3LHxyc3CNqDCEK37CHioCk7zGrTMY5sUtkH8q+o6qA80nn6w3/fyBPGNE7OIRlmoOxRQA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/nested-clients": "^3.997.34", + "@aws-sdk/token-providers": "3.1092.0", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.66", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.66.tgz", + "integrity": "sha512-kSAziJboOmZmsR9/MTbiNjowl2BPes1bQuJpne4qAZ62ubi8fjfr/aupJSQje6udBoYxXTQbsL0e0kby2la3ng==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/nested-clients": "^3.997.34", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.65", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.65.tgz", + "integrity": "sha512-udwNhRfDTfCB98mAHjjgsnKQlxygB4e0X+Obne/XjJpvVsF0YCQC8ZErd/8Z6IPoLQjtiKHzwqEDbZiLrJEnOg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/signature-v4-multi-region": "^3.996.41", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.34", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.34.tgz", + "integrity": "sha512-Y9REVrSwmLM+Qy6sZJ7ofMC2S3Hr3tPP/4CzL5U1olPP7OGoF+6+Px0E49cVQBtSxJtyeLJMf0UaBErfeSahAA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/signature-v4-multi-region": "^3.996.41", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/fetch-http-handler": "^5.6.6", + "@smithy/node-http-handler": "^4.9.6", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.41", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.41.tgz", + "integrity": "sha512-QMUytg+FQMGouc8gHS00KoYih3+N6cqmVI/pQGOIo7Nr7OpQaiXjSYOuL+vsPZ1tymY4LAQ8MYcHJmws5LRxng==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.2", + "@smithy/signature-v4": "^5.6.5", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1092.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1092.0.tgz", + "integrity": "sha512-hBYUAr6iBLNFcsiWTgtBb0stdSw39VOUq4Sp4A5caCNf66BAZplWN4FleKrVpJx5li2YgdnK2DqoFSMWC642FQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.976.0", + "@aws-sdk/nested-clients": "^3.997.34", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.29.4", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.974.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.2.tgz", + "integrity": "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.36", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.36.tgz", + "integrity": "sha512-RdGmS1GLrtaTOLE1ElSluMldNrpk9Emq6uYs8SS8iHlu5xTAmM9rRkM91o48+rIRryBtyO9t+uLYCoMG6jVMVA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz", + "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@bch-wc2/interfaces": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@bch-wc2/interfaces/-/interfaces-0.0.8.tgz", + "integrity": "sha512-gRt95azHVqoViGI4X3F1ObgTpYBh3//KaCHRBWSMJbAKNM09T+TA/h9kx9u9SZmE8otMtpFSy4awSHWHUQZmbg==", + "license": "MIT", + "peerDependencies": { + "@bitauth/libauth": "^3.1.0-next.2" + } + }, + "node_modules/@bitauth/libauth": { + "version": "3.1.0-next.8", + "resolved": "https://registry.npmjs.org/@bitauth/libauth/-/libauth-3.1.0-next.8.tgz", + "integrity": "sha512-Pm+Ju+YP3JeBLLTiVrBnia2wwE4G17r4XqpvPRMcklElJTe8J6x3JgKRg1by0Xm3ZY6UFxACkEAoSA+x419/zA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@noble/ciphers": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-2.1.1.tgz", + "integrity": "sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", + "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@rpckit/core": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@rpckit/core/-/core-2.2.0.tgz", + "integrity": "sha512-CvPzjH0dG26zIldubGv9omRaibf9qLNadDRcxQthLlhUmTgKmI5eyKrGou52RN1/tBKCd4Ybwz2l5U5WQwb4aA==", + "license": "MIT" + }, + "node_modules/@rpckit/fallback": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@rpckit/fallback/-/fallback-2.2.0.tgz", + "integrity": "sha512-X878VeShennvdtk8fGbPM0GTyrpDlKJx7YxjmMLz0ZhKXxFbz+hgGEqg3csvUDJOOPSvkZas+H4mJiIvpXYNOQ==", + "license": "MIT", + "dependencies": { + "@rpckit/core": "^2.2.0" + } + }, + "node_modules/@rpckit/websocket": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@rpckit/websocket/-/websocket-2.2.0.tgz", + "integrity": "sha512-Ha+rXyX2NMalmDs3jVaP+5M0rzTGKl4EStDxB7RuxdATRiQwQDmFofbkKwTzYQU/DuiGrvTYMu8VyivwHoAYbQ==", + "license": "MIT", + "dependencies": { + "@rpckit/core": "^2.2.0", + "isows": "^1.0.6" + } + }, + "node_modules/@scure/base": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz", + "integrity": "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-2.0.1.tgz", + "integrity": "sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA==", + "license": "MIT", + "dependencies": { + "@noble/curves": "2.0.1", + "@noble/hashes": "2.0.1", + "@scure/base": "2.0.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-2.0.1.tgz", + "integrity": "sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1", + "@scure/base": "2.0.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@smithy/core": { + "version": "3.29.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.29.7.tgz", + "integrity": "sha512-BiEE2bnnGoPKdlGe3L+gOYORDHFGPuYVRLP7iUow/Sflm0B4hC4XY3FC1MRuc7ltzpW2xNnXopKi34TTkULlKQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.12.tgz", + "integrity": "sha512-ZZPDbl/aRp77aycuoMlo3BTayT4CE2a3uoqETYZU5ySnVbhpl5IJiY7dCZedn+ZusyDLqVv44IvKBiXd2/nK0Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.29.7", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.6.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.9.tgz", + "integrity": "sha512-EJktha5m5MXCwzdXrlWyqb9UCNHNFKlg+PmTpRsdX3dncJPTiqYleM9OKj2mLgdVJHR01d2tU4alG+z2NdH5rQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.29.7", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.9.9", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.9.tgz", + "integrity": "sha512-xVBZ3hptB99iNO9XyWqEhC7KD9bP9UPXhuy3h5Y2ItCfBv160D9IIC/Fmmp3EbnWwit4C+KVqlSE+E29Nk/pPg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.29.7", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.6.8", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.6.8.tgz", + "integrity": "sha512-iGBm6hIwD2MGvVRSgrjVWa4FXtXDq3akxu0DCpnkmBo0xtEHZ/siMRt7ycfZAefYr2UdywUgmGtoRLaq5u56pg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.29.7", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.16.1.tgz", + "integrity": "sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@wizardconnect/core": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@wizardconnect/core/-/core-0.2.4.tgz", + "integrity": "sha512-CHcm3YWEdZHGSH6+Ml2H8LE4HCe2DWKX6PbzvGIaExYg1LGbiTXABPoBWMhCOibcQFz3BIgjfcewP29M0JFrUw==", + "dependencies": { + "@bch-wc2/interfaces": "^0.0.8", + "@bitauth/libauth": "^3.1.0-next.2", + "eventemitter3": "^5.0.1", + "isomorphic-ws": "^5.0.0", + "lossless-json": "^4.3.0", + "nostr-tools": "^2.23.0", + "ws": "^8.18.0" + } + }, + "node_modules/@wizardconnect/dapp": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@wizardconnect/dapp/-/dapp-0.2.2.tgz", + "integrity": "sha512-A71y5Qg5SIdIjXNngfpgBGxuQFgWRLQOigFa7zRX8HCoOh/62yOBfQUFtQDOgfRtGV7jv8nZ1Xb6wFqXVoEQvg==", + "dependencies": { + "@wizardconnect/core": "*", + "eventemitter3": "^5.0.1" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/lossless-json": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lossless-json/-/lossless-json-4.3.0.tgz", + "integrity": "sha512-ToxOC+SsduRmdSuoLZLYAr5zy1Qu7l5XhmPWM3zefCZ5IcrzW/h108qbJUKfOlDlhvhjUK84+8PSVX0kxnit0g==", + "license": "MIT" + }, + "node_modules/mainnet-js": { + "version": "4.0.0-next.13", + "resolved": "https://registry.npmjs.org/mainnet-js/-/mainnet-js-4.0.0-next.13.tgz", + "integrity": "sha512-mn5CSau3sSRqCYSr26/xKQO1+dkPWEyr7DXdICu6UDnRbZkHzhzPH0J1tt6nuxd21pNKFEZEOtsi/SpkuxxUKQ==", + "license": "MIT", + "dependencies": { + "@bitauth/libauth": "^3.1.0-next.8", + "@rpckit/core": "^2.2.0", + "@rpckit/fallback": "^2.2.0", + "@rpckit/websocket": "^2.2.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-forge": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/nostr-tools": { + "version": "2.24.1", + "resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.24.1.tgz", + "integrity": "sha512-KdrKjC74n/rr6J3eCSfZj8dcbZFvolHYe4S22SefNZ5YWbhHiB0KL/mmJjEZ0u6B9mZK0YcQtl+WQ46KzwapeQ==", + "license": "Unlicense", + "dependencies": { + "@noble/ciphers": "2.1.1", + "@noble/curves": "2.0.1", + "@noble/hashes": "2.0.1", + "@scure/base": "2.0.0", + "@scure/bip32": "2.0.1", + "@scure/bip39": "2.0.1", + "nostr-wasm": "0.1.0" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/nostr-wasm": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz", + "integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==", + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..83c52d3 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "namecoin", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "wallet": "node src/wallet-app.js", + "register": "node src/register.js", + "resolve": "node src/resolve.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "dependencies": { + "@aws-sdk/client-s3": "^3.1093.0", + "@bitauth/libauth": "^3.1.0-next.8", + "@wizardconnect/core": "^0.2.4", + "@wizardconnect/dapp": "^0.2.2", + "express": "^5.2.1", + "mainnet-js": "^4.0.0-next.13", + "node-forge": "^1.4.0" + }, + "devDependencies": { + "esbuild": "^0.28.1" + } +} diff --git a/setup/install.ps1 b/setup/install.ps1 new file mode 100644 index 0000000..b03f80d --- /dev/null +++ b/setup/install.ps1 @@ -0,0 +1,163 @@ +# BNS resolver daemon installer — run from an ELEVATED PowerShell. +# .\setup\install.ps1 # surgical: NRPT rule routes only .bch to the daemon +# .\setup\install.ps1 -Mode full # universal: adapter DNS -> 127.0.0.1, daemon forwards the rest +param( + [ValidateSet("nrpt", "full")] [string]$Mode = "nrpt", + [string]$Upstream = "1.1.1.1", + # Opt-in: turn off Firefox DNS-over-HTTPS. Only needed to resolve names that + # ALSO exist in ICANN (a reclaimed .de) via BCNR in Firefox — BCNR-only names + # (all .bch, etc.) already work with DoH on, because DoH returns NXDOMAIN and + # Firefox falls back to the local resolver. Trade-off: Firefox's other DNS then + # goes through the system resolver instead of encrypted to Cloudflare. + [switch]$FirefoxNoDoH, + # Opt-in: force-close a running Firefox so it reloads the new root CA + policy + # immediately (Firefox only reads them at startup). Off by default because it + # discards the user's open tabs/unsaved data. + [switch]$CloseFirefox +) +$ErrorActionPreference = "Stop" +if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() + ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + throw "Run this from an elevated (Administrator) PowerShell." +} + +$root = Split-Path -Parent $PSScriptRoot +# Prefer a bundled Node runtime (distributable installer ships runtime\node.exe); +# fall back to a Node on PATH for running straight from the dev repo. +$node = Join-Path $root "runtime\node.exe" +if (-not (Test-Path $node)) { + $onPath = Get-Command node -ErrorAction SilentlyContinue + if (-not $onPath) { throw "No Node runtime found: expected runtime\node.exe (bundle) or node on PATH (dev repo)." } + $node = $onPath.Source +} +$daemon = Join-Path $root "src\daemon\bnsd.js" +$stateFile = Join-Path $PSScriptRoot "install-state.json" + +# Generate the name-constrained local root CA now (on THIS machine; the key never +# ships) so the trust-store import below always finds a valid, constrained root +# instead of racing the daemon's first request. +Push-Location $root +& $node --input-type=module -e "import('./src/lib/ca.js').then(m=>m.loadOrCreateRootCA()).catch(e=>{console.error(String(e));process.exit(1)})" +Pop-Location + +# 0) Idempotency: clear any previous install of ours first +Get-DnsClientNrptRule | Where-Object Comment -eq "BNS .bch resolver" | ForEach-Object { + Remove-DnsClientNrptRule -Name $_.Name -Force +} +try { Stop-ScheduledTask -TaskName "BNS Resolver Daemon" -ErrorAction Stop } catch {} +try { Stop-ScheduledTask -TaskName "BNS Sia Bridge" -ErrorAction Stop } catch {} + +# 1) Scheduled task: run the daemon at boot as SYSTEM (survives logoff, binds port 53/80/443) +$action = New-ScheduledTaskAction -Execute $node -Argument "`"$daemon`" --upstream $Upstream" -WorkingDirectory $root +$trigger = New-ScheduledTaskTrigger -AtStartup +$settings = New-ScheduledTaskSettingsSet -RestartCount 5 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit (New-TimeSpan -Days 3650) +Register-ScheduledTask -TaskName "BNS Resolver Daemon" -Action $action -Trigger $trigger -Settings $settings ` + -User "SYSTEM" -RunLevel Highest -Force | Out-Null +Start-ScheduledTask -TaskName "BNS Resolver Daemon" +Write-Host "[ok] daemon installed as scheduled task 'BNS Resolver Daemon' and started" + +# 1b) Sia bridge (optional): serves "s3" records from the Sia network +$s3d = Join-Path $root "tools\s3d.exe" +if (Test-Path $s3d) { + # runs as the logged-on user (s3d's login state lives in the user's APPDATA) + $s3dAction = New-ScheduledTaskAction -Execute $s3d -WorkingDirectory $root + $s3dTrigger = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME + $s3dPrincipal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive + Register-ScheduledTask -TaskName "BNS Sia Bridge" -Action $s3dAction -Trigger $s3dTrigger ` + -Settings $settings -Principal $s3dPrincipal -Force | Out-Null + Start-ScheduledTask -TaskName "BNS Sia Bridge" + Write-Host "[ok] s3d Sia bridge installed (runs at your logon) and started" +} + +# 2) Route queries to it +$state = @{ mode = $Mode; adapters = @() } +if ($Mode -eq "nrpt") { + Add-DnsClientNrptRule -Namespace ".bch" -NameServers "127.0.0.1" -Comment "BNS .bch resolver" + Write-Host "[ok] NRPT rule added: .bch -> 127.0.0.1 (all other DNS untouched)" +} else { + # Reinstall-safe backup: if the machine is ALREADY pointed at us (127.0.0.1) from a + # prior install, the "current" servers are a useless restore target. Reuse the last + # good backup if we have one, else fall back to DHCP. NEVER record loopback as the + # original — that's exactly what left a past uninstall pointing DNS at a dead daemon. + $prior = @{} + if (Test-Path $stateFile) { + try { (Get-Content $stateFile | ConvertFrom-Json).adapters | ForEach-Object { $prior[$_.alias] = $_ } } catch {} + } + foreach ($if in Get-DnsClientServerAddress -AddressFamily IPv4 | + Where-Object { $_.InterfaceAlias -notmatch "Loopback" -and $_.ServerAddresses.Count -gt 0 }) { + $orig = @($if.ServerAddresses | Where-Object { $_ -ne '127.0.0.1' -and $_ -ne '::1' }) + if ($orig.Count -eq 0 -and $prior.ContainsKey($if.InterfaceAlias)) { + $orig = @($prior[$if.InterfaceAlias].servers | Where-Object { $_ -and $_ -ne '127.0.0.1' -and $_ -ne '::1' }) + } + $state.adapters += @{ alias = $if.InterfaceAlias; servers = $orig; resetToDhcp = ($orig.Count -eq 0) } + Set-DnsClientServerAddress -InterfaceAlias $if.InterfaceAlias -ServerAddresses 127.0.0.1 + $restoreTo = if ($orig.Count) { $orig -join ', ' } else { 'DHCP (automatic)' } + Write-Host "[ok] adapter '$($if.InterfaceAlias)' DNS -> 127.0.0.1 (restore target: $restoreTo)" + } +} +# 3) TLS trust anchor: BNS Local Root CA into the machine store, and tell +# Firefox (which keeps its own store) to honor Windows enterprise roots. +$caCert = Join-Path $root "ca\root-ca.crt" +if (Test-Path $caCert) { + # Refuse to trust a root that can vouch for the whole internet. Roots generated + # before 2026-07-26 carry no name constraints; installing one means anyone who + # can read ca\root-ca.key can forge a trusted certificate for any domain on this + # machine. Constrained roots are limited to the TLDs in src\lib\tlds.js. + $ncOid = "2.5.29.30" + $parsed = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($caCert) + $hasConstraints = @($parsed.Extensions | Where-Object { $_.Oid.Value -eq $ncOid }).Count -gt 0 + $parsed.Dispose() + if (-not $hasConstraints) { + Write-Host "" + Write-Host "[STOP] ca\root-ca.crt has NO X.509 name constraints." -ForegroundColor Red + Write-Host " Trusting it would let anyone who reads ca\root-ca.key forge a" -ForegroundColor Red + Write-Host " certificate for ANY domain on this machine, not just BNS names." -ForegroundColor Red + Write-Host " Rotate the CA, then re-run this installer:" -ForegroundColor Red + Write-Host " setup\uninstall.ps1 (removes the old root from the store)" -ForegroundColor Red + Write-Host " Remove-Item -Recurse $($root)\ca" + Write-Host " setup\install.ps1 (generates a constrained root)" -ForegroundColor Red + Write-Host "" + throw "refusing to install an unconstrained root CA" + } + $imported = Import-Certificate -FilePath $caCert -CertStoreLocation Cert:\LocalMachine\Root + $state.caThumbprint = $imported.Thumbprint + New-Item -Path "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\Certificates" -Force | Out-Null + Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\Certificates" ` + -Name "ImportEnterpriseRoots" -Value 1 -Type DWord + # Also install our specific root into Firefox's own store (works even if the + # user disabled enterprise-roots). Both policies are applied at Firefox startup. + New-Item -Path "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\Certificates\Install" -Force | Out-Null + Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\Certificates\Install" -Name "1" -Value $caCert -Type String + Write-Host "[ok] BNS Local Root CA trusted (machine store + Firefox enterprise-roots & install policies)" + # Firefox only loads the enterprise-roots policy + new root at startup, so a + # running Firefox keeps rejecting .bch until it is restarted. + $ff = Get-Process firefox -ErrorAction SilentlyContinue + if ($ff) { + if ($CloseFirefox) { + $ff | Stop-Process -Force + Write-Host "[ok] Firefox was closed — reopen it and .bch certificates will be trusted" + } else { + Write-Host "[!] Firefox is open — fully QUIT and reopen it so it trusts .bch (or re-run with -CloseFirefox)." -ForegroundColor Yellow + } + } +} else { + Write-Host "[skip] no ca\root-ca.crt found — https for .bch not enabled" +} + +# 3b) Firefox DNS-over-HTTPS. BCNR-only names (all .bch, etc.) already work in +# Firefox with DoH ON — DoH returns NXDOMAIN and Firefox falls back to the +# system resolver (us). Only names that ALSO exist in ICANN (a reclaimed .de) +# lose to the real record. Turning DoH off fixes those but sends Firefox's +# other DNS through the system resolver instead of encrypted to Cloudflare — +# so it's opt-in (-FirefoxNoDoH) and left unlocked so the user can re-enable. +if ($FirefoxNoDoH) { + New-Item -Path "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\DNSOverHTTPS" -Force | Out-Null + Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\DNSOverHTTPS" -Name "Enabled" -Value 0 -Type DWord + Write-Host "[ok] Firefox DNS-over-HTTPS disabled (opt-in) — ICANN-conflicting names now resolve via BCNR in Firefox too" +} else { + Write-Host "[info] Firefox DoH left on (BCNR-only names work anyway). To also reclaim names that exist in ICANN, re-run with -FirefoxNoDoH" +} + +$state | ConvertTo-Json -Depth 5 | Set-Content $stateFile +Clear-DnsClientCache +Write-Host "[done] try: https://hello.bch (uninstall: .\setup\uninstall.ps1)" diff --git a/setup/uninstall.ps1 b/setup/uninstall.ps1 new file mode 100644 index 0000000..5f795ec --- /dev/null +++ b/setup/uninstall.ps1 @@ -0,0 +1,62 @@ +# Reverses install.ps1 — run from an ELEVATED PowerShell. +$ErrorActionPreference = "Stop" +$stateFile = Join-Path $PSScriptRoot "install-state.json" + +Get-DnsClientNrptRule | Where-Object Comment -eq "BNS .bch resolver" | ForEach-Object { + Remove-DnsClientNrptRule -Name $_.Name -Force + Write-Host "[ok] NRPT rule removed" +} +if (Test-Path $stateFile) { + $state = Get-Content $stateFile | ConvertFrom-Json + foreach ($a in $state.adapters) { + # Never "restore" DNS to 127.0.0.1 (the daemon we're removing) — that leaves the + # machine with no working resolver. Drop loopback; if nothing real remains, or the + # install marked this adapter, hand DNS back to DHCP (automatic). + $servers = @($a.servers | Where-Object { $_ -and $_ -ne '127.0.0.1' -and $_ -ne '::1' }) + if ($a.resetToDhcp -or $servers.Count -eq 0) { + Set-DnsClientServerAddress -InterfaceAlias $a.alias -ResetServerAddresses -ErrorAction SilentlyContinue + Write-Host "[ok] adapter '$($a.alias)' DNS reset to automatic (DHCP)" + } else { + Set-DnsClientServerAddress -InterfaceAlias $a.alias -ServerAddresses $servers -ErrorAction SilentlyContinue + Write-Host "[ok] adapter '$($a.alias)' DNS restored to: $($servers -join ', ')" + } + } + Remove-Item $stateFile +} +# SAFETY SWEEP — always, even with a missing/stale state file. No adapter may be +# left pointing at 127.0.0.1 (or ::1) once the daemon is gone, or the machine loses +# ALL DNS ("server not responding"). This is the guarantee that a botched or +# state-less uninstall can never take the user offline. +foreach ($if in Get-DnsClientServerAddress -AddressFamily IPv4 | + Where-Object { $_.InterfaceAlias -notmatch 'Loopback' -and ($_.ServerAddresses -contains '127.0.0.1') }) { + Set-DnsClientServerAddress -InterfaceAlias $if.InterfaceAlias -ResetServerAddresses -ErrorAction SilentlyContinue + Write-Host "[fix] adapter '$($if.InterfaceAlias)' was still pointed at the resolver — reset to automatic (DHCP)" +} +# Remove the BNS root CA robustly — by SUBJECT, not only the thumbprint recorded +# in install-state.json. A missing or stale state file (the daemon was run by +# hand, or this is an upgrade from an older build) must never leave an orphaned +# trusted root in the machine store. +$removed = 0 +Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -match "BNS Local Root CA" } | ForEach-Object { + Remove-Item $_.PSPath -Force + Write-Host "[ok] BNS Local Root CA removed ($($_.Thumbprint))" + $removed++ +} +if ($removed -eq 0) { Write-Host "[ok] no BNS root CA left in the machine store" } +try { + Remove-Item -Path "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\Certificates" -Recurse -Force -ErrorAction Stop + Write-Host "[ok] Firefox certificate policies removed (enterprise-roots + install)" +} catch {} +try { + Remove-Item -Path "HKLM:\SOFTWARE\Policies\Mozilla\Firefox\DNSOverHTTPS" -Recurse -Force -ErrorAction Stop + Write-Host "[ok] Firefox DNS-over-HTTPS policy removed (DoH restored to default)" +} catch {} +foreach ($task in "BNS Resolver Daemon", "BNS Sia Bridge") { + try { Stop-ScheduledTask -TaskName $task -ErrorAction Stop } catch {} + try { + Unregister-ScheduledTask -TaskName $task -Confirm:$false -ErrorAction Stop + Write-Host "[ok] scheduled task '$task' removed" + } catch {} +} +Clear-DnsClientCache +Write-Host "[done] system DNS configuration restored" diff --git a/sia-test.html b/sia-test.html new file mode 100644 index 0000000..12a56fc --- /dev/null +++ b/sia-test.html @@ -0,0 +1,34 @@ + + + + + +siatest.bch — stored on Sia, named on Bitcoin Cash + + + +
+

This page has no server.

+

You are reading a file stored on the Sia decentralized host network — + erasure-coded into 30 encrypted pieces on independent hosts worldwide — + reached via a domain name that is an NFT on the Bitcoin Cash blockchain.

+
+
✔ Name: CashTokens NFT on BCH chipnet
+
✔ Resolution: local BNS daemon reading the chain
+
✔ Storage: Sia host network (via Sia Storage)
+
✔ TLS: BNS Local Root CA, fingerprint on-chain
+
✔ Registrar, host, CA involved: none
+
+
+ + diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..90e5cd2 --- /dev/null +++ b/src/config.js @@ -0,0 +1,7 @@ +export const BEACON = "bchtest:qp8njaa9a3ahg7259s5qajnce526n0rttsq8qwx4jl"; +export const PREFIX = "BNS1"; +export const TLD = ".bch"; +export const BEACON_DUST_SAT = 600; +export const NAME_RE = /^[a-z0-9-]{1,32}$/; +export const WALLET_FILE = new URL("../wallets.json", import.meta.url); +export const APP_PORT = 4626; diff --git a/src/daemon/bnsd.js b/src/daemon/bnsd.js new file mode 100644 index 0000000..cdd496e --- /dev/null +++ b/src/daemon/bnsd.js @@ -0,0 +1,266 @@ +// bnsd — system-wide BNS/BCNR resolver daemon (secret-free, multi-TLD). +// +// Two servers in one process: +// - DNS on udp/53: for every query it checks the BCNR chain index FIRST; if the +// name is registered (any TLD) it answers with the name's "ip" record, else +// 127.0.0.1 so the local gateway serves it. Every unregistered name is +// forwarded to an upstream resolver — i.e. BCNR-first, ICANN-fallback. That +// makes it safe to be the machine's system DNS. +// - HTTP/HTTPS gateway: routed by Host header; serves h / s3 / p / u records. +// Sia (s3) content is fetched from the PUBLIC gateway (navigate.st) — no S3 +// credentials ship. HTTPS is terminated with certs minted on the fly by the +// BNS local CA (generated on THIS machine at install; the root key never ships). +// +// Zero secrets by design: uses the wallet-free resolver-web.js reader (electrum +// over WebSocket) — no wallet, no mainnet-js, no S3 keys, no sia-s3.json. +// +// Usage: node bnsd.js [--dns-port 53] [--http-port 80] [--https-port 443] +// [--upstream 1.1.1.1] [--bind 127.0.0.1] [--dns-bind 127.0.0.1] +// +// --bind defaults to loopback. A PUBLIC gateway must pass --bind 0.0.0.0 +// explicitly; --dns-bind is deliberately separate so that doing so does not also +// expose the DNS server (an open resolver is a DDoS amplification relay). +import dgram from "dgram"; +import https from "node:https"; +import tls from "node:tls"; +import { Readable } from "node:stream"; +import { readFileSync, existsSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import express from "express"; +import WebSocket from "ws"; +import { buildIndex, normalizeName } from "../lib/resolver-web.js"; +import { issueCert } from "../lib/ca.js"; +import { isPermittedHost } from "../lib/tlds.js"; + +// ---------- configuration ---------- +// Effective value = CLI flag, else config file, else built-in default. So the +// installed program is fully file-configurable and a settings UI just edits +// ariadne.config.json (then restarts the service to apply). +const CONFIG_FILE = fileURLToPath(new URL("../../ariadne.config.json", import.meta.url)); +let cfg = {}; +try { + if (existsSync(CONFIG_FILE)) { cfg = JSON.parse(readFileSync(CONFIG_FILE, "utf8")); console.log(`bnsd config: loaded ${CONFIG_FILE}`); } +} catch (e) { console.error(`bnsd config: ${CONFIG_FILE} unreadable (${e.message}) — using defaults`); } +const arg = (name) => { const i = process.argv.indexOf("--" + name); return i > -1 ? process.argv[i + 1] : undefined; }; +const opt = (cli, key, dflt) => arg(cli) ?? cfg[key] ?? dflt; + +const DNS_PORT = Number(opt("dns-port", "dnsPort", 53)); +const HTTP_PORT = Number(opt("http-port", "httpPort", 80)); +const HTTPS_PORT = Number(opt("https-port", "httpsPort", 443)); +const UPSTREAM = opt("upstream", "upstream", "1.1.1.1"); +// Listen addresses. Both default to loopback: on an end-user machine this daemon +// is a local resolver, and binding it to every interface exposed it to the whole +// LAN. A public gateway (navigate.st) must opt in explicitly with --bind 0.0.0.0. +const BIND = opt("bind", "bind", "127.0.0.1"); +// DNS is bound separately and stays on loopback unless deliberately overridden: +// a recursive resolver reachable from the internet is an amplification relay for +// DDoS traffic, so --bind alone must never open it. +const DNS_BIND = opt("dns-bind", "dnsBind", "127.0.0.1"); +const GATEWAY = cfg.gateway ?? "https://navigate.st"; // public content relay (secret-free) +const TTL = 60; + +// Canonical BCNR index key ("