Files
theta-suite/docs/jump-host/mesh.md
T
wmantly 744c85f4bf feat(multi-site): wire no-inbound relay registration into the real bootstrap flow
sso-manager-node/jump-host already had the relay-automation mechanism
(noInbound/meshIp/publicHost -> theta-proxy route via proxy_client.js,
GET /api/mesh/self on jump-host) but nothing in the actual operator
bring-up flow could ever reach it -- setup.sh, bootstrap/site-join.js,
and setup.env.example had zero wiring for it.

Add bootstrap/site-relay-register.js: reads this spoke's own role from
/config/site.json, logs into the local jump-host as its bootstrap
admin to discover its mesh IP, and registers it with the master. Mesh
peering itself stays a manual step (mint/paste a join token, same
pattern as the site join key), so this runs on every setup.sh
invocation via CFG_SPOKE_NO_INBOUND/CFG_SPOKE_PUBLIC_HOST and is a
no-op ("not meshed yet") until an operator has actually meshed the two
jump-hosts.

Also updates MULTI_SITE_SPEC.md's status table/TODO and the published
mesh.md docs page, which still described this as "designed but not
automated" after the API-level work had already shipped.
2026-08-10 21:09:10 -04:00

3.0 KiB
Raw Blame History

layout, title
layout title
default Gateway Mesh

Gateway Mesh

Theta Gateway can mesh with other Theta Gateway instances over real site-to-site WireGuard tunnels — separate from its SSH jump host role, and separate from the roaming-client/exit-node WireGuard feature (individual peer configs for laptops/phones). This is gateway-to-gateway: two sites' networks reaching each other directly.

Why and when to use this

  • Direct site-to-site networking, not just SSH. Once two gateways are meshed, hosts behind each can reach each other over the tunnel using the mesh addressing scheme below — not limited to jumping through SSH.
  • No manual WireGuard config. Meshing is a join-token exchange; both sides come out with a live, working peer entry for each other automatically.
  • Works without a kernel WireGuard module. Prefers in-kernel WireGuard, falls back to the userspace wireguard-go implementation automatically — useful for older kernels, some container/cloud images, or hosts where the kernel module isn't available.

How it works

  1. On the gateway you want others to join, mint a join token: Mesh page → Mint a Join Token. It's single-use and expires in 15 minutes.
  2. On the new gateway, use Join a Remote Gateway's Mesh: paste the other gateway's URL and the token.
  3. Both sides now have a live WireGuard peer for each other. The Meshed Gateways table shows every peer, its assigned mesh subnet, and when it was last seen.

Each gateway is assigned a mesh index (an integer 1254) the first time it either mints a token or is registered by another gateway. That index determines its subnet: 172.24.<index>.0/24 for the mesh tunnel itself, plus 10.<index>.0.0/16 reserved for that site's own local network — 254 sites is the hard ceiling this addressing scheme supports.

Requirements

  • Both gateways need a reachable endpoint (host:port) for the WireGuard handshake — typically the same public host the SSH/web ports are already on, with UDP 51820 reachable.
  • NET_ADMIN capability (or equivalent) on the container/host running the gateway, to create the WireGuard interface.

Connected to directory sync

Theta Directory's multi-site join (catalog + LDAP replication between a master and its spokes) prefers this mesh once it's up: a spoke that's registered a mesh IP gets its live resync pushes routed over the tunnel instead of the open internet, falling back to its public endpoint if the mesh path fails. A spoke with no public IP at all can also register as no-inbound (CFG_SPOKE_NO_INBOUND in theta-suite's setup.env) so the master auto-creates a relay route through its own theta-proxy — the master terminates TLS for that spoke's hostname and relays over this mesh. The mesh peering itself (this page) stays a manual step on both sides; directory join and relay registration pick up from there. See the architecture spec for the full detail.