foundation/documentation/contracts/CONTRACT_003_container_network_dns.md
Andreas Niemann 188e30e23e docs(contracts): add CONTRACT_001-004 — T00
Interface contracts unblocking the parallel fan-out (T01-T07):
- 001 config schema (single stack, passphrase + VERSIONS + Pulumi config)
- 002 Vault path layout (foundation/<service>/<type>-credentials, camelCase)
- 003 container network/DNS/ports/volumes (foundation-net, named volumes)
- 004 backup artifact format + restore order (Vault->PG->RustFS->Forgejo)

ADR_F001 (layered platform) already satisfied by ADR-004.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 17:41:43 +02:00

4 KiB

Contract — CONTRACT_003 — Container Network, DNS, Ports & Volumes

Between: all bootstrap/components/* that create containers ↔ each other (service discovery) Status: Agreed (pending implementation validation) Realizes: PLAN-002 §0 (Layer-0 = containers), §3 · Uses: packages/pulumi-docker (DockerDeployments)

Interface

3.1 Network

  • Name: foundation-net (Docker user-defined bridge — enables name-based DNS).
  • Subnet: 172.30.0.0/24 (configurable, CONTRACT_001 network.subnet).
  • DNS: containers reach each other by container name on foundation-net. No hardcoded IPs.

3.2 Containers, ports, exposure

Container name Image (digest in VERSIONS) Internal port(s) Published to host? Reached by
foundation-caddy caddy 80, 443 Yes 80/443 the internet
foundation-forgejo forgejo 3000 (http), 22 (sshd) SSH yes as :2222; HTTP no (via Caddy) Caddy → 3000; git over :2222
foundation-postgres postgres 5432 No (internal only) forgejo
foundation-rustfs rustfs 9000 (S3 API), 9001 (console) optional (S3 via Caddy) forgejo, backup
foundation-vault vault 8200 No (via Caddy, restricted) pulumi, components
foundation-runner act_runner — (egress only) No registers to forgejo
foundation-registry-cache registry:2 5000 No (internal only) runner (Docker Hub pull-through)

Exposure rule: only Caddy publishes 80/443; Forgejo SSH is the one extra published port (:2222). Everything else is internal to foundation-net (PLAN-002 §9.4). The runner SHOULD run on a separate privileged VM/network (PLAN-001 §4a) — if co-located, fence it (NetworkPolicy-equivalent).

3.3 Internal endpoints (what components write into config/app.ini)

postgres:        foundation-postgres:5432
rustfs (S3):     http://foundation-rustfs:9000
vault:           http://foundation-vault:8200
forgejo (http):  foundation-forgejo:3000
registry cache:  http://foundation-registry-cache:5000

3.4 Named volumes (the stateful core — back these up, CONTRACT_004)

Volume Mounted by Holds Backup?
foundation-forgejo-data forgejo git repos (POSIX FS — irreducible), app.ini, host SSH keys Yes — critical
foundation-postgres-data postgres relational data (users, orgs, CI, package metadata) Yes (via pg_dump)
foundation-vault-data vault raft storage Yes (via raft snapshot)
foundation-rustfs-data rustfs blobs: LFS, packages, Actions artifacts Yes (bucket-level)
foundation-caddy-data caddy ACME certs/account recreatable (re-issue) — optional
foundation-caddy-config caddy autosave config recreatable

Volume root maps under CONTRACT_001 dataRoot (e.g. /srv/foundation/<volume>).

Ownership

  • packages/pulumi-docker provides the DockerDeployments primitive (name, image, ports, volumes, networks, envs) — vendored from olsicloud4 modules/docker.
  • Each service component owns exactly one container definition + its volumes; the network is owned by network.ts and created first.

Assumptions

  • Single VM, single Docker daemon, RWO local volumes (no RWX — that's HA/Layer-1, PLAN-001 HA note).
  • Container restart policy unless-stopped; Vault re-seals on restart → unseal helper (ADR-004).

Validation

  • After each component: docker ps shows the container healthy; an internal curl/pg_isready from a peer container resolves the name and connects.
  • Only ports 443/80/2222 are reachable from off-host (assert with an external probe).

Change Process

New service = add a row to §3.2 + §3.3, declare its volumes in §3.4, and (if external) justify the published port. Renaming a container is breaking (it is the DNS name) — version this contract.