Files
infrastructure/README.md
T
blandClaude Opus 4.8 debc0c9149
deploy / deploy (push) Canceled after 0s
vel isolation on kasse: own Kanidm(:8443)+NATS(:4223) behind the host Caddy
The vel keeps its own identity+bus so it can later lift onto a host of its own
unchanged. On kasse it runs in isolation fronted by kasse's host Caddy: kanidm
publishes 127.0.0.1:8443 (shared Kanidm is on 8310) and nats publishes
127.0.0.1:4223 (kasse's shared platform NATS owns 4222). README documents the
two conf.d site blocks (portal -> :3050, id -> https://localhost:8443 with
tls_insecure_skip_verify), the reload port, and the app@ handoff.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-28 12:31:14 +02:00

4.6 KiB

Tomter Vel — infrastructure

Everything the vel's own site needs on one host, as containers: Caddy for TLS, NATS with JetStream for the records, Kanidm for who is who, and portal, the site itself. The content (pages, forms, desks) lives in tomtervel/questions and is fetched from there; this repo owns the host.

Internet ──► Caddy (Let's Encrypt)
               ├── PORTAL_HOST ──► portal:3000 ──► NATS (records) + Kanidm (login)
               └── ID_HOST ─────► kanidm:8443 (internal TLS)

Today the vel's draft site runs on Klingenberg Bygg's host as vel.klingenbergbygg.no, sharing that host's Kanidm and NATS. This repo is the same site on a host of the vel's own, with a Kanidm of its own, so nothing about the vel's members or records depends on anyone else.

First start

On a fresh Linux host with podman + podman-compose and openssl (run rootful, i.e. as root, so Caddy can bind 80/443 and Kanidm sees a stable source IP):

git clone https://prosjekt.klingenbergbygg.no/tomtervel/infrastructure /srv/tomtervel/infrastructure
cd /srv/tomtervel/infrastructure
cp .env.example .env      # set PORTAL_HOST and ID_HOST; DNS must point here
sudo sh bootstrap.sh      # renders configs, makes the internal cert, starts, recovers Kanidm admin
sh kanidm-setup.sh        # logs in, creates the portal client and desk groups, restarts portal

bootstrap.sh prints the admin and idm_admin passwords once; write them down. After kanidm-setup.sh, https://PORTAL_HOST serves the site and https://ID_HOST is the login.

Running on kasse (behind the host Caddy)

The vel keeps its own Kanidm and NATS here so it can later lift onto a host of its own unchanged — on kasse it just runs in isolation, fronted by kasse's existing host Caddy (which owns 80/443). So the bundled Caddy stays off (it's behind profiles: [edge]); run the default podman compose up -d --build. The services publish loopback-only ports for the host Caddy to reach:

  • portal → 127.0.0.1:3050
  • kanidm → 127.0.0.1:8443 (internal self-signed TLS)
  • nats → 127.0.0.1:4223 (kasse's shared platform NATS owns 4222)

Add two host-Caddy site blocks in /etc/caddy/conf.d/, using the vel's .env hosts (vel.klingenbergbygg.no → :3050 already exists):

<PORTAL_HOST> {
	reverse_proxy localhost:3050
}
<ID_HOST> {
	reverse_proxy https://localhost:8443 {
		transport http { tls_insecure_skip_verify }
	}
}

Then sudo systemctl reload caddy, point the content repo's lint-and-reload reload step at nats://127.0.0.1:4223, and retire the old systemd portal: sudo systemctl disable --now app@tomtervel-portal.

People and desks

Each group in the content (qualifies: under questions/) is a Kanidm group tomtervel_<group>, all members of tomtervel_members. Someone in tomtervel_styret logs in and sees the board's desk.

kanidm -D idm_admin -H https://ID_HOST person create kari "Kari Lien"
kanidm -D idm_admin -H https://ID_HOST person update kari --mail kari@example.no
kanidm -D idm_admin -H https://ID_HOST person credential create-reset-token kari
kanidm -D idm_admin -H https://ID_HOST group add-members tomtervel_styret kari

Membership is read at login; someone added while logged in logs out and in again.

Content changes

A push to tomtervel/questions is linted on push and tells the portal to reload over NATS. That reload reaches the host it runs on: today Klingenberg Bygg's. On this host the runner is a host service (pacman -S gitea-runner, registered against prosjekt.klingenbergbygg.no) — not an in-compose container — so it reaches NATS on the published 127.0.0.1:4222. Give the content repo's lint-and-reload.yml a reload job whose runs-on matches that runner's host label, running nats --server nats://portal:$NATS_PASSWORD@127.0.0.1:4222 pub portal.content.reload "". Until then, podman compose restart portal picks up new content.

Upgrading portal

Bump PORTAL_RELEASE in .env (a tag of uhhm/portal) and podman compose up -d --build portal. Keep IRIS_RELEASE in the content repo's workflow matched to it.

Backups

  • Kanidm writes a nightly backup into its volume (/data/backups, seven kept); copy that directory off the host.
  • NATS JetStream data is the nats_data volume: every record ever submitted and every state change. Snapshot the volume.
  • Caddy's certificates regenerate; nothing to keep.

What is not here

Mail (a person's reset link is a token you hand them), monitoring, and the vel's current website at tomtervel.no, which stays where it is until the vel points the apex at PORTAL_HOST.