agents agents/C-infrastructure.md
Owns: no repository files. Cloud accounts, and PARKVPS/ for local VM work. Must not touch: application code in any lane.

Lane C — stand up the cloud

Owns: no repository files. Cloud accounts, and PARKVPS/ for local VM work. Must not touch: application code in any lane.

## ❌ NEEDS TRAVIS IN THE ROOM Every task here spends money or creates an account. An agent must not provision paid infrastructure, enter payment details, or accept terms. Prepare the commands, explain what each will cost, and let Travis run the ones that spend. Standing constraint: this stack is built on a zero-budget assumption. Do not casually add a line item.

Read first

STACK_HUB.md §0.7 and §0.9–0.11 — the provider rulings and what each costs.

What is ruled

PieceWhereWhy that vendor
KeycloakFly `iad`Workers are V8 isolates and cannot run Java
Keycloak's databasePlanetScale PS-5 HA, $15/moRationale replaced 2026-08-21 (R9) — see §3. Failure isolation, not price
Control-plane API, portal BFF, credential mintingCloudflare WorkersR7 — §0.6's ruling holds. No server-hosted BEAM in Cloud v1
Worker → Pigsty transportHyperdrive → Workers VPC → Cloudflare TunnelR7 — Pigsty stays private on Hetzner with no Postgres port facing the internet
Pigsty Postgres — studbook index and the Cloud control planeHetzner VMPigsty needs systemd/sudo/sshd; Fly gives Firecracker containers
Bulk objectsCloudflare R2zero egress at any volume

2026-08-21 — this table gained a row and lost an assumption. A proposed Cloud brief put a Phoenix/Elixir API at the centre of the architecture, which silently reversed STACK_HUB.md §0.6 ("Workers cannot run Elixir, Phoenix, or the BEAM … that decision has to be revisited rather than patched"). R7 declines to reopen it. The control plane is Workers; Pigsty is reached over Hyperdrive rather than by moving the application to the database. Super stays BEAM/OTP on the user's machine — Cloud does not mirror Super's implementation just because Super is Elixir.

The four jobs

1. The Hetzner VM

T&R is FreeBSD and Hetzner Cloud's API has no image upload. The documented path is to boot the rescue system and write the raw image straight to /dev/sda (qemu-img convert, or hcloud-upload-image which automates it). FreeBSD-on-hcloud is independently documented, including a 2026 Packer write-up.

Snapshot after the first install — that is the distribution path, and the workaround is not repeated per machine.

Budget a session, not an afternoon. It is documented, not supported. Nothing is blocked meanwhile: PARKVPS runs T&R locally today.

Sizing: do not trust any euro figure you find. Hetzner adjusted prices in June 2026 and CX32 is deprecated, so published comparisons are stale. Price it on Hetzner's own calculator.

CORRECTED 2026-08-21. This read "Pigsty wants 1C2G, Keycloak wants 2 GB, so ~4 vCPU / 8 GB is the plausible floor for the shared box" — which budgeted Keycloak's RAM onto Hetzner while the table two sections up ruled Keycloak onto Fly. The table is right and the sizing line was wrong. Keycloak is not on this box. What actually runs here under R7: Pigsty (Patroni, etcd, HAProxy, PgBouncer, pgBackRest, MinIO, Grafana, Nginx) and `cloudflared` for the Tunnel. No application server — R7 keeps the control plane on Workers, so nothing here serves HTTP to the public. Shape only, still to be priced first-party: Pigsty's own floor is 1C1G, 1C2G recommended, and the Tunnel daemon is small.

2. Pigsty

Ansible-driven, needs systemd, sudo and sshd, installs Patroni, etcd, HAProxy, PgBouncer, pgBackRest, MinIO, Grafana and Nginx. Minimum 1C1G, 1C2G recommended. This is why it goes on the Hetzner VM and not on Fly.

Serves studbook's index (lane B) — and not user bytes. Bytes in Postgres is 18.7× R2 and would lose ~$2,867/month on a single 10 TB customer.

3. Keycloak on Fly + PlanetScale

  • Deploy Keycloak to Fly `iad`. PlanetScale's pricing is AWS us-east-1, the same metro —

single-digit milliseconds. Put it in a European region and every token issuance crosses the Atlantic. The rule is co-locate, and co-location is about distance, not matching logos.

  • Do not point it at the Hetzner Postgres. It will look tidy. It puts the auth path — the one

thing that must be up for anyone to do anything — on the longest wire in the architecture, and re-couples exactly what the split decoupled.

R9, 2026-08-21 — this bullet is now the whole reason, and the recorded price argument is retired. The $15/mo was justified by beating Fly Managed Postgres at $38. That comparison was correct when nothing else in the stack ran Postgres. It is not the live alternative any more: Pigsty is Patroni plus streaming replicas, so the honest comparison became "$15/mo versus another database on a cluster you are already paying for", and on price alone PlanetScale loses that. The ruling does not change, because price was never the load-bearing reason — this bullet was. What $15/mo buys is stated plainly so it can be re-decided later on its merits: `` Pigsty trouble ≠ Keycloak database trouble `` Reconsider when product economics matter. Do not reconsider mid-implementation.

  • PS-5 is 1/16 vCPU and 512 MiB. "HA" means redundant, not fast. If auth feels slow the answer

is PS-10 HA at $30, still under Fly's $38.

  • PlanetScale Postgres is real Postgres, not Vitess — the well-known no-foreign-keys caveat is

about their MySQL product and does not apply. Keycloak's JPA schema is fine.

  • Connect directly, not through their optional PgBouncer. Keycloak brings Agroal;

transaction-mode pooling against JDBC prepared statements is a known trouble source.

  • SQLite is not an option. Keycloak supports Postgres/MariaDB/MySQL/MSSQL/Oracle/Aurora/Azure

SQL. H2 (dev-file) is explicitly "not suitable for production use-cases."

4. R2

A bucket for published track bundles. Class A ops are $4.50/M — the discipline is content-addressed chunks, never re-PUT an unchanged chunk, ≥8 MB minimum, incremental by default. Measured: naïve weekly fulls on 1 TB are $30.67/yr against $4.30/yr incremental.

Public tracks need no credential — class A data, world-readable, no Worker in front of them. That is why this rung ships before lane D.

Done means

  • trvs doctor printing status ready on a Hetzner instance, and trvs id deriving the same

sem- identity as the workstation. A different id is the more interesting result and should be reported loudly, not debugged into agreement.

  • A Keycloak realm issuing a JWT, fetched from Fly iad, verifiable against its JWKS.

  • A track bundle PUT to R2 and GET back on a different machine, verified by re-derived hash.

  • Every figure recorded with the date it was measured. This tree has quoted stale storage prices

three times; the rule now is quote first-party at the point of use, never from prose.

Do not tear anything down yet

Ten Fly apps are live_deployed and are being wound down — but nothing is torn down before its OS-side replacement is `live_local`. Deleting a working deployment to honour a plan is how a portfolio loses a capability it had. box-and-box-mcp is first in the queue and even that can wait.

Open in the interactive atlas