The isolated Signet explorer used for functional acceptance on the workspace host. It runs the real backend, the real gateway and the real frontend build against a Universe-owned Signet node and Esplora index. Nothing here is a production deployment.
Runtime state lives outside the Git checkout, in the project runtime directory required by the workspace layout:
D:\universe\mempool\.runtime\signet\
mempool-config.signet.json backend configuration (Signet, Esplora, database mempool_signet)
rpc.cookie current Core RPC cookie, kept fresh by rpc-cookie-sync
cache\ backend disk cache
logs\backend-signet.log rotating, 50 MB x 5
logs\cookie-sync.log rotating
logs\gateway.log rotating
pids.json wrapper process ids and start time
| Service | Where | Local endpoint |
|---|---|---|
| Bitcoin Core (Signet) | universe-indexers-2, universe-bitcoin-signet |
ssh tunnel 127.0.0.1:38335 to 127.0.0.1:38332 |
| Esplora (electrs) | universe-indexers-2, universe-explorer-electrs-signet |
ssh tunnel 127.0.0.1:3022 |
MariaDB mempool_signet |
universe-indexers-2 | ssh tunnel 127.0.0.1:33307 |
The tunnels use the deploy key over the VPN or the host address; the backend only ever talks to loopback.
The address readiness probe selects a checksum-valid address for the configured
network. Mainnet retains the historical funded probe; Signet/testnet use a
test-network witness address. An empty result for this readiness-only probe is
valid when every response has the required shape and the index tip matches the
owned node within the configured lag. It does not prove funded-address history
or a complete wallet journey. The release history gate separately requires an
actual funded address; set UNIVERSE_RELEASE_ADDRESS_PROBE for non-mainnet runs.
Primary and fallback Esplora endpoints must be operated loopback HTTP(S) origins
or absolute Unix socket paths. Both preflight and the data router enforce this.
The sample Signet configuration has no public fallback. Esplora data, health and
address-readiness clients do not follow redirects or use ambient HTTP proxies.
Build metadata retains the configured origin/port or Unix socket; it never guesses
a public hostname. A missing metadata endpoint leaves that version unknown.
The gateway converts API 301/302/303/307/308 responses into
502 {"error":"upstream-redirect-refused"} so redirects cannot move a client or
POST body onto an unowned data source. Configure the final local endpoint directly.
Regression checks are in backend/src/__tests__/address-index-network.test.ts,
backend/src/api/bitcoin/esplora-transport.test.ts and the gateway tests. These
use actual loopback listeners to exercise malformed responses, redirects and
proxy bypass, separate from owned Signet runtime evidence.
Bitcoin Core rewrites .cookie on every restart. A copy taken once stops
working at the next restart and the backend then answers 503 on fees and
falls behind the tip while logging 401 on every loop. The JSON-RPC client
re-reads its cookie file after a 401, so the only requirement is a copy
that follows the node:
node scripts/universe/rpc-cookie-sync.mjs \
--ssh root@production-backend.netbird.cloud \
--identity ~/.ssh/universe-deploy-20260831-ed25519 \
--remote /var/lib/bitcoind-signet/signet/.cookie \
--local D:/universe/mempool/.runtime/signet/rpc.cookie \
--interval 30The script writes the cookie bytes exactly as Core does (no trailing
newline, the client sends the file content as the auth string), only when
the value changed, and never prints it. On Git Bash set
MSYS_NO_PATHCONV=1 so the remote path is not rewritten.
Every long-running process is started through run-logged.mjs, which
rotates the combined output at a byte ceiling and keeps a fixed number of
older files:
node scripts/universe/run-logged.mjs --log D:/universe/mempool/.runtime/signet/logs/backend-signet.log \
--max-bytes 52428800 --keep 5 -- node --max-old-space-size=2048 dist/index.jsThe previous runtime wrote to one redirected file that reached 16 GB in five
days, most of it repeated stall lines from leaked watchdog timers. That leak
is fixed ($updateBlocks and $updateMempool release their timer and disk
cache lock in a finally), and the wrapper caps what any future fault can
write.
$rt = "D:\universe\mempool\.runtime\signet"; $wt = "D:\universe\mempool\mempool"
$env:MEMPOOL_CONFIG_FILE = "$rt\mempool-config.signet.json"
Start-Process node -WorkingDirectory "$wt\backend" -ArgumentList "$wt\scripts\universe\run-logged.mjs --log $rt\logs\backend-signet.log -- node --max-old-space-size=2048 dist/index.js"
$env:UNIVERSE_GATEWAY_BACKEND_SIGNET = "http://127.0.0.1:8997"
$env:UNIVERSE_GATEWAY_ESPLORA_SIGNET = "http://127.0.0.1:3022"
$env:UNIVERSE_GATEWAY_PORT = "8099"
$env:UNIVERSE_GATEWAY_ROOT = "$wt\frontend\dist\mempool\browser"
Start-Process node -WorkingDirectory $wt -ArgumentList "$wt\scripts\universe\run-logged.mjs --log $rt\logs\gateway.log -- node scripts/universe/gateway.mjs"The app is then at http://127.0.0.1:8099/signet.
node scripts/universe/acceptance-preflight.mjs --origin http://127.0.0.1:8099 --network signet --out audits/preflight-signet.json
curl -s http://127.0.0.1:8099/signet/api/v1/backend-info # gitCommit, chainSync
curl -s http://127.0.0.1:8099/signet/api/blocks/tip/height # must equal the Esplora tip
curl -s http://127.0.0.1:3022/blocks/tip/height
curl -s http://127.0.0.1:8099/signet/api/v1/fees/recommended # 200 once the mempool is in syncPreflight requires an explicit literal loopback HTTP origin and network. The output file must be new; existing evidence is never overwritten. It checks API genesis, block identity and a stable tip, and retains the eleven historical transport probes without interpreting their HTTP responses as operation passes. It does not evaluate prior prerequisite claims or establish full GO. Source hashes identify the local tooling, not the process serving the URL.
backend-info.gitCommit is the served revision, chainSync is the node the
backend reads, and the database namespace is DATABASE.DATABASE in the
config file. Record all three with every piece of evidence.
A descriptor wallet mempool-audit-20260915-0436 exists on the Signet node
(load_on_startup=false), first address
tb1qy0swjglf4nw5cac2kh3nh7y09dkevedl7cfuta. Public Signet faucets sit
behind CAPTCHAs that the automation must not bypass; funding remains a
human step until an owned faucet exists.