# Bitcoin RPC Relay for External Wallets This note captures the pattern used to let an external wallet, such as Wasabi, use an Archipelago Bitcoin node for transaction relay without exposing the node's admin RPC credentials. ## Goal Expose a public HTTPS JSON-RPC endpoint that can broadcast transactions and read basic chain/mempool state, while preventing wallet and admin RPC access. The endpoint should be fronted by nginx or another TLS reverse proxy: ```text wallet client -> https:/// -> reverse proxy -> Archipelago node nginx -> bitcoind RPC ``` Do not expose Bitcoin RPC credentials with wallet/admin access to external users. ## Restricted RPC User Create a separate RPC user, currently named `txrelay`, with an `rpcauth` secret and a Bitcoin RPC whitelist. Allowed RPC methods: ```text sendrawtransaction testmempoolaccept getmempoolinfo getrawmempool getmempoolentry getnetworkinfo getblockchaininfo getblockcount getblockhash getblockheader getrawtransaction decoderawtransaction decodescript estimatesmartfee ``` Wallet/admin access is denied by setting `-rpcwhitelistdefault=0` and giving the `txrelay` user only the method whitelist above. Secrets live under: ```text /var/lib/archipelago/secrets/bitcoin-rpc-txrelay-password /var/lib/archipelago/secrets/bitcoin-rpc-txrelay-rpcauth /var/lib/archipelago/secrets/bitcoin-rpc-txrelay-client.env ``` Do not commit these files or paste them into docs. ## Archipelago UI/API Flow The productized flow is managed from the Bitcoin Core/Knots custom UI in the `Transaction Relay Sharing` panel. Implemented RPC methods: ```text bitcoin.relay-status bitcoin.relay-update-settings bitcoin.relay-request-peer bitcoin.relay-approve-request bitcoin.relay-reject-request bitcoin.relay-create-tor-service ``` When peer sharing is enabled, `bitcoin.relay-update-settings` automatically provisions the restricted `txrelay` password, `rpcauth`, and client env file if they do not already exist. If those files were just generated, restart Bitcoin Core/Knots so `bitcoind` reloads the `txrelay` `rpcauth` and whitelist flags. The UI shows: ```text HTTP / HTTPS / Tor relay endpoint settings local sync status restricted credential readiness, without printing the password trusted peer dropdown, disabled until the local node is synchronized incoming relay requests with approve/reject actions outbound relay requests and approval status ``` Approving an incoming peer request sends the selected endpoint plus restricted `txrelay` credentials through the existing encrypted peer-message path. On the requesting node, approved peer credentials are stored in a per-peer secret env file: ```text /var/lib/archipelago/secrets/bitcoin-relay-peer-.env ``` The UI returns the credential secret path and approved endpoint metadata, but it does not display the raw password. For dev review, the mock server exposes the Bitcoin UI at: ```text http://localhost:8102/app/bitcoin-ui/ ``` ## Bitcoin Startup Flags The Bitcoin Knots app should add the restricted user only when the secret exists: ```sh RPC_TXRELAY_AUTH="$(printenv BITCOIN_RPC_TXRELAY_RPCAUTH || true)" RPC_TXRELAY_FLAGS="-rpcwhitelistdefault=0" if [ -n "$RPC_TXRELAY_AUTH" ]; then RPC_TXRELAY_FLAGS="$RPC_TXRELAY_FLAGS -rpcauth=$RPC_TXRELAY_AUTH -rpcwhitelist=txrelay:sendrawtransaction,testmempoolaccept,getmempoolinfo,getrawmempool,getmempoolentry,getnetworkinfo,getblockchaininfo,getblockcount,getblockhash,getblockheader,getrawtransaction,decoderawtransaction,decodescript,estimatesmartfee" fi ``` Then include `$RPC_TXRELAY_FLAGS` in the `bitcoind` command. Keep the local `archipelago` RPC user unrestricted for internal services by using `-rpcwhitelistdefault=0` and only setting a whitelist for `txrelay`. The current implementation touches: ```text apps/bitcoin-knots/manifest.yml scripts/container-specs.sh ``` ## Node nginx The Archipelago node can expose a host-based nginx vhost that proxies to local Bitcoin RPC: ```nginx limit_req_zone $binary_remote_addr zone=bitcoin_rpc_ext:10m rate=5r/s; server { listen 80; server_name rpc.example.com; client_max_body_size 2m; location / { limit_req zone=bitcoin_rpc_ext burst=20 nodelay; limit_req_status 429; proxy_pass http://127.0.0.1:8332; 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_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 5s; proxy_send_timeout 120s; proxy_read_timeout 120s; proxy_buffering off; } } ``` If another public reverse proxy terminates TLS, point it at: ```text http://:80 ``` For the tested node the LAN upstream was: ```text http://192.168.1.116:80 ``` The public proxy should serve a valid TLS certificate for the chosen subdomain. ## DNS and Routing Use a subdomain that resolves to the public reverse proxy: ```text Type: A Host/Name: Value: ``` For example, if the desired hostname is `rpc.example.com`, the DNS host/name field is usually only `rpc`, not the full `rpc.example.com`. Entering the full hostname in some DNS panels can accidentally create: ```text rpc.example.com.example.com ``` The public proxy should forward: ```text TCP 443 -> TLS reverse proxy for the subdomain TCP 80 -> optional, needed for HTTP-01 certificate issuance or redirects ``` If the public proxy is separate from the Archipelago node, configure it with: ```text server_name: scheme: http upstream host: upstream port: 80 ``` ## Verification Check authoritative DNS: ```sh dig @ A +noall +answer +authority dig @1.1.1.1 +short A ``` Check TLS: ```sh openssl s_client -connect :443 -servername " ``` Check that transaction broadcast reaches Bitcoin RPC, without needing a real transaction: ```sh curl -sS --user "$BITCOIN_RPC_TXRELAY_USER:$BITCOIN_RPC_TXRELAY_PASSWORD" \ --data-binary '{"jsonrpc":"1.0","id":"badtx","method":"sendrawtransaction","params":["00"]}' \ "" ``` Expected result is a Bitcoin RPC validation error such as `TX decode failed`, which confirms the request reached `sendrawtransaction`. Check that wallet/admin RPC is blocked: ```sh curl -sS -o /tmp/txrelay-deny.json -w '%{http_code}\n' \ --user "$BITCOIN_RPC_TXRELAY_USER:$BITCOIN_RPC_TXRELAY_PASSWORD" \ --data-binary '{"jsonrpc":"1.0","id":"deny","method":"listwallets","params":[]}' \ "" ``` Expected result: ```text 403 ``` ## Tested Outcome The working endpoint used in this setup was: ```text https://shard.tx1138.com/ ``` It was verified with: ```text DNS resolves TLS certificate is valid txrelay credentials authenticate getblockchaininfo returns chain=main sendrawtransaction reaches Bitcoin RPC listwallets is blocked for txrelay ```