The daemon's wallet scan hashed every watched script against every block
filter with a BigInt SipHash: ~3 filters/s on the VPS once the gap limits
went to 200/100 (~3,300 scripts), i.e. days for a one-year window. The
matcher now lives in dgbpow.wasm next to the PoW hashes — scripts are
uploaded once per scan, each filter is one call, the set is decoded once
and each target binary-searched — ~5,500 filters/s locally, ~2,000/s on
the VPS with block fetches. The JS path was rewritten with uint32 lanes
and exact 32×32 partial products as a fallback (module missing or filter
beyond its buffers). scripts/gcs-check.ts cross-checks both against a
BigInt reference on random filters; `npm run gcs:test -w @dgb-wallet/p2p`.
NOWNodes stopped accepting the proxy's API key ("Unknown API_key" on every
endpoint), which is why the wallet showed "Blockbook HTTP 401" for every
path. The vhost mirror now proxies /blockbook/ to Atomic Wallet's public
Blockbook; ops/blockbook-upstream.sh switches the live vhost either way.
162 lines
8 KiB
Text
162 lines
8 KiB
Text
# nginx vhost for digibyte.x on the coinspectrum VPS — the LIVE layout.
|
|
# Mirror of /etc/nginx/sites-enabled/digibyte-x (synced 2026-09-09).
|
|
#
|
|
# Deploy: render the one secret, then install + reload:
|
|
# . /etc/dgb-core/nownodes.env # NOWNODES_API_KEY=…
|
|
# sed "s|@NOWNODES_API_KEY@|$NOWNODES_API_KEY|" ops/nginx-digibyte-x.conf # > /etc/nginx/sites-enabled/digibyte-x && nginx -t && systemctl reload nginx
|
|
#
|
|
# The Content-Security-Policy hash below pins the inline <base>-fix
|
|
# script in the built index.html. Recompute after any change to
|
|
# packages/web-ui/index.html: node packages/web-ui/scripts/csp-hash.mjs
|
|
#
|
|
# nginx vhost for digibyte.x on coinspectrum.
|
|
#
|
|
# Trust model: SELF-SIGNED cert (10y), pinned on-chain via the digibyte.x
|
|
# BNS `tls` record. Wallet clients resolve digibyte.x → {ip, tls}, dial
|
|
# the IP on :443 with SNI=digibyte.x, and verify by comparing the observed
|
|
# cert's DER SHA-256 to the on-chain pin. No CA, no ACME.
|
|
#
|
|
# nginx selects this cert only for SNI=digibyte.x — every other vhost on
|
|
# this box (navigate.st, silentmode.st, mail.silentmode.st, etc.) keeps
|
|
# its Let's Encrypt cert untouched.
|
|
#
|
|
# Layout:
|
|
# / → 302 to /web/ (wallet is the only thing here)
|
|
# /web/* → static bundle from /var/www/dgb-wallet/web/
|
|
# /p2p/* → reverse-proxy to dgb-daemon on 127.0.0.1:5174
|
|
# (strip /p2p prefix so daemon sees /health, /balance, etc.)
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
listen [::]:443 ssl http2;
|
|
|
|
server_name digibyte.x;
|
|
|
|
# Self-signed cert. Fingerprint is pinned on-chain — rotating the cert
|
|
# is a single BNS UPD, no client update.
|
|
ssl_certificate /etc/ssl/dgb-wallet/server.crt;
|
|
ssl_certificate_key /etc/ssl/dgb-wallet/server.key;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_prefer_server_ciphers on;
|
|
|
|
# No HSTS: HSTS presupposes CA-issued trust. We use fingerprint pinning
|
|
# instead — the wallet client rejects any cert whose DER SHA-256 does
|
|
# not match the on-chain record, regardless of browser trust store.
|
|
|
|
# Belt-and-suspenders: keep this vhost strictly for digibyte.x. If
|
|
# SNI doesn't match the server_name above, nginx falls back to the
|
|
# default_server (coinspectrum), which has its own LE cert — so a
|
|
# wrong-SNI connection never sees our self-signed cert anyway.
|
|
|
|
# /web/* — static wallet bundle. Vite emits absolute URLs like
|
|
# /web/assets/index-<hash>.js so we keep the /web/ prefix in the
|
|
# filesystem path (root, not alias).
|
|
location /web/ {
|
|
root /var/www/dgb-wallet;
|
|
try_files $uri $uri/ /web/index.html;
|
|
|
|
# index.html must never be cached — new deploys change the asset
|
|
# filename hashes, and the entry HTML has to fetch the new ones.
|
|
add_header Cache-Control "no-store" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header Referrer-Policy "no-referrer" always;
|
|
# Strict CSP (docs/SECURITY-ROADMAP.md item 1). The sha256 is the
|
|
# inline <base>-fix script in the built index.html — recompute
|
|
# whenever packages/web-ui/index.html changes.
|
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'sha256-MYCXjyaeuO4n6g9JktOO1yXmG9rBTuGJ5Wk4GojDwig=' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https://api.coingecko.com; img-src 'self' data:; font-src 'self'; base-uri 'self'; frame-ancestors 'none'; form-action 'none'; object-src 'none'" always;
|
|
}
|
|
|
|
# Content-hashed assets: cache forever.
|
|
location /web/assets/ {
|
|
root /var/www/dgb-wallet;
|
|
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
}
|
|
|
|
# /p2p/* → daemon. The daemon registered /health, /balance, /broadcast
|
|
# at its own roots, so strip the /p2p prefix before proxying.
|
|
location /p2p/ {
|
|
proxy_pass http://127.0.0.1:5174/;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_read_timeout 120s; # BIP158 scans can take a while
|
|
|
|
# The daemon already emits CORS headers for '*' — no need to add
|
|
# them here. Leave request bodies (POST /broadcast, /balance) unbuffered.
|
|
proxy_request_buffering off;
|
|
}
|
|
# /blockbook/* → same-origin reverse-proxy to a public Blockbook.
|
|
# Two reasons this exists:
|
|
# 1. Atomic's Blockbook sends no CORS headers, so a direct
|
|
# browser fetch from the wallet origin is silently blocked
|
|
# and the wallet reads "zero balance" for every xpub.
|
|
# 2. Requests go out under this VPS's IP, not the individual
|
|
# user's — the upstream can't correlate address queries to
|
|
# individual browsers. (This VPS operator still can — the
|
|
# wallet's Blockbook backend toggle carries an inline note.)
|
|
location /blockbook/ {
|
|
# Upstream: Atomic Wallet's public DGB Blockbook. The proxy hides
|
|
# individual user IPs from the upstream operator. Every public DGB
|
|
# Blockbook rejects raw bech32 address queries; `/xpub` works
|
|
# (server-side derivation covers all script kinds).
|
|
#
|
|
# NOWNodes (dgbbook.nownodes.io, api-key injected here from
|
|
# /etc/dgb-core/nownodes.env) was the upstream 2026-09-02..10-11
|
|
# until the key stopped being accepted ("Unknown API_key" on every
|
|
# endpoint). To switch back with a fresh key:
|
|
# proxy_pass https://dgbbook.nownodes.io/api/v2/;
|
|
# proxy_ssl_name dgbbook.nownodes.io;
|
|
# proxy_set_header Host dgbbook.nownodes.io;
|
|
# proxy_set_header api-key "@NOWNODES_API_KEY@";
|
|
proxy_pass https://digibyte.atomicwallet.io/api/v2/;
|
|
proxy_ssl_server_name on;
|
|
proxy_ssl_name digibyte.atomicwallet.io;
|
|
proxy_set_header Host digibyte.atomicwallet.io;
|
|
proxy_set_header X-Forwarded-For "";
|
|
proxy_set_header X-Real-IP "";
|
|
proxy_http_version 1.1;
|
|
proxy_read_timeout 60s;
|
|
proxy_hide_header Access-Control-Allow-Origin;
|
|
proxy_hide_header Access-Control-Allow-Methods;
|
|
proxy_hide_header Access-Control-Allow-Headers;
|
|
add_header Access-Control-Allow-Origin "*" always;
|
|
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
|
|
add_header Access-Control-Allow-Headers "Content-Type" always;
|
|
if ($request_method = OPTIONS) { return 204; }
|
|
}
|
|
|
|
|
|
# Bare / — redirect to /web/. This vhost exists ONLY to host the wallet;
|
|
# no reason to leave a hint here for hostile traffic.
|
|
# /web (no trailing slash) — redirect so bare /web works. Sits above /web/ (longest-prefix) so it does not shadow the static block.
|
|
location = /web {
|
|
# BNS gateway strips the Location header, so a plain 302 goes
|
|
# nowhere for gateway-loaded users. Return HTML that redirects
|
|
# via meta-refresh + JS with a RELATIVE href — resolves against
|
|
# the current URL, works under both digibyte.x and navigate.st.
|
|
default_type text/html;
|
|
return 200 "<!doctype html><html><head><meta http-equiv=\"refresh\" content=\"0;url=web/\"><script>location.replace(\"web/\")</script></head><body>Redirecting to <a href=\"web/\">web/</a></body></html>";
|
|
}
|
|
|
|
# Marketing landing page — served at the apex. Wallet is at /web/.
|
|
location = / {
|
|
root /var/www/dgb-wallet;
|
|
try_files /index.html =404;
|
|
add_header Cache-Control "no-store" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
}
|
|
|
|
# Root-level static files (favicon.svg, robots.txt, …). Anything
|
|
# not on disk still 404s, and the wallet routes above (which are
|
|
# longer-prefix) still win. Kept explicit-only to avoid accidental
|
|
# exposure of /var/www/dgb-wallet subdirs.
|
|
location = /favicon.svg { root /var/www/dgb-wallet; }
|
|
location = /robots.txt { root /var/www/dgb-wallet; }
|
|
|
|
# Everything else on this vhost — 404. No indexer/status page.
|
|
location / {
|
|
return 404;
|
|
}
|
|
}
|