trails/openspec/changes/relocate-brouter-to-dedicated-host/specs/brouter-integration/spec.md
Ullrich Schäfer 102a744e67
Add Hetzner vSwitch network for BRouter relocation
Adds an OpenSpec change scoping the relocation of BRouter from the
co-located flagship (cx23, 4 GB RAM, 40 GB SSD, Europe segments only) to
a dedicated Hetzner Robot host in the same datacenter, with private
connectivity over Hetzner vSwitch #80672 (VLAN 4000).

This first PR only lays the network prerequisite:

- Terraform: a Hetzner Cloud Network (10.0.0.0/16) with a cloud subnet
  (10.0.0.0/24) hosting the flagship at 10.0.0.2, and a vSwitch subnet
  (10.0.1.0/24) bridged to Robot VLAN 4000. The dedicated host's VLAN
  sub-interface (10.0.1.10 on enp4s0.4000) is configured out-of-band via
  netplan and is not Terraform-managed.
- lifecycle { ignore_changes = [user_data] } on the flagship server to
  prevent the Hetzner provider's post-1.45 user_data hash drift from
  triggering a spurious full-server replacement on unrelated applies.
- OpenSpec change with proposal, design, specs (delta for
  brouter-integration / infrastructure / observability /
  security-hardening), and tasks; Section 1 (pre-flight) is checked off
  with operator notes.

Verification: ping both directions across the vSwitch is 0% loss,
sub-ms latency; dedicated host's VLAN config persists across reboot
(verified ~60 s to restore private reachability).

Follow-up PRs will land the BRouter host compose project, Planner
shared-secret header, CD workflow retarget, observability wiring, and
the cutover.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 22:43:57 +02:00

2.5 KiB

MODIFIED Requirements

Requirement: BRouter API proxy

The Planner backend SHALL proxy all BRouter API calls. Clients SHALL NOT communicate with BRouter directly. The proxy SHALL attach a X-BRouter-Auth: <token> header to every upstream request using the BROUTER_AUTH_TOKEN environment variable.

Scenario: Proxied route request

  • WHEN the routing host client requests a route computation
  • THEN the Planner backend forwards the request to BRouter over the configured upstream URL with the X-BRouter-Auth header set, applies rate limiting, and returns the response

Scenario: Missing auth token at startup

  • WHEN the Planner starts in production without BROUTER_AUTH_TOKEN set
  • THEN the Planner logs a fatal error and refuses to start

Requirement: BRouter Docker deployment

BRouter SHALL run as a Docker container on a dedicated Hetzner host reached over a private Hetzner vSwitch, with planet-wide RD5 segments mounted as a volume. The BRouter container SHALL NOT be exposed on any public network interface.

Scenario: BRouter container starts

  • WHEN the docker compose up -d command runs in ~trails/brouter/ on the dedicated host
  • THEN the BRouter container is reachable only on the vSwitch-bound IP and can compute routes using the mounted planet-wide RD5 segments

Scenario: Public network isolation

  • WHEN a request is sent to the dedicated host's public IP on the BRouter port
  • THEN the request is refused at the host firewall or times out; BRouter does not respond

Scenario: JVM memory sizing

  • WHEN the BRouter container starts
  • THEN the JVM is launched with -Xmx8g (or equivalent) so that the heap does not exceed 8 GB on the 32 GB host

ADDED Requirements

Requirement: Shared-secret auth on BRouter

The BRouter deployment SHALL require a shared-secret header on every request. Requests without a valid X-BRouter-Auth header SHALL be rejected before reaching the BRouter process.

Scenario: Valid token

  • WHEN a request arrives at the BRouter host with the correct X-BRouter-Auth header
  • THEN the Caddy sidecar forwards the request to BRouter and returns its response

Scenario: Missing or wrong token

  • WHEN a request arrives without an X-BRouter-Auth header or with an incorrect value
  • THEN the Caddy sidecar responds with HTTP 403 and does not forward the request to BRouter

Scenario: Token not logged

  • WHEN Caddy emits an access log line for a BRouter request
  • THEN the X-BRouter-Auth header value is redacted or omitted from the log line