furtka-gateway/README.md
Robert Syrnicki 3a9d18fcd5 Scaffold gateway stack and single-tenant control plane
wg-easy + Traefik docker-compose stack (Phase 1) plus a stdlib-only
control-plane API for box registration, WireGuard peer provisioning via
wg-easy, and per-box route publish/unpublish backed by Traefik's file
provider (Phase 2, single-tenant mode). SQLite holds accounts/boxes/routes
so a later multi-tenant shared instance is the same schema with more rows,
not a reshape.

wg-easy's actual REST API was verified against its source rather than
assumed: it has no bearer-token auth (session-cookie login via
POST /api/auth/password) and no way to accept an externally-generated
public key (it always mints the keypair itself, private key included) —
both corrected from the original plan during implementation.
2026-08-24 11:50:25 +02:00

43 lines
2 KiB
Markdown

# furtka-gateway
WireGuard (wg-easy) + Traefik reverse-proxy that lets a Furtka box — typically
sitting behind NAT/CGNAT with no public IP — expose individual apps to the
internet under a real domain with automatic TLS.
A Furtka box becomes a WireGuard peer of this gateway. The gateway's Traefik
reverse-proxies public HTTPS traffic over that tunnel to whichever apps the
box owner has explicitly published. Only apps whose Furtka `manifest.json`
declares `internet.viable: true` can be published at all, and publishing is
always an explicit per-app opt-in on the box side — the gateway never exposes
anything on its own.
Supports two deployment modes from the same codebase:
- **`single`** — the common case: one person/operator runs this on their own
VPS with their own domain's A/AAAA records pointed at it, for their own
Furtka box(es).
- **`shared`** — one larger, multi-tenant instance (operated by the Furtka
project) for people who don't want to run their own. Same schema, same
code; `single` is just the one-account case.
See `docker-compose.yaml` and `control_plane/` for the moving parts. Status:
**Phase 1 (scaffold)** — wg-easy + Traefik + a stub control-plane exposing
only `/healthz`, enough to manually prove the wiring end-to-end before the
real control-plane (accounts/boxes/routes) lands.
## Local dev
```bash
cp .env.example .env
# edit .env: set GATEWAY_PUBLIC_HOST to a real hostname you control (or a
# LAN-reachable IP for a first smoke test), and a PASSWORD_HASH for wg-easy
# (see https://github.com/wg-easy/wg-easy for how to generate one).
docker compose up -d
curl http://127.0.0.1:8090/healthz
```
Traefik's dashboard/API and wg-easy's own UI are intentionally not published
on a host port — reach them via `docker compose exec` / port-forwarding
during development. Neither should ever be reachable from the internet in a
real deployment; only the control-plane (`8090`, bound to `127.0.0.1`) and
Traefik's `80`/`443` entrypoints are meant to be exposed.