diff options
| author | Adam Soltys <[email protected]> | 2026-08-01 20:46:12 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-01 20:46:12 -0700 |
| commit | ed1d996f9dbd6cedc18ec293daba0cfcd745ae2b (patch) | |
| tree | 6d552796e899314d402dc92f6f80886405e296b5 /tools/dnssec-test.js | |
| parent | 5986cbf5337593eb11fd984d284bd41da7719a87 (diff) | |
| download | coinosv3-ed1d996f9dbd6cedc18ec293daba0cfcd745ae2b.tar.gz coinosv3-ed1d996f9dbd6cedc18ec293daba0cfcd745ae2b.zip | |
Zappable offer codes and per-name Lightning offers
CLINK offers (noffer): every wallet gets a static offer keypair (synced
across devices, first-minted wins) answering kind-21001 invoice
requests — from open tabs and from the service worker while closed,
which mints against the mirrored state (the record now carries chain-4
and chain-5 key windows) and raises an open-to-receive notification.
Zap requests ride along and their kind-9735 receipts publish once the
payment is claimed. The code is shown in the payment-address card for
pasting into a Nostr profile; the notifier watches and relays kind
21001 alongside NWC.
Payment names also gain a Lightning offer: the registrar creates a
BOLT 12 offer per name on the node and appends lno= to the DNS record;
a settle-forwarder pushes each received payment on to the name's ark
destination from a small float wallet (custody window: seconds between
Lightning settle and the ark send).
Name resolution now runs full client-side DNSSEC validation
(src/dnssec.js): binary DoH, root-anchor chain walk, RSA + ECDSA via
WebCrypto — the resolver is untrusted transport, verified against live
signed zones including the canonical BIP-353 test name.
Co-Authored-By: Claude Fable 5 <[email protected]>
Diffstat (limited to 'tools/dnssec-test.js')
| -rw-r--r-- | tools/dnssec-test.js | 39 |
1 files changed, 39 insertions, 0 deletions
diff --git a/tools/dnssec-test.js b/tools/dnssec-test.js new file mode 100644 index 0000000..71ebcee --- /dev/null +++ b/tools/dnssec-test.js @@ -0,0 +1,39 @@ +// DNSSEC chain validation against LIVE DNS. Needs network. +// Run: bun tools/dnssec-test.js +import { resolveTxtValidated } from '../src/dnssec.js'; + +let fails = 0; +const check = (name, ok, detail = '') => { + console.log(` ${ok ? '✓' : '✗'} ${name}${detail ? ' — ' + String(detail).slice(0, 140) : ''}`); + if (!ok) fails++; +}; + +// The canonical BIP-353 record: ₿[email protected] (alg 8 chain). +try { + const { txts } = await resolveTxtValidated('matt.user._bitcoin-payment.mattcorallo.com'); + const uri = txts.find((t) => t.toLowerCase().startsWith('bitcoin:')); + check('validates [email protected] (RSA chain)', !!uri, (uri || '').slice(0, 60) + '…'); +} catch (e) { check('validates [email protected] (RSA chain)', false, e.message); } + +// A Cloudflare-signed TXT (alg 13 chain): cloudflare.com SPF record. +try { + const { txts } = await resolveTxtValidated('cloudflare.com'); + check('validates cloudflare.com TXT (ECDSA chain)', txts.length > 0, txts[0]); +} catch (e) { check('validates cloudflare.com TXT (ECDSA chain)', false, e.message); } + +// An unsigned zone must FAIL closed, not pass quietly. +try { + await resolveTxtValidated('google.com'); // google.com is famously unsigned + check('unsigned zone fails closed', false, 'validated?!'); +} catch (e) { + check('unsigned zone fails closed', /not in a signed zone|no DNSKEY|fails/.test(e.message), e.message); +} + +// A missing record must throw, not return empty-and-valid. +try { + await resolveTxtValidated('definitely-not-real.user._bitcoin-payment.mattcorallo.com'); + check('missing record throws', false); +} catch (e) { check('missing record throws', true, e.message); } + +console.log(fails ? `\n❌ ${fails} failure(s)` : '\n✅ DNSSEC validation behaves'); +process.exit(fails ? 1 : 0); |
