Skip to content

Federation

Federation lets two or more gatewayd instances act as a coordinated group. Each peer keeps its own SQLite (no shared database), but selected configuration kinds — policies, devices, privacy classes, DNS rules, presets — can be pushed from one peer to another or pulled by a peer from a designated primary.

This is how you run “the home gateway” and “the VPS bridge” as a single logical thing without operating two unrelated control planes.

Federation isn’t a “node is primary” / “node is secondary” model. It’s per peer, per direction. Each peer relationship has three knobs:

Field Values Meaning
sync_policy none / push / pull / bidirectional The direction this peer relationship operates in. none disables sync between this pair.
sync_resources CSV of kind keywords The outgoing allowlist — which kinds this node may push to (or share with) the peer.
allowed_receive_resources CSV of kind keywords The incoming allowlist — which kinds we accept from this peer. Empty = accept all (legacy).

Kind catalog (GET /api/v1/federation/resources): bridges, routing, dns, devices, profiles, tunnels, segments, settings, nodes.

So “node X is the primary for routing across the cluster” is shorthand for: every peer relationship has sync_policy: push with sync_resources: routing from X’s side, and allowed_receive_resources: routing from the other side.

Three-node example: the office Pi pushes routing and dns to two VPS peers; the VPS peers don’t push back. Each VPS still owns its own tunnels config locally — because tunnels isn’t in the office Pi’s sync_resources, it never gets overwritten.

The federation_roles table is a separate labelling taxonomy for an operator’s own bookkeeping (role_type{standalone, hub, spoke, relay, observer}, trust_level, sync_policy). It documents intent for the dashboard; the actual sync gating comes from the peer-relationship fields above.

Federation does not stand up its own dataplane tunnel. It piggybacks on whatever connectivity is already there — typically the mesh WireGuard tunnel between the two nodes, or a TLS-fronted REST connection. Peer setup:

  1. On the primary, register the peer: POST /api/v1/nodes with the peer’s mesh address and an API key.
  2. The two sides exchange signing keys via POST /api/v1/nodes/exchange — that’s what authenticates federation messages.
  3. Operator-side approval: POST /api/v1/nodes/{id}/approve.
  4. From that point, federation requests carry signed manifests over the existing channel; no separate peer tunnel is provisioned.

If the underlying mesh tunnel breaks, federation pauses; it doesn’t try to repair connectivity itself.

Sync happens through two paths:

  • Operator-triggered push / pullPOST /api/v1/federation/sync (or the UI’s Sync now button) walks the peer list and pushes / pulls the configured object kinds. This is what operators use after a config change.
  • Auto-sync prober — each peer can have a sync_interval_seconds. When set, a background prober runs against that peer on the cadence. Useful for keeping a standby continuously fresh.

Pushed and pulled changes are applied atomically at the storage layer: a partial sync never leaves the secondary in an inconsistent state.

Conflicts (e.g. a local edit on the receiving peer that collides with an incoming push) are resolved by last-writer-wins, with the overwritten row logged to the audit trail so you can see what was replaced.

  • Home + remote VPS — the home Pi pushes device + routing + dns; the VPS pulls. Same rules apply at home or on the road; the VPS keeps its own tunnels local.
  • Warm standby — two boxes with bidirectional routing sync; the standby’s sync_resources excludes anything the standby is supposed to own (its own nodes row, its own logs). If the active fails, point your LAN’s gateway IP at the standby and policies are already in place. (The VIP / failover mechanism itself is outside The Gateway’s scope — keepalived or a similar tool handles that part.)
  • Multi-site — three offices, each with its own gateway. A central admin Pi (or a VPS) pushes routing and dns to each site; each site’s gateway never has tunnels in its allowed_receive_resources, so its local outbound config stays site-specific.

Federation has two endpoint families: status/control under /federation/, and the underlying peer/node lifecycle under /nodes/.

Method Path Purpose
GET /api/v1/federation/status Federation health snapshot
GET /api/v1/federation/peers List known federation peers (read-only)
GET /api/v1/federation/manifest Authoritative manifest the primary advertises
GET /api/v1/federation/inventory Per-peer inventory snapshot
GET /api/v1/federation/recent-events Recent federation event log
POST /api/v1/federation/sync Force a sync with one peer; body {peer_url: "..."}
GET/POST/PUT/DELETE /api/v1/federation-roles Per-kind role table (top-level, hyphenated)
GET/POST/DELETE /api/v1/nodes Peer node lifecycle (create / list / remove)
POST /api/v1/nodes/exchange Exchange signing keys with a new peer
POST /api/v1/nodes/{id}/approve Approve a pending peer
POST /api/v1/nodes/{id}/connect Initiate a connection attempt

After peers are exchanged and approved:

  1. Set per-peer allowlists deliberately. Each peer’s sync_resources (out) and allowed_receive_resources (in) decides which kinds flow. Empty receive-side = accept-all, which is rarely what you want — set them explicitly even if you have to repeat the same list.
  2. Audit recent federation events monthly. GET /api/v1/federation/recent-events plus /federation/overview surface drifting peers and conflict events (every overwrite from an incoming push is logged). Conflicts indicate two operators editing the same kind in different places — agree on which peer’s allowlist that kind belongs to.
  3. Keep backup separate. Federation is not a backup — if the primary applies a bad policy, secondaries cheerfully replicate it. Snapshot independently.
  4. Don’t expose secondaries to operators by mistake. A secondary that someone edits “just this once” turns into a primary-by-confusion. Lock UI access by role.
  • Mesh networking — federation typically runs over a mesh tunnel.
  • Backup — federation is not a backup; take snapshots independently.
  • Use case: hybrid home lab — Pi pushes routing/dns/devices to a VPS, VPS keeps its own tunnels.