> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# DD-0043 — The retained Themelio as the `themelio-compat` service

**Related ADR:** [ADR-0043](/reference/docs/decisions/0043-themelio-compat-service.md).

**Status:** Draft for review. Qualified on the authorised `dev-ikons` dev box;
other dev boxes remain pending. Staging and production do not run it.

**Date:** 2026-10-14.

## Scope and responsibilities

This is infrastructure configuration, stage 4 of [decision 0040](/reference/docs/decisions/0040-platform-layout.md),
for the legacy source relocated under [decision 0042](/reference/docs/decisions/0042-legacy-source-relocation.md).
There is no new business context, aggregate, command API, event or subscription.

| Owner             | Responsibility                                                                                                                            |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Ansible bootstrap | The `themelio-compat` namespace, its Pod Security labels and the controller's Role in it                                                  |
| Argo CD           | The web, both actors, PostgreSQL, Valkey, generated Secrets, the database Jobs and the network policies; Waldur's Identity Bridge setting |
| `make deploy`     | The images and their digests, the AAI client Secret and the GitLab and Waldur tokens                                                      |

## Application and manifests

Every dev box installs the `themelio-compat` Application
(`gitops/apps/base/themelio-compat.yaml`) on wave 4, after GitLab and Waldur.
`apps/overlays/devbox` sets its path, and `apps/base` patches Waldur's Application
with the Identity Bridge setting. When the new Themelio replaces it, it becomes an
optional component again ([ADR-0045](/reference/docs/decisions/0045-development-loop.md)). An
earlier saved `themelio-compat` profile is still accepted and has no effect.

The Application installs `gitops/manifests/themelio-compat/overlays/devbox`:

- `base/` holds plain manifests, one file per workload or concern, as
  `legacy/gitops/manifests/themelio` does. Its `configMapGenerator` names the
  configuration with a content hash, so a change rolls the pods that read it at
  start. Superseded ConfigMaps carry `IgnoreExtraneous`, so they stay (the root
  does not prune) without marking the service OutOfSync.
- `overlays/devbox/` merges the dev box's public origins into that configuration.

`check_platform_gitops.py` builds the service as Argo does with the root's images.
Every container image must be a digest, and the `docker.io/pharos/themelio-compat-*`
ones must be the digests of the in-cluster builds. Without them the build fails
the check. `model.application_errors` accepts only `kustomize.images` on this
Application.

## Images

The `web`, `gitlab-root-actor` and `k8s-root-actor` targets are entries of the
image catalogue, built in the cluster from the delivered commit
([DD-0045](/reference/docs/dd/0045-development-loop.md)). The Dockerfile copies all of
`compat/themelio`, so a change anywhere in it rebuilds all three. The root passes
their digests as the Application's `kustomize.images`.

The web pins Next.js 15.5.7. Its loopback rewrite matches `127.x.x.x` anywhere in a
URL, so `themelio-compat.pharos.127.0.0.1.sslip.io` became
`...pharos.localhost.sslip.io` and the OIDC redirect URI no longer matched.
Next.js 16 anchors the rule to the host. No 15.5 release does.
`compat/themelio/next-loopback.cjs` applies the anchored rule to the 27 affected
files before the build and fails on any other version or count, so an upgrade is
reviewed.

## Data and credentials

PostgreSQL 17 and Valkey 8.1 run in the namespace on Ceph block volumes, pinned by
digest. A Sync hook creates the generated Secrets once: the PostgreSQL
administrator, migrator and runtime passwords, the Valkey password, the web's
session secret and the actors' internal token. The provision Job alone reads the
administrator password and creates the two roles. The migration Job runs DDL as
the migrator, and the Kubernetes root actor runs as the runtime role.

The provider tokens are the exception in [ADR-0043](/reference/docs/decisions/0043-themelio-compat-service.md).
While Argo installs the services, `make deploy` waits for GitLab and Waldur to be
Synced and Healthy. It then:

- creates root's `themelio-compat-root-actor` token (`api`, `sudo`), revoking
  earlier ones, for the GitLab root actor only;
- creates the password-less staff user `themelio-compat` and its single token, for
  the web only.

The values reach GitLab, Waldur and Kubernetes only on stdin. Each token expires
after 30 days. A deploy rotates it when fewer than seven days remain and restarts
only the workload that reads it.

## Sign-in, Waldur and routes

Themelio signs in through the shared AAI's `pharos-control-plane` client, which
gains its callback. `aai-client.yaml` makes `make deploy` render the client and the
AAI CA into the `themelio-oidc` Secret. Every Themelio process trusts that CA,
which also signs the edge. The web requests only the scopes the AAI defines:
`openid`, `voperson_id`, `email`, `profile`, `voperson_external_affiliation`,
`schac_home_organization` and `entitlements`.

On sign-in, the web creates or updates the user in Waldur through the Identity
Bridge. Waldur's wrapper chart gains `pharos.settings`, Constance settings declared
in the synced `pharos-settings` ConfigMap. Its PostSync bootstrap applies them with
`waldur override_constance_settings`. Argo runs the hook only when a synced
resource changes, so the settings cannot live in the hook alone. The command skips
a setting it cannot save and still succeeds, so the hook fails on its report. The
apps set `FEDERATED_IDENTITY_SYNC_ENABLED`, and Waldur keeps it set if the service
is later removed.

The catalogue route `themelio-compat.pharos` reaches the web. Network policies
deny ingress except from the namespace and the gateway's paired policy, and apply
before the data servers start.

## Qualification and remaining boundaries

On `dev-ikons`:

- **Sign-in:** alice and bob sign in through the AAI in Chromium, which trusts only
  the AAI CA, and land on the dashboard (`dx/common/scripts/service-login.sh themelio-compat`).
- **Waldur:** the bootstrap reports
  `FEDERATED_IDENTITY_SYNC_ENABLED has been set to True`, and the web syncs alice
  and bob into Waldur on sign-in. The check requires the setting on every dev box.
- **GitLab:** alice and bob are GitLab users with the `openid_connect` identity.

Not exercised: a first sync of a user Waldur does not yet know, action execution
(spaces, inference services, Slurm jobs), egress isolation, fresh-VM and reboot
qualification, and other dev boxes.

<details class="page-source"><summary>Page source</summary>

<a href="/source/docs/dd/0043-themelio-compat-service.md.txt">Source snapshot</a> · Revision `f928834c8`.

</details>
