hub v0.59.0: Direction-2a agent-plane immediate-sync poke sender + ep0 felhom-poke surface

- internal/poke: pinned-host-key SSH poke sender (wgsync sibling) + fire-and-forget Notifier (PokeHost/PokeAllHosts). Poke refuses non-WG targets pre-dial; contentless via ep0 forced command to the box WG /32:51822.
- wiring: Server.SetPoke; applyPBSDR pokes the host after each descriptor gen-bump; handleSetArtifacts (MinAgent floor) pokes all hosts. main.go env POKE_SSH_KEY_FILE (reuses peersync endpoint/hostkey).
- scripts/felhom-poke.sh (non-root forced command) + offsite-endpoint.md §11; manifests/hub.yaml Secret/agent-poke + POKE_SSH_KEY_FILE (image tag bump follows the build).
This commit is contained in:
2026-07-16 22:48:15 +02:00
parent bdb65a80e8
commit eb227486d0
11 changed files with 567 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
#!/bin/sh
# felhom-poke v1.0.0 — the offsite endpoint's agent-plane immediate-sync surface (Direction-2a,
# SPIKE-immediate-sync-transport-2026-07-16). The hub's THIRD forced-command surface on ep0
# (alongside felhom-peersync and felhom-tenantsync).
#
# It sends ONE contentless UDP datagram from wg0 to a REGISTERED box's WireGuard /32 on the fixed
# poke port; the box's felhom-agent poke listener then runs an immediate desired-state cycle. This
# collapses a user-triggered agent-plane config change (a pbsdr descriptor, a MinAgent floor) from
# the 15-min report cycle to the spike-measured sub-second path.
#
# CONTRACT (do not weaken any of these):
# - CONTENTLESS: an EMPTY datagram — a poke means only "tick now". No payload, no secret, no
# version, no auth handshake. A forged or replayed poke costs at most one extra (debounced)
# tick on the box, and the report cycle remains the guarantee.
# - CONFINED: the target IP arrives as $SSH_ORIGINAL_COMMAND (the hub sets it as the SSH command;
# this forced command ignores it for execution and reads it only as data). It is validated to
# 10.77.0.0/24 here, AND the WireGuard kernel independently refuses to encrypt to any /32 no
# registered peer owns (EKEYREJECTED, spike P1) — so a poke can only ever reach a real box.
# - NON-ROOT: sending a datagram needs no privilege. Unlike peersync/tenantsync this surface has
# NO sudoers grant — the forced command runs as felhom-peersync directly.
# - ORIGINATION, NOT TRANSIT: the datagram leaves via wg0's own output path; it never touches the
# forward chain, so it needs no ip_forward and no nft rule (spike P1 §2).
#
# authorized_keys line on the box (PUBLIC key only; the private half lives in the hub k8s
# Secret/agent-poke, never here):
# restrict,command="/usr/local/bin/felhom-poke" ssh-ed25519 AAAA... hub-poke@felhom
set -eu
POKE_PORT=51822
ip="${SSH_ORIGINAL_COMMAND:-}"
# Coarse then strict: WG /24 prefix, then a valid dotted octet (0-255) in it. Anything else refused.
case "$ip" in
10.77.0.*) : ;;
*) echo "felhom-poke: refused non-WG target: '$ip'" >&2; exit 1 ;;
esac
echo "$ip" | grep -Eq '^10\.77\.0\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$' \
|| { echo "felhom-poke: malformed target: '$ip'" >&2; exit 1; }
python3 - "$ip" "$POKE_PORT" <<'PY'
import socket, sys
ip, port = sys.argv[1], int(sys.argv[2])
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.sendto(b"", (ip, port)) # contentless: an empty datagram = "tick now"
finally:
s.close()
PY
echo "poke-fired"