// Package poke is the agent-plane immediate-sync SENDER (Direction-2a, // SPIKE-immediate-sync-transport-2026-07-16). It is the third structural sibling of // internal/wgsync and internal/tenantsync: the hub holds a forced-command SSH credential to the // offsite endpoint (ep0) and drives it; the endpoint stays a dumb, runbook-provisioned box. // // A poke is a CONTENTLESS "sync now" nudge: the hub SSHes to ep0's `felhom-poke` forced command // with the target box's WireGuard /32 as the command string (→ SSH_ORIGINAL_COMMAND); the forced // command validates it and sends ONE empty UDP datagram from wg0 to that /32:51822. The agent's // poke listener (felhom-agent internal/poke) fires an immediate desired-state cycle. Measured // ~0.42 s per fresh SSH session in the spike (target ≤2–3 s). // // It carries NO payload semantics, NO auth handshake, NO retry: a lost or forged poke costs at // most one extra debounced tick, and the 15-min report cycle remains the guarantee. The transport // is SSH with a PINNED host key (the wgsync posture — exact match or refuse, no insecure // fallback); credential theft bounds the attacker to "make registered boxes tick", and WireGuard // itself refuses to encrypt to any /32 no registered peer owns (spike P1 EKEYREJECTED). package poke import ( "bytes" "context" "fmt" "log" "net" "net/netip" "strings" "time" "golang.org/x/crypto/ssh" ) // wgSubnet confines every poke target to the WG /24 (defence in depth — the ep0 forced command // validates independently, and the kernel refuses non-peer /32s). var wgSubnet = netip.MustParsePrefix("10.77.0.0/24") // Config configures the SSH poke client. All values come from the deployment env / mounted Secret // (operator infra — never a customer record). Addr + HostKeyLine mirror wgsync/tenantsync; the // private key is the poke key's OWN (the ep0 authorized_keys line selects the forced command). type Config struct { Addr string // "host:22" User string // "felhom-peersync" (same user; the key selects the forced command) PrivateKey []byte // PEM private key (from the mounted Secret file) HostKeyLine string // endpoint host pubkey, authorized_keys format (the wgsync pin) Timeout time.Duration // default 10s (a poke must be quick or abandoned) } // Client is a pinned-host-key SSH poke sender. Construct with New (parses keys up front). type Client struct { addr string user string signer ssh.Signer hostKey ssh.PublicKey timeout time.Duration logger *log.Logger } // New builds a Client, failing early on an unparsable key or host-key line. func New(cfg Config, logger *log.Logger) (*Client, error) { if cfg.Addr == "" || cfg.User == "" { return nil, fmt.Errorf("poke: Addr and User are required") } signer, err := ssh.ParsePrivateKey(cfg.PrivateKey) if err != nil { return nil, fmt.Errorf("poke: parse private key: %w", err) } hostKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(cfg.HostKeyLine)) if err != nil { return nil, fmt.Errorf("poke: parse host key line: %w", err) } timeout := cfg.Timeout if timeout == 0 { timeout = 10 * time.Second } if logger == nil { logger = log.Default() } return &Client{addr: cfg.Addr, user: cfg.User, signer: signer, hostKey: hostKey, timeout: timeout, logger: logger}, nil } // Poke sends one contentless UDP nudge to boxWGIP via the ep0 forced command. boxWGIP is validated // to be inside the WG /24 before any connection (the ep0 script validates again). The box IP is // the SSH command string (the forced command reads $SSH_ORIGINAL_COMMAND). Returns an error on any // failure — but the CALLER treats this fire-and-forget (log, never fail the operator save). func (c *Client) Poke(ctx context.Context, boxWGIP string) error { addr, err := netip.ParseAddr(boxWGIP) if err != nil || !wgSubnet.Contains(addr) { return fmt.Errorf("poke: refusing non-WG target %q", boxWGIP) } sshCfg := &ssh.ClientConfig{ User: c.user, Auth: []ssh.AuthMethod{ssh.PublicKeys(c.signer)}, HostKeyCallback: ssh.FixedHostKey(c.hostKey), HostKeyAlgorithms: []string{c.hostKey.Type()}, // pin the algorithm (wgsync S1 finding) Timeout: c.timeout, } dialer := net.Dialer{Timeout: c.timeout} conn, err := dialer.DialContext(ctx, "tcp", c.addr) if err != nil { return fmt.Errorf("poke: dial %s: %w", c.addr, err) } if dl, ok := ctx.Deadline(); ok { conn.SetDeadline(dl) } else { conn.SetDeadline(time.Now().Add(c.timeout)) } sconn, chans, reqs, err := ssh.NewClientConn(conn, c.addr, sshCfg) if err != nil { conn.Close() return fmt.Errorf("poke: ssh handshake %s: %w", c.addr, err) } client := ssh.NewClient(sconn, chans, reqs) defer client.Close() conn.SetDeadline(time.Time{}) if dl, ok := ctx.Deadline(); ok { conn.SetDeadline(dl) } session, err := client.NewSession() if err != nil { return fmt.Errorf("poke: session: %w", err) } defer session.Close() var stdout, stderr bytes.Buffer session.Stdout = &stdout session.Stderr = &stderr // The forced command IGNORES this string for execution but reads it as $SSH_ORIGINAL_COMMAND — // that is how the target box IP crosses to ep0. The command carries only the IP, nothing else. if err := session.Run(boxWGIP); err != nil { return fmt.Errorf("poke: remote felhom-poke failed: %w (stderr: %s)", err, strings.TrimSpace(stderr.String())) } c.logger.Printf("[INFO] poke: sync-poke delivered to %s via %s", boxWGIP, c.addr) return nil }