Skip to content

Commit b7e2fec

Browse files
author
S. K. Rotwang MMMMMMMMM
committed
fix: read v2 payment-response header in self-bootstrap; tunnel unit hardened (system unit, retry forever)
1 parent 5e8fb07 commit b7e2fec

2 files changed

Lines changed: 33 additions & 27 deletions

File tree

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,54 @@
1-
# Reverse SSH tunnel: fin1 -> fin4, exposing fin1's loopback-bound
2-
# services on fin4's loopback. The Seneschal data-api on fin4 depends
3-
# on these three forwards:
1+
# Persistent reverse tunnel fin1 → fin4 exposing local chain RPCs to the
2+
# data-api host: monerod (18081), zebra (8232) and NFPT (3555) appear on
3+
# fin4's 127.0.0.1. The data-api, its pollers and the ops-monitor probes
4+
# all depend on these forwards.
45
#
5-
# fin4 127.0.0.1:18081 -> fin1 monerod RPC
6-
# fin4 127.0.0.1:8232 -> fin1 zebra (Zcash) RPC
7-
# fin4 127.0.0.1:3555 -> fin1 NFPT scanner API
6+
# Installed on fin1 as /etc/systemd/system/fin1-seneschal-tunnel.service:
7+
# sudo systemctl daemon-reload
8+
# sudo systemctl enable --now fin1-seneschal-tunnel.service
89
#
9-
# INSTALL (on fin1, which we cannot reach from the dev box — fin1 only
10-
# connects outward to fin4):
10+
# Replaces the per-user unit (~/.config/systemd/user/seneschal-chain-tunnel
11+
# .service, retired 2026-06-12): with Linger=no a user unit only runs while
12+
# rotwang has a session, so an unattended reboot left the tunnel down.
13+
# A system unit starts at boot unconditionally.
1114
#
12-
# 1. Copy this file to /etc/systemd/system/seneschal-tunnel.service
13-
# 2. Confirm root@fin1 already has a key authorised on fin4 (it does —
14-
# the tunnel runs today as root with an ED25519 key).
15-
# 3. If the current hand-started tunnel is running, kill it first so
16-
# the listen ports are free (`pgrep -af "ssh.*18081"` on fin1).
17-
# 4. systemctl daemon-reload && systemctl enable --now seneschal-tunnel
15+
# Runs as rotwang with the dedicated tunnel key — restricted on fin4 with
16+
# `restrict,port-forwarding,command="echo seneschal-tunnel-only"`, so a
17+
# leaked key can only open this same tunnel: no shell, no exec.
1818
#
19-
# Why plain ssh + systemd Restart instead of autossh: systemd already
20-
# does supervised restart with backoff, and ServerAliveInterval makes
21-
# ssh itself exit when the link goes quiet — autossh would only add a
22-
# second supervisor doing the same job.
23-
#
24-
# ExitOnForwardFailure=yes makes ssh fail fast if fin4 still has a
25-
# stale listener on any port (e.g. an old tunnel session that hasn't
26-
# timed out) instead of starting half-connected; systemd then retries
27-
# until the stale socket clears.
19+
# ExitOnForwardFailure + Restart=always: a half-up tunnel (binds taken /
20+
# connection dropped) dies and retries rather than lingering. fin4's sshd
21+
# runs ClientAliveInterval 30 / CountMax 3 (sshd_config.d/99-clientalive
22+
# .conf) so a dead client's stale listeners are reaped within ~90s —
23+
# without it a SIGKILL'd client wedges the rebind forever.
24+
# StartLimitIntervalSec=0: never stop retrying — after a long fin4 outage
25+
# the tunnel must come back on its own.
2826

2927
[Unit]
3028
Description=Seneschal reverse tunnel to fin4 (monerod/zebra/NFPT forwards)
3129
After=network-online.target
3230
Wants=network-online.target
31+
StartLimitIntervalSec=0
3332

3433
[Service]
3534
Type=exec
35+
User=rotwang
3636
ExecStart=/usr/bin/ssh -NT \
37+
-i /home/rotwang/.ssh/seneschal_tunnel_ed25519 \
38+
-o IdentitiesOnly=yes \
39+
-o BatchMode=yes \
3740
-o ExitOnForwardFailure=yes \
3841
-o ServerAliveInterval=30 \
3942
-o ServerAliveCountMax=3 \
4043
-o ConnectTimeout=15 \
41-
-o BatchMode=yes \
4244
-o StrictHostKeyChecking=accept-new \
45+
-o StreamLocalBindUnlink=yes \
4346
-R 127.0.0.1:18081:127.0.0.1:18081 \
4447
-R 127.0.0.1:8232:127.0.0.1:8232 \
4548
-R 127.0.0.1:3555:127.0.0.1:3555 \
46-
root@65.108.233.209
49+
root@fin4
4750
Restart=always
48-
RestartSec=10
51+
RestartSec=15
4952

5053
[Install]
5154
WantedBy=multi-user.target

scripts/x402-self-bootstrap.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,10 @@ async function settle(fetchWithPayment, target) {
197197
const startedMs = Date.now();
198198
const res = await fetchWithPayment(url, requestInit(target));
199199
const elapsedMs = Date.now() - startedMs;
200-
const settlement = decodeSettlement(res.headers.get('x-payment-response'));
200+
// x402 v2 middleware names the header `payment-response`; older stacks
201+
// used `x-payment-response`. Check both so settle reporting (success +
202+
// tx hash) survives upgrades on either side.
203+
const settlement = decodeSettlement(res.headers.get('payment-response') ?? res.headers.get('x-payment-response'));
201204
let bodyPreview = '';
202205
let resBody = null;
203206
if (res.ok) {

0 commit comments

Comments
 (0)