| `PEOPLE_BACKEND` | `kanidm` or `memberships` (see [Memberships](memberships.md)); unset, Kanidm when `KANIDM_API_TOKEN` is set, memberships otherwise |
| `KANIDM_API_TOKEN` | invites and `grants:` through Kanidm |
| `PHONE_COUNTRY` | the calling code a bare phone number gets, `+47` unless set |
| `SEED_ADMIN_EMAIL` or `SEED_ADMIN_PHONE`, `SEED_ADMIN_NAME`, `SEED_GROUP` | the first person in, invited once at start into the most privileged group (or `SEED_GROUP`) |
| `RESPONSIBLE_GROUP` | the group every person a page names as `responsible` joins, at start and whenever the content changes, and leaves when no page names them any more; they get an account either way |
Both hosts deploy from CI now, and a rollout is the same commit either
way: bump `PORTAL_RELEASE` in the site's own content repo.
| | runner | how it is allowed to |
|---|---|---|
| ergo | `runs-on: bare` | the runner is privileged and writes `/srv/app` and `/etc/app` itself |
| kasse | `runs-on: fish` | the runner is not privileged. It may run one root script, `/usr/local/bin/deploy-portal-instance`, which checks the instance against a fixed list and the tag against a release-tag shape before touching anything |
The kasse grant is a single sudoers line
(`/etc/sudoers.d/30-portal-deploy`). It exists so a workflow can pick
which of three known instances gets which published portal tag, and
nothing else.
Both paths health-check the instance afterwards **without** a forwarded
header. A check that sends the header Caddy would send cannot tell you
the site is broken for everything that is not Caddy, which is how
v0.5.1 reached a host.
`IRIS_RELEASE` in a repo's lint workflow should equal its
`PORTAL_RELEASE`. Bump it only once iris's own publish job has attached
the binary for that tag - portal's release tags iris, but iris builds
and uploads on its own run, and a pin bumped in between fails the lint