secrets: factor shared values into common.yaml; stop the renderer failing open
All checks were successful
PR build (required check) / changes (pull_request) Successful in 7s
secrets-guard / encrypted (pull_request) Successful in 5s
shell-lint / shellcheck (pull_request) Successful in 6s
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / build-frontend (pull_request) Successful in 1m11s
PR build (required check) / build-backend (pull_request) Successful in 1m25s
PR build (required check) / gate (pull_request) Successful in 2s

Two changes to how credentials are stored and distributed.

1. common.yaml now exists.

The renderer has always concatenated common.yaml then <env>.yaml, host winning,
and the README has always documented common.yaml with a checkmark. The file was
never created. So every value shared by prod and beta was duplicated in both
vaults, free to drift apart with nothing to detect it.

16 values move to common.yaml: VAPID keypair, metrics token, IndexNow key,
REGISTRY_TOKEN, the S3 endpoint/bucket and both S3 keypairs, plus shared config.
5 stay per-host because their values genuinely differ (APP_CPUS, DB_CPUS,
DB_MEMORY, WORKERS, THERMOGRAPH_BASE_URL). 8 exist only on prod — the Discord
and mail credentials, which beta does not have at all.

Three more are held back deliberately despite being identical today:
POSTGRES_PASSWORD, THERMOGRAPH_AUTH_SECRET, THERMOGRAPH_DATABASE_URL. These are
the credentials that let one environment act as another, and beta is the more
exposed box — it serves public Forgejo and Grafana. They match only because beta
was seeded from prod. Keeping them per-host costs one line each and preserves
the ability to diverge; putting them in common.yaml would encode the equivalence
as intentional and make breaking it a migration rather than an edit. Reasoning
recorded in the vault README, since a future reader will otherwise "fix" it.

Done without any plaintext leaving prod: ciphertext was shipped up, decrypted
against the host's age key, recombined, re-encrypted, and shipped back. The
consolidation refuses to write unless it has proved merged(common + env) equals
the original env vault exactly — same keys, same values — and re-verifies from
the written files afterwards. Both checks passed for prod and beta.

2. render_thermograph_secrets no longer fails open.

One `return 0` covered two different situations: "this host is not configured
for SOPS" (true of the LAN dev box, and correct to succeed) and "this host IS
configured but its vault is not where we looked". The second is a failure, and
returning 0 made it a silent no-op wearing a success code — a deploy would
report success having rendered nothing, and the host would keep serving whatever
/etc/thermograph.env already held, including after a rotation.

The likely cause is passing the wrong root: the function wants the directory
containing deploy/secrets, which on the hosts is /opt/thermograph/infra, not
/opt/thermograph. That mistake looked exactly like "not configured here", which
is why it went unnoticed. It now exits 1 and names the probable cause.

Claude-Session: https://claude.ai/code/session_0182KTMrsTHJc3TcewCatJFY
This commit is contained in:
Emi Griffith 2026-07-24 17:40:26 -07:00
parent f4313b5d87
commit cb69a22c0f
5 changed files with 115 additions and 75 deletions

View file

@ -25,11 +25,33 @@ render_thermograph_secrets() {
# The per-host file is required; common.yaml is optional so the initial cutover can
# seed each host as an exact copy of its live env (byte-identical render, no value
# changes) and factor out shared secrets into common.yaml later.
if [ -z "$env_name" ] || [ ! -f "$key" ] \
|| [ ! -f "$repo/deploy/secrets/${env_name}.yaml" ]; then
#
# These are two different situations and used to share one `return 0`.
#
# (a) Not configured for SOPS at all — no env marker, or no age key. The LAN
# dev box is legitimately in this state. Saying so and succeeding is right.
if [ -z "$env_name" ] || [ ! -f "$key" ]; then
echo "==> SOPS secrets not configured here; using existing /etc/thermograph.env"
return 0
fi
# (b) Configured — there is a key and a named environment — but the vault for
# that environment is not where we looked. That is a failure, and it used
# to return 0: a silent no-op wearing a success code. A deploy would report
# success having rendered nothing, and the host would quietly keep serving
# whatever /etc/thermograph.env already held, including after a rotation.
#
# The likely cause is the caller passing the wrong root. This function
# wants the directory CONTAINING deploy/secrets — on the hosts that is
# /opt/thermograph/infra, not /opt/thermograph. Getting it wrong looked
# exactly like "not configured here", which is why it went unnoticed.
if [ ! -f "$repo/deploy/secrets/${env_name}.yaml" ]; then
echo "!! this host is configured for SOPS (env='${env_name}', age key present)" >&2
echo "!! but no vault was found at ${repo}/deploy/secrets/${env_name}.yaml" >&2
echo "!! pass the directory containing deploy/secrets — on the hosts that is" >&2
echo "!! /opt/thermograph/infra — or add ${env_name}.yaml to the vault." >&2
return 1
fi
if ! command -v sops >/dev/null 2>&1; then
echo "!! sops not installed but this host is configured for SOPS secrets" >&2
return 1

View file

@ -14,12 +14,31 @@ no per-host duplication.
| File | Holds | Encrypted? |
|------|-------|------------|
| `common.yaml` | Secrets shared by every host — Discord tokens/secret, VAPID keypair, `THERMOGRAPH_AUTH_SECRET`, metrics token, indexnow key, SMTP creds | ✅ |
| `prod.yaml` | Prod-only overrides — `POSTGRES_PASSWORD`, `REGISTRY_TOKEN`, ERA5 lake bucket creds (`THERMOGRAPH_LAKE_S3_ACCESS_KEY` / `THERMOGRAPH_LAKE_S3_SECRET_KEY`, consumed by the `lake` Swarm service and `gen_era5_lake.py`) | ✅ |
| `beta.yaml` | Beta-only overrides | ✅ |
| `common.yaml` | The 16 values identical on every host — VAPID keypair, metrics token, IndexNow key, `REGISTRY_TOKEN`, S3 endpoint/bucket and both S3 keypairs, plus shared non-secret config | ✅ |
| `prod.yaml` | Prod's own — the three held-back credentials below, sizing (`APP_CPUS`, `DB_CPUS`, `DB_MEMORY`, `WORKERS`), `THERMOGRAPH_BASE_URL`, and the Discord + mail credentials that exist nowhere else | ✅ |
| `beta.yaml` | Beta's own — the three held-back credentials, sizing, `THERMOGRAPH_BASE_URL` | ✅ |
| `example.yaml` | Format reference / CI fixture (fake values) | ✅ |
| `../../.sops.yaml` | Which age recipient files are encrypted to (plaintext config) | — |
### Three credentials are deliberately NOT in `common.yaml`
`POSTGRES_PASSWORD`, `THERMOGRAPH_AUTH_SECRET` and `THERMOGRAPH_DATABASE_URL`
hold **identical values on prod and beta today**, so by the mechanical rule they
belong in `common.yaml`. They are kept per-host anyway.
These are the credentials that let one environment act as another: with them, a
foothold on beta is a foothold on prod's database and prod's session signing.
Beta is the *more* exposed box — it serves public Forgejo and Grafana. They match
only because beta was seeded from prod, not because the two are meant to be one
system.
Keeping them per-host costs nothing now (same values, one extra line each) and
buys the ability to diverge: rotating prod's database password stops implying
"and beta's too". Moving them into `common.yaml` would encode the equivalence as
intentional and turn breaking it into a migration rather than an edit.
If you ever *do* want them to differ, change one file. That is the whole point.
At deploy the renderer concatenates `common.yaml` then `<env>.yaml`, so a **host
value wins** (env_file / `source` take the last occurrence of a duplicate key).
`<env>` comes from `/etc/thermograph/secrets-env` on the box (`prod`/`beta`/`dev`).

View file

@ -1,39 +1,23 @@
PORT: ENC[AES256_GCM,data:fQi1uQ==,iv:qedUPYIGaUh6xJKessGDssYNvLyUv1MwnBJDzhA0074=,tag:FHcxDJC94B2xNMDgwU+4VA==,type:str]
POSTGRES_PASSWORD: ENC[AES256_GCM,data:SXlK1jZq8gRir51EbYWraa2ZRK4bmwCs2ThqNiKZDaU=,iv:8NwAyoMEsU6boXmCbLRENWBhcejvyy1ZlFRi1rCUK2Y=,tag:paNCYlJVVXYYdaHwmu9MkA==,type:str]
THERMOGRAPH_DATABASE_URL: ENC[AES256_GCM,data:iSVBtlYKy7Yd+i0atzNUzMQN8LhNbc4dzSTW2celQsTSs/Cq7dNKurQdvJpmLvJIbUv7aw7u9bDQLckrF/PsPCw+AeQ/qAvSrop2+StVS+Jo6neU0A==,iv:4m7jx+JD2dnq6JlyT8phqnjrTBLtuiXTM0KP8BPOtkg=,tag:7G80BYRGZxrtwp7jN6TxnQ==,type:str]
WORKERS: ENC[AES256_GCM,data:jQ==,iv:zXbYr35kE/oPVBwYVGjrbcOvFNyyn2YUY6LNRx04BAU=,tag:YjhQvhrY3HpfDVT9YdR/Yg==,type:str]
APP_CPUS: ENC[AES256_GCM,data:Mw==,iv:YbELJMxO99xhHn3T/VoAlF2K+q70YqSX7lQe74e3ILw=,tag:MFXHhcTEhN8Z/Ay9rDbOUA==,type:str]
DB_CPUS: ENC[AES256_GCM,data:Vw==,iv:EQQQapb30E7d7QVLbgDwsqM9RlHLi/6d015ptxDyW5o=,tag:C6X1bLZeRbiVzvZ6L29feg==,type:str]
DB_MEMORY: ENC[AES256_GCM,data:IcM=,iv:ufsk06o8dCUQsy4zt5GfbKKFUV8r30h7CYlxrayAj84=,tag:0RfDuSUwyhB66GR16a256g==,type:str]
TIMESCALEDB_TAG: ENC[AES256_GCM,data:LNRpLf3rZE32exI=,iv:8Ipv+YXoMJXAlvHd98Gdy3DQ9Vf9KyhQA8JIBO30cOk=,tag:CW71eYfQW7WXrtTiPFEwCQ==,type:str]
THERMOGRAPH_BASE: ENC[AES256_GCM,data:MQ==,iv:9jP2QHwTTVJoYASWLEmB+YDnGcPHZVEromVemOtvars=,tag:HFamQ250lBShDwPryzBP/A==,type:str]
THERMOGRAPH_BASE_URL: ENC[AES256_GCM,data:WJLcqKzwI0qOh7yb88wIgW9EzKN/uANOn/Dc+Q==,iv:5z2RSKIb+RJCCscUH5AC18rRa0x89RgIHiqu5xUUxSc=,tag:Niw093JTHoH7IjxF9QuR+A==,type:str]
THERMOGRAPH_COOKIE_SECURE: ENC[AES256_GCM,data:qw==,iv:5wsDViGkCWZC0hNzANfPLVgbNrO5qTcVQ9q167EMN/k=,tag:lZfmR4uHgkPl45mul2bOcw==,type:str]
THERMOGRAPH_AUTH_SECRET: ENC[AES256_GCM,data:9n0Yup3NxOO0NzUwNlA7aBvEFQJT8xIy31yIfUkaMoyHOlmzsj8JukjiDw==,iv:OE5S2fsLkQUL/bsinO6gHxcxr3fXBJXwpIEDWQXtWHI=,tag:WDqAe40LiTtl3nM2iuFLOQ==,type:str]
THERMOGRAPH_METRICS_TOKEN: ENC[AES256_GCM,data:FgjpP9BZ8OakTG5j4hvP3RM5rGwqMQVSqoOA3QUUs/Y=,iv:qYIGtbFRLtjOuBPuKJo7NG4tYlvwJ+d2w0B0p6++wzU=,tag:WworrFhfAJC2nulIvK/fZg==,type:str]
THERMOGRAPH_INDEXNOW_KEY: ENC[AES256_GCM,data:WFTDtzYb6nzLdsF11NarGCvm+B1WhmAOPNw6V8232/8=,iv:bsAO1Jq4h1paeNEJBg2fpkvQzBZSndEc7uf6aueMGfA=,tag:J2pHU9z6HASS9ggXE8oP4g==,type:str]
THERMOGRAPH_VAPID_PRIVATE_KEY: ENC[AES256_GCM,data:7jpriHBwPlgA1n5CuXU3BXn6zo0r8/IcEv5tBHDKxQ6PBnX9wG2rBMNLTg==,iv:lk9hg+mLYgK0db321tri12kWR1dntjS6Kld8Wtcfr3c=,tag:LB3V+6X/pGugU2hun9/dCg==,type:str]
THERMOGRAPH_VAPID_PUBLIC_KEY: ENC[AES256_GCM,data:05tQe7KaNQXWHzKQENrdk4X2hSUC50Z8TKBGzHurf4NuRYTba4FfAu2fj9ODFOWeV/j0LKk48zE8u7QfKz7pgrFuGccKQNmW9MTYrcGERQ8GlZh/n97l,iv:jy9IC+FKCD6Np/HlmZZDIfX8aMr7BfA/vL32DPFUzFU=,tag:w38/Q4MHqtldoE0pEI5WmQ==,type:str]
THERMOGRAPH_VAPID_CONTACT: ENC[AES256_GCM,data:ANT1qBKnFUjyPp+NwF++jam69B8cWADVBtIcHdci7A==,iv:4tCQCQbWGnnlo/qKv9YBmmXlTfJkrSonT0M7y8UImIk=,tag:TLKQKzuM/M//Uq7gde+JUg==,type:str]
REGISTRY_TOKEN: ENC[AES256_GCM,data:P7oFRZ8SZm6ZpJIMoH/xoNu9+sD084b6DUQWc3tSx4qGFPxXveDHmQ==,iv:B5J+qFgorUoJ3TK6TKWwXuNLs8mD+plaY4W8UaS/xEw=,tag:8CfR7DtJ5dd238n6YSyqNg==,type:str]
THERMOGRAPH_LAKE_S3_ACCESS_KEY: ENC[AES256_GCM,data:83u4MJ5XjT9uxtBiJwfH+ICPUj0q1f7NMIXnqK2pjeA=,iv:QEq02qRPt0SO1RcsDZnOIe1V3mFu1bLxoaxGuv3Q8QU=,tag:HdF3hmPXCIOElGXmoy58vw==,type:str]
THERMOGRAPH_LAKE_S3_SECRET_KEY: ENC[AES256_GCM,data:FlRe/NNxyFTLYrV3pBonTGC7Qvo9cxwAYuIDEo6tzcA=,iv:QqTpQ8UM3EiV4p4rJztjx3jdyAFJuqU+4rkpe7epfCk=,tag:W+sCbhE1sK9+Z9quOpoJyA==,type:str]
THERMOGRAPH_S3_ENDPOINT: ENC[AES256_GCM,data:3ooQIJ9QtksXnQpM3xDZVV+DhtuIZEYjWfISs716,iv:n0Hd4Vm5zDlSJJ0UeH8dyItu39UYaSmlgYcLN1vDJuM=,tag:xqxl1dv72dS7tXjBRRA2gA==,type:str]
THERMOGRAPH_S3_BUCKET: ENC[AES256_GCM,data:Q4fbMVujISo567VYdOgMkQ==,iv:sdHNp4bE+7BrrjSDWGCybhM1tbZhGzW/wmOuZOE9AhA=,tag:nVj3aO3DEwnjyPRdhjYuRw==,type:str]
THERMOGRAPH_S3_ACCESS_KEY: ENC[AES256_GCM,data:Gdifczs+h4KQlJViCV+q9TR896fMajwllKiV/feN8wI=,iv:ekRpckoRI5SdqOKfx+85PAnA3J1PsZrX3tXMGLtOO7I=,tag:meTPyJCM83cfwX6sC25aEQ==,type:str]
THERMOGRAPH_S3_SECRET_KEY: ENC[AES256_GCM,data:UsTtqDA1h2H1F8czE31mj/QfgdNb/GEg8HWQxrl6nCo=,iv:h3jO38fJ0gcih1qvnBWPX3AS8EWOErZbIQaC10lV70I=,tag:1NoN52vRiUHeu10Lzgai2A==,type:str]
APP_CPUS: ENC[AES256_GCM,data:Wg==,iv:9kB0WruqLCIkJp7i+t5kGB4qv8NzHwgTQlFmYwhTEXc=,tag:q6cKDI86YrFJjGRnEkdweA==,type:str]
DB_CPUS: ENC[AES256_GCM,data:7w==,iv:xDOeZo3dJ5gnXIEGyOBGZm4RUTNyzinMkvxWoY/EbQs=,tag:ikmAjzeM/U4w5HDZ+C7mgA==,type:str]
DB_MEMORY: ENC[AES256_GCM,data:apg=,iv:4+GHbccVBFUGtrP12a2oEya7Hz0KUhJAFzdwpqQJ2sc=,tag:X0h2nYza80c9PjeNgqX7Uw==,type:str]
POSTGRES_PASSWORD: ENC[AES256_GCM,data:X5HmOvHVtm2mjeLZO7FaEWP0Qq767D0quycr2iI3/Mw=,iv:BQsjJaDPW9RxnMldE1rCU3yP5WBOXvc9VjnTim3kMZs=,tag:yfdi+T7lBbBK544bSvEZtQ==,type:str]
THERMOGRAPH_AUTH_SECRET: ENC[AES256_GCM,data:YjgknnUhAU0gW6YuMCmlZYfVhOgye52MLhVF7nKMuc0ToGn0OTqqoiXN0A==,iv:Q9XCoKa5Y/7V9b72tktGZ12jgPumC4kJKjrtCM39ihw=,tag:3kEpMjLCEFQcpSir3N9f6w==,type:str]
THERMOGRAPH_BASE_URL: ENC[AES256_GCM,data:bus0/ibfLohpsszHZKKoGe5P0/Su5i36Hk2nzg==,iv:QI3crdbipse62xdakCGxPIA4wmdq/T4KzZSvi/NE1Xg=,tag:/S4Bzk2aDoA6ahDlW7qG8w==,type:str]
THERMOGRAPH_DATABASE_URL: ENC[AES256_GCM,data:xMd8m6VajAjtwQy8DDqTs4VbyASG8ua8FUZHfPmmEzZEb4cTtYILJ+z6LXhcyvdenDsPg2/GJx5BApvVAKSrWrnSzNWnp+pAs7x+VnNzZ2giZ9meiQ==,iv:MOU+SB/taEc/ExRc07ChF1ksOQiaDj7TPMQq+aV4Ltg=,tag:XP2h9KBGJE7JzripO7yjcg==,type:str]
WORKERS: ENC[AES256_GCM,data:BQ==,iv:XYgQ+Sn9OnbUR8LVW+4r9vhnQQJZmliVomfyRmUqMsw=,tag:JzM3JsNR08z1pePSTl/dhw==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDYThHTE9VS0ppWmR3djdE
V3JreTBudFhqZTR1MEhZMXd3dzFTakRDWlZ3ClB6OUhSbFdjU2tmWVZXZlRtWExG
dXhtTXFLV21DemUybXB4aWE4aWVSVlkKLS0tIHNEaVRGbWdPcmlZcG1UV2tFRFdh
RVdSemxabXg2S2d0Y0tYeGh1MXZIOUUK2dJfuUkq5NZJPUrteMt1E+QvQZQNsnhr
H5nrEF+WqfTizIbZ0rowqr8QxAbn6zcb/5+VZGjFIUP1qfXe2cI+5w==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFWHVZU2xSS1pmcy91S2w5
MWNhQW90TDdQdDdJV1BHM2Y0a1d6aFJOS2hnCjRodHZlY0orODdpS3QwTVdTdXM5
Z3RJOFhyR3AzbjljaExkQjN0YUJ2ckUKLS0tIGpua1Z2TDJrRzE3WFZlMWNpL1Ja
VG45eVM0MldLejRNd0pweWNna3JYWlUKWuNU+6PqKlbr7F0ckrNxsMF2OyXh1fMu
cLFBIQg/7vO7O7PJ0VIy0Ugfq6gj2Gv91qKJUGeOXOw3tv1Y6HHbjA==
-----END AGE ENCRYPTED FILE-----
recipient: age1xx4dzs0dxlwvkv9sjuqzsphl7lfrxannkfken374yu2qvvcte9sqzktqt2
lastmodified: "2026-07-24T20:06:42Z"
mac: ENC[AES256_GCM,data:2yk/ZeaTxefXMiRVbUfwiEpa76KvR6WZ/ySBONi66IQr6olCV7P+ZOpfZ49vN2HWy8XycCABjRYrxvpM9FJcOIz3srTWLrtc5Mv+hJ1gQedc57gItF0KuUBxpF4n0AuI+OFuLtB8GFxohFCk8/ZNAdGyhK+5RH95Y6aC58+2y9c=,iv:W5uBB+dkKKxAQDgsDb4uitVWrB0zgGh3Zg/5qWrs5OU=,tag:/mtl9OHGc2mwM+9d2cnFqg==,type:str]
lastmodified: "2026-07-25T00:38:24Z"
mac: ENC[AES256_GCM,data:lTpEmA8gweS+QlAakIziXxAEiHBUsNDk5lGwVG+uaXNhifIYWbu+QWkklP4wES9o3pu4TNCzFboRWkBbnzhW8HYRglkSChsmmFXDChz67IYlaveCFwczxffp39iypnV/EKaBCa8m1eDJ5kugzr2s7+blagCH0TwpNXBqZShyGNE=,iv:KBF6CreB4oTJGwNetTj27Grox30X3s3qEEYb1cJXmIE=,tag:YfRb5yAoY8K/TXspoZYdcA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.2

View file

@ -0,0 +1,31 @@
PORT: ENC[AES256_GCM,data:DygsjQ==,iv:9CQZ8qDFdUkfUDBdS5dpx79YqzXLzTS1KMafH8N8k8c=,tag:HtO18+gDgU+lfBhu6Zswmg==,type:str]
REGISTRY_TOKEN: ENC[AES256_GCM,data:C7IHLfZvhNuzwy7TGl00u4gyj0YqEpScEaXW9VaE/uYMg51Wcr6oLg==,iv:BhBSppQ2xLRQm5u1DkFFAgB+EgKp7wYw2KlPDpdbUho=,tag:ZbCHYCY+EQg6AJwAr/J27Q==,type:str]
THERMOGRAPH_BASE: ENC[AES256_GCM,data:sQ==,iv:Pop6S2qU0o2+2xMKWQD2P3Vjdh4rUafWF7o2/k8b6fw=,tag:NTJKCX7hH0shrVzmTAYrkQ==,type:str]
THERMOGRAPH_COOKIE_SECURE: ENC[AES256_GCM,data:CA==,iv:TM6XiygrDQn2qps4lO6hY2ldjW8LWlO3D/R/DBPqvGQ=,tag:+thBT7fMzH+DigecgxF4CQ==,type:str]
THERMOGRAPH_INDEXNOW_KEY: ENC[AES256_GCM,data:XZEMMJfmrSjc/sKEePaigTqEkh3ob/E25R25DvOAWwY=,iv:fqdaegzG1Na0zW+UgOh92RS4FP22pxStdD1/Jq5DRjs=,tag:/8ORtVkGUWjqgGXG/xZHmg==,type:str]
THERMOGRAPH_LAKE_S3_ACCESS_KEY: ENC[AES256_GCM,data:5pL+8tZbs8TZ5ezJpkPUmMss0gLcte73lU+au/476Ws=,iv:ujU3G+rbPZQx/igg50GeIPQxqgd+szIsjc3AqGhGLBU=,tag:hrkgn2DPkmBGThlsdMplqg==,type:str]
THERMOGRAPH_LAKE_S3_SECRET_KEY: ENC[AES256_GCM,data:XG+6wizXMVVFixhqLhg72HoKw9kEmH+Tty8jYMycvVk=,iv:tNz/WrkMYip2QyAr6bRy6PaIee56ZZ64BELBgvidVT0=,tag:HiQ55DGbeDAOSF0vpNWpWw==,type:str]
THERMOGRAPH_METRICS_TOKEN: ENC[AES256_GCM,data:DmOZU3HWQTuoSvQMb7iPkClJbDH48NB3OcRIYRkqNz8=,iv:acgGh0w2mc5ZD7rB7m/GW3Awsx3RWK+02L6YkAv5Rw0=,tag:x3U9F0Dekw/r9euRMjVQAA==,type:str]
THERMOGRAPH_S3_ACCESS_KEY: ENC[AES256_GCM,data:hTSSUIfOlY0GYI8gZ7FcOi7c89iye0Ym73l4M22zkNk=,iv:aPdbXKb4pOqigo9J3a7oM3qTip6HQcHUQDlAwg6XfoE=,tag:C4p+Ob/iSElNdF4m9E+M7A==,type:str]
THERMOGRAPH_S3_BUCKET: ENC[AES256_GCM,data:vGJDaElN6JRUawlD55Vr5Q==,iv:4ZAQ5NXb9xuMN+qPspM17W6zm9NIDXLX9hTJ/etSL0E=,tag:Ss7xaUl+kOiVF7oDs9FiYw==,type:str]
THERMOGRAPH_S3_ENDPOINT: ENC[AES256_GCM,data:PtZg52Uu70Ndx7L/dRTCZTmqv80NXiohOg+gfrzG,iv:hKQTy3Twd5uWwnS4UIs9oqT4NzpYUWQxWO1lUqrzpAc=,tag:kYwYp58fxkzRe5yQPSXa3g==,type:str]
THERMOGRAPH_S3_SECRET_KEY: ENC[AES256_GCM,data:1xuZRaC4p+ZM/oFAUuGDZb6AAu2PbjFrHQY3zmczRpI=,iv:xzI24qlUOvYiThBLOue0odvopuxdHCwDlwI4JwiP9EU=,tag:yRS9GjGLBEYk/kZ6miFudg==,type:str]
THERMOGRAPH_VAPID_CONTACT: ENC[AES256_GCM,data:N9mPLx6Mcgqm5TlgqIMFhNuZsBZxVPXf3LplO9k+VA==,iv:y5+MLI0zC5Kb6pRdORTMVJ1iMMoFrVRfv99mKWMRjp8=,tag:7VvKCLU+1TJtQcHWlDwybg==,type:str]
THERMOGRAPH_VAPID_PRIVATE_KEY: ENC[AES256_GCM,data:hTxKlgPWeW4HGBf08SxdAdK+ce6T5Fl9f+5tSGV6WpS+za5EI3zsK8BgRw==,iv:l+omFaCyL2+PHWdchadkmED2gIAoj5T0u/Ltzaw8mok=,tag:AQ3wT/wD5JAouX1M+Tjjmw==,type:str]
THERMOGRAPH_VAPID_PUBLIC_KEY: ENC[AES256_GCM,data:bgtqZFfTzYz5llh/YhAyGwGtRedK3/ze8SAWj8YdldY6s4nt2F3XeTANVO2Y9+NzvXc16PB3WWPnYEzCxnrG2KciIeYpOYZMoPpGldENPBLJtLa76Ej9,iv:HuZ7XtlXIt4wyJ79k3IYjHfWrQp7lnak4uCspyzS4BE=,tag:uPZvba8LVugDy76pKat6TA==,type:str]
TIMESCALEDB_TAG: ENC[AES256_GCM,data:3mDU/k5fx0894+E=,iv:/iw3BZPF3mZ9M3bFNJSTzP9t15YZWCJwzoVXYL9Vj9o=,tag:Y4ez3+XfLJN1PtJyZlJj+g==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrdjgydDB2VWs3VjZ1TjUy
dDQveXNKdGFkSDFrNXVMMmJKM2xtVThxWGswCjNxUjhvZ2NRYUJFQzcwaDFKL3dW
OFZ0RjFBODBQcHJqYjBjSSsyZll2cTAKLS0tIHZlYjdqSVRKZEtmcHJKOU9UN010
QWdpRENmTm5HaHRTNGJybTEyUkttN1kK7RTlbbT1u1EGE1IVykk576Tig2Bgm3Fh
bncE6yn10QK5kVcF9dDvpQ6RbaG+ESpNZTnuhSL5PDqrKtjnsV0Iqw==
-----END AGE ENCRYPTED FILE-----
recipient: age1xx4dzs0dxlwvkv9sjuqzsphl7lfrxannkfken374yu2qvvcte9sqzktqt2
lastmodified: "2026-07-25T00:38:24Z"
mac: ENC[AES256_GCM,data:C12PnFE7MO0Ge5dCAHCcyXh650hFtRuexl5d3DL0IqTf8nLyNtRJzrPyl9whIs+S3HulsXh3xJOwwPdg6aTqdCfGSQ4Kp5qV+OHxH9lfpHs2yH+exa/eX+7Khpjk0OfvX5beC0GSPpYf9c01buaUTWcIh3pJXpDMdqr+aLC+Zd0=,iv:6VeaaEEkWU5rdTHy75rc2emb2u/HmOmLzO11D38ZXHc=,tag:iGRevzdtqZF9kyH0wxqG3w==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.2

View file

@ -1,47 +1,31 @@
PORT: ENC[AES256_GCM,data:fIs4bw==,iv:IxgyETePv05oUAbuNrMYi02KFgR3P2nrH21m5BzubP0=,tag:dLvmH1eZ+GUR1SdouSZyOQ==,type:str]
POSTGRES_PASSWORD: ENC[AES256_GCM,data:ozf8j0NeYoPezyvop7AQfecLbDrAF2Gm/rH6zy0m7tc=,iv:uvgkKEHEdsvTaF/JEuApwBGCoWVE5X6ISjk/8twerwk=,tag:2GIQY8BxGB2on+RpKMTGsA==,type:str]
THERMOGRAPH_DATABASE_URL: ENC[AES256_GCM,data:tk7/lmdlleneechtHysVACO6kxpNv88xpMjzEEyOgOI18fZ5X6g50re/4A3HVBRCDajzFMiVi6RwkshuD14431cuSy+fUwG5YSmJ59EgVQdjk3TXJg==,iv:5S9mla2hWZdW53J4Hxtwh7Hm51yElZNwpnI+Y5J47Ws=,tag:X9bgD2hEHSN6v4BWMsXV6Q==,type:str]
WORKERS: ENC[AES256_GCM,data:IQ==,iv:WSA3xRt9/OgWeaOx8LM17apYa8Ig/P5oLwx2F6yqwEM=,tag:2GhxzgvAGeF8QC1j7lJbqA==,type:str]
APP_CPUS: ENC[AES256_GCM,data:+w==,iv:Xi5gGmV1/7GLOK2s6tmajyYOi73iiQ1MiSIjeVzeUec=,tag:oUP56tzWwsVy+B6BsI9IXw==,type:str]
DB_CPUS: ENC[AES256_GCM,data:Yg==,iv:YJUkzGMZKd4ol1u3M1YisPd0RttCC6Q4GLzavrpxv7I=,tag:49RAdZFKlB3T50dL8mWtng==,type:str]
DB_MEMORY: ENC[AES256_GCM,data:MzTb,iv:oT4Nuj2DBzOt2j/pXKGcWDxHEJspQyFNd56IfHxsg+M=,tag:PdnuD7CE13FE6cHs/Jzp8A==,type:str]
TIMESCALEDB_TAG: ENC[AES256_GCM,data:M5c1teXwfm06bAw=,iv:NdmaDupUdOZIhtKx4AdILszGNau35PpgnkCEWR73SM4=,tag:SVtQbhKQJc33+40nwDOX6w==,type:str]
THERMOGRAPH_BASE: ENC[AES256_GCM,data:qw==,iv:qWtU+SEPpTVsskhjLBtvgAw8jc2FmJJEkIdxt3ExNd8=,tag:al5lpHwgbzWS7MUD8pBXVg==,type:str]
THERMOGRAPH_BASE_URL: ENC[AES256_GCM,data:IEPwvaGXhyJiM+TfbXUycoURU57uBMA=,iv:87pKtsL2hZVMRqHfwRP3nxTdG6iUpzbrUVCTZDTjR8w=,tag:1q3lOTpIn51+5RD4TV/Ssg==,type:str]
THERMOGRAPH_COOKIE_SECURE: ENC[AES256_GCM,data:XA==,iv:a7Ck0qcUOmWLJMtWO8j7GlPRnx80lj0iZYnkAPl0JDU=,tag:BkPelQBxIus4pwLJVlcQPQ==,type:str]
THERMOGRAPH_AUTH_SECRET: ENC[AES256_GCM,data:YVAX4BlmBzq7hcHQacVxDFLxKnd6C6QiEV7N9/MCjJNUlQonDX/+aydXpA==,iv:8PON0qfVH1LsXHOXUbogRJcTeosswQLVRlctKkscLAw=,tag:Lhnm5QGRJyVDbl5CIzBbPg==,type:str]
THERMOGRAPH_METRICS_TOKEN: ENC[AES256_GCM,data:SggaGZF2wIFV3uiE1bnWWreyCuFXrtmvHi+Yp+tuF9E=,iv:QcgEAWNKRS3kiBfdjnK8UzG6XQ4q4vBoRgT2Uyw2NI4=,tag:ElAAWl0/OPONBTabag72TA==,type:str]
THERMOGRAPH_INDEXNOW_KEY: ENC[AES256_GCM,data:UDCEfsWNX/4DwAkyo/QeDckFTBe7r/CT9/sEgfdzn88=,iv:TAY5BdkBq4KwtXhRazKiuTdnjE5UVcWFBRkAlkpnC08=,tag:knZ77X3fCfEPpN0eC+mP+g==,type:str]
THERMOGRAPH_VAPID_PRIVATE_KEY: ENC[AES256_GCM,data:fOxls5noaPsKU23kNOoaTrYL1r68ggkLp3qxG7Kpc5W7VSGjBRlpvpNlOQ==,iv:sF2JMS8QyjvAjnfA/LjgCiwFJgWPZCs2ZtyHJrhstHI=,tag:wbwJl2whlzoaKh6OhDYu6Q==,type:str]
THERMOGRAPH_VAPID_PUBLIC_KEY: ENC[AES256_GCM,data:IRz3U8C5vQ5n32uX5TI8IVmyWudnmprV1rJxMjl2kyVda5WK9tWkQ8qxq0XuRqgsc5vaLYNR/pfK/cJipBcFA7oR/zxsNzzyth49UzzTEVeTrkt4gPIj,iv:OCA5AcXjSoYItL09OHtYkJczOrkq1vS2D0V7msjXKhU=,tag:JqRPiWDLSisxZqYUcC6pNg==,type:str]
THERMOGRAPH_VAPID_CONTACT: ENC[AES256_GCM,data:MmpxuSfiLpZg5RsxE9nl2Vz6J2HocgONf1Oe2atk4A==,iv:ijGCvfbts9ZhzXWR9mou8o6pQ1AkGdEoFQs+zZNTMpo=,tag:QmuRJoiIIAPwCbjRD/0S1w==,type:str]
THERMOGRAPH_MAIL_BACKEND: ENC[AES256_GCM,data:etyPNQ==,iv:QnmEDt4aKYBBB/cp+tvvX7MqgVDGBBgqCO22M/GvEP8=,tag:STILjLmrxXrT8TEatf2phg==,type:str]
THERMOGRAPH_SMTP_HOST: ENC[AES256_GCM,data:oiVnVJNwjFFdnA==,iv:PXKNKaGas3aTKpUk53WX5NJw/YS5Sxxv62RNZBlFd0M=,tag:sJMo0EVCVKZhj4pBjJGWRA==,type:str]
THERMOGRAPH_SMTP_PORT: ENC[AES256_GCM,data:l3o=,iv:B2Qq+Eq+EL8F5R3dio/jxl65RGYtnouU9MChlXlnlaU=,tag:vLvd50WBMXVAoN+XHyltpQ==,type:str]
THERMOGRAPH_DISCORD_CLIENT_SECRET: ENC[AES256_GCM,data:SugVMdKFZbudfN3l/ZbYZf9lLBVdNPrJ7p3keOM9NmE=,iv:Cc7m2t+49edItI74/bNB0/J/XF6fsG+efXVdBf9xj1Y=,tag:jgBGAXaMrmMh3Jkgh/zBMQ==,type:str]
THERMOGRAPH_DISCORD_APP_ID: ENC[AES256_GCM,data:Gkk9f189A0pqGEMPFuqFKIxHAw==,iv:OMML3H845FEa3BLBphlkX7vWn04mpD6K4i0GwFBjgr4=,tag:V+GGCXgF1ZWuCEhScm2j8A==,type:str]
THERMOGRAPH_DISCORD_BOT_TOKEN: ENC[AES256_GCM,data:NWuiv3jQFL0v0rB/O/Kt0i5y7eUoDw1pfrpF67zr4h7uN+MrvHau/0juaEEDurOuvwks+1jikZUrR1UX3warmi6sRZsFjRqp,iv:cuA1dvi5kNJ6/OM24xuQSTPjkW+FdfnMYqrePO+oyhE=,tag:Cq1uxz0PFYNUzJUsOWfSGw==,type:str]
REGISTRY_TOKEN: ENC[AES256_GCM,data:HUDgFdmIKadUP79HN/ojKheWihTds1bR8+NxJnsY0G0PRU6QgGq3aA==,iv:XK/IBNpLljaPZYJddWDPLiykC7k0TcVKr82xa6bKfRQ=,tag:AfJBdXE2WkT6Fl61IuVWIA==,type:str]
THERMOGRAPH_DISCORD_BOT: ENC[AES256_GCM,data:hw==,iv:Us8MoaQodVojvUfi5P+gdvL7QHMnT629GP4n/8F0rcA=,tag:fo4/zq3hTRyQPwLy9kE8IQ==,type:str]
THERMOGRAPH_DISCORD_WEATHER_CHANNEL: ENC[AES256_GCM,data:OaCxv7LZ6Pj3UARlEKayilzT4Q==,iv:rdH2TvqtI1yedcHnAjn5ymbhaiz1F7lAY1mHSaBiBUY=,tag:BBKR/XtljAaoheQDDEMD8A==,type:str]
THERMOGRAPH_LAKE_S3_ACCESS_KEY: ENC[AES256_GCM,data:KkaOozI9y/IJDJMPnSA6sxmUzdD/ao6RoYuDhTObuEg=,iv:zSC5ICu35a0A83OFTl2ft/8SIUeiXAJn9uWL4/q5IHk=,tag:dIkCNTmChd+/s39C7P8YqQ==,type:str]
THERMOGRAPH_LAKE_S3_SECRET_KEY: ENC[AES256_GCM,data:z5Yh2FTxDCaLBbTuNSXEYMCbn6/abi08Ou+brzbbALI=,iv:s0bsnId0qiBBnAhAGVSO8WMFxpIjj43kkb/092Kwo+8=,tag:qv5dZOt6dyDHnIFHbX0yKg==,type:str]
THERMOGRAPH_S3_ENDPOINT: ENC[AES256_GCM,data:Gs8UZiaXyDSQMVi6qHdjf46I+GHjbJxne/S6XDQ2,iv:fAnXNzLU+4sykVRDh8G35tloKkr/6de/Gn3IKx0Cn4w=,tag:UBKnOUqYO04tQPp6+8EeHA==,type:str]
THERMOGRAPH_S3_BUCKET: ENC[AES256_GCM,data:FDIcNaBgJkhILXL1/5Bw/w==,iv:aHPhSd8XLs6aizu64fa51NLFpFQceK4+e0v4SygJ9QQ=,tag:Ppz4Orec54bpiacRr/hqEQ==,type:str]
THERMOGRAPH_S3_ACCESS_KEY: ENC[AES256_GCM,data:sNZj3vQP2sR/TwvW3Xn+RYaX2wr2r5au99oyb/S4UNg=,iv:3qsuksTfLKsI/RBNMQtRfmYAnTMLpeASUEmZk1/+AFQ=,tag:OIvVtFDl9u2mSCwNd05Oiw==,type:str]
THERMOGRAPH_S3_SECRET_KEY: ENC[AES256_GCM,data:BkjZCjpz0FMMl3XIzlH7BKVJkK0e1mhMx9ax8m4OEJw=,iv:JEgXkYHCgafPXKjJZFWifyVOYBtknmxmbWW37jc4tkM=,tag:jvKrdddwYDZYZL4A5Y+7kA==,type:str]
APP_CPUS: ENC[AES256_GCM,data:Mw==,iv:VDl9fjeKCS6QKOxqEcnsUzm28hz2gi862U/MXdt48e4=,tag:pNOUUCcXnPXrGwrfV7mtPw==,type:str]
DB_CPUS: ENC[AES256_GCM,data:8Q==,iv:pSM/YYBm8wC1BXKbKjys+zgBilZrm3nF+8X6QLDfnyM=,tag:hc5eZNmH6kFbcYmBaWt04Q==,type:str]
DB_MEMORY: ENC[AES256_GCM,data:zXCT,iv:Z+Dc3x12wL2RCPj67vkQAb5/h6MlS2eU39MP8kgYDxs=,tag:izgtoFV/sTMcdg0SCpncZQ==,type:str]
POSTGRES_PASSWORD: ENC[AES256_GCM,data:kRusxDXF8YvSQVNCD4iZlvt7pXFfFZe0hWLELxl5904=,iv:xzM54TsbVb+94Yp2oDB7Bi5R1a9mV7HxY+oOBEE4ANg=,tag:JRYqcpkAxsA8a/nvhRlODg==,type:str]
THERMOGRAPH_AUTH_SECRET: ENC[AES256_GCM,data:8KnDLjhlE7Hv7G0CXuttmHjzBgqS38DZMpeRJOqYJhOJ4vjVNeWWyd9Ibg==,iv:MNiZRkWnV74+4ALNwcWyFbtyFOU/kuU5ZCH6OooF5X4=,tag:uZoZ4HwGxVYuWVkxOFNLFQ==,type:str]
THERMOGRAPH_BASE_URL: ENC[AES256_GCM,data:m7fOK/MQEl41pzRIvqMrx1tqYjxt29k=,iv:qYdOs9gitsL9yz39bO3OvlKaHlMSYb0Qy7fHoBrikGE=,tag:4dEmR7twOmUosk/AfxOtqg==,type:str]
THERMOGRAPH_DATABASE_URL: ENC[AES256_GCM,data:qZTguYgyrMxgI8thK5+16TP2X+Y89je7n92PcMm8Ha4v1Iig57I7hmhDL5m0HT0O6R2d1VR0LfhGa2G5cs9Vu/bwpqQKcy857w2hXjHegc6PB3gJ/g==,iv:ncj8a8WwLocqVloxvnIQLZginpXmyN1HOE9NOuh54nE=,tag:CL3lqG/Jdk4I8rccX/RASg==,type:str]
THERMOGRAPH_DISCORD_APP_ID: ENC[AES256_GCM,data:uFKK3zDAtHezqUh+J7vTfDOZjA==,iv:IJDdNoTaM8EiXw9WweMI7Tw8yUvelUDCqILb/ybusv8=,tag:rasuzDgBwO2hPHRHWYpewQ==,type:str]
THERMOGRAPH_DISCORD_BOT: ENC[AES256_GCM,data:sw==,iv:yCSFbOotnUWSGgsKwcbk4Kf6QDw8e6ap9WJCaf4R9y0=,tag:REJXFRuKYrkdsazbj/ZzBA==,type:str]
THERMOGRAPH_DISCORD_BOT_TOKEN: ENC[AES256_GCM,data:qm8qSXUjnBh5/y3oEtWwxT63dJMta9x1cm7Fqoa+UrKffHrWgABirYnXdw30Xl8u/Xpozb9XTZjCXNOoLytJHHLcQ6pizK3h,iv:Z1btW0ISGgaNk8k+9vs0X8XFjWDnizysJmlytTbObrE=,tag:KmevCBpKUP65zAXb3pCpRg==,type:str]
THERMOGRAPH_DISCORD_CLIENT_SECRET: ENC[AES256_GCM,data:dOhFSOHOzKyY6Eo5XxicTqeMs7uKMoBYSDko62U//cY=,iv:feppgBFYJqFCATsjoMDdtqImyRP+Fxlh3MGHZr69ARY=,tag:p0fMiqFxWKzBSyaTeYfqJw==,type:str]
THERMOGRAPH_DISCORD_WEATHER_CHANNEL: ENC[AES256_GCM,data:L3WUamgkAd6KwX7f9hK4IquX4w==,iv:GUZMsF/nHevbjDaYuABALJJjnFQpAiv9pviKFAZ7zOg=,tag:D9Aprtq/uY56yCcWacyMeQ==,type:str]
THERMOGRAPH_MAIL_BACKEND: ENC[AES256_GCM,data:sVvAiw==,iv:v7APCvTRHn2WVt6jR79aSuvAr71WOuW01XkxxNJd1VI=,tag:W9Mjv1ugYTTJIqhOwEYLKA==,type:str]
THERMOGRAPH_SMTP_HOST: ENC[AES256_GCM,data:57g1wwzwH2ZpOw==,iv:oCE45LyGfMvOGkreX/lfbaE71bxULcDLRRv5trfptr0=,tag:pGU56sqExjkrsLCsP30tvw==,type:str]
THERMOGRAPH_SMTP_PORT: ENC[AES256_GCM,data:L0M=,iv:jlq7qKwNO2CQsmjXm2YGar5vpVOouQrTPv7aCZqjZV8=,tag:VqYm/rz3XXjt1ZIAzcOk9g==,type:str]
WORKERS: ENC[AES256_GCM,data:yg==,iv:cmix2WTi17ps925n5mzGC8jNTUXgaGMOsY8cv5b7K88=,tag:KZW4B4s/yRI6JhvMf+Sejw==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTK0ZVNC8welBOanMxRzFJ
VUNNV1FmM1dIdkpiUDQvRFlIZ2JDc3hNOTJVCkNiTHdJRHlHYzFpeVBlbFRLSnN3
VEc2NWF0RzZCZDFKaUJuK0p2VUpNTkUKLS0tIE04WmpiejBuRUM1NnE1WTNjWDdp
NS9ITjkrSmhueXBLd1pQRDYxNzc5MUUKnP05qgyr7IrY8qWOl/OrkaanS+zGBlWA
+tEJ8oBk9numWkpwHRCQj102ixXoFuEJkbjPHMQK98uCn6sVLrc8yA==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzTVBsWDFITXlsc3k1Z3B6
ZHlZK0ZNaWhkTTJwcVMxY0NzdUNRdFdjSVVZCnVCdVMweW1yY2NEaks0M1lNNXMr
Z1pYUDBCcnpsWVYxMzI5SlRvSXNTVzQKLS0tIHJpZUVsOVpqVXVmL251eE9qV01H
THpLaGs0dUdKTi9zYlZ4aVhDM0pSV2MKntzANQ+MBmF0534dVqHceJtQPXEjFQMk
bpO0CS4dNU9F0KRYpGrYCf1n6/+9W4dw3ZX/qLryzqNZD0cslVZm3Q==
-----END AGE ENCRYPTED FILE-----
recipient: age1xx4dzs0dxlwvkv9sjuqzsphl7lfrxannkfken374yu2qvvcte9sqzktqt2
lastmodified: "2026-07-24T20:06:42Z"
mac: ENC[AES256_GCM,data:ne1vtQ3sTWEYGNme6cn84IKKJkXFkJKz+QEn3pi4vjwQYFa1hcdOSE/Sv42Tjx5FiQ+8tFbbwtuGrsVxcydqgxCqt5LiIMJiO4jdGVEaZsKYgshzzSGE1qn7fhPaz5welxiq5pNCddsDUmzVBBuKx8ddi4DCJk1JNSvIufMNvRU=,iv:sTSvy+IxfZHV6s4yZx8BZnVfPhaUR0pZIZrnMTjTvGw=,tag:4Xbf5pPDop2y5GcOaKfHNA==,type:str]
lastmodified: "2026-07-25T00:38:24Z"
mac: ENC[AES256_GCM,data:8eaV8ZKuJVwAic7MIpKd2dHXm4FYcqo9WsBOaolaJ9tBo9OOZhHG8VeYraeMGZ/KqTp3PMgek5aSN0zLGyY1yaqhj4ygocYLpfOHamkodcyvQvno2rOpe9VVQ/wg9GelpikYIA4zkK+hnZ5Xa7HidxNsBd7rfCMWFXOdjOsNzoM=,iv:+6VLa3B/H53+ilrELuTwHsXIxzpytyBMDaYNsK1ZmVQ=,tag:h2969P1BODB1Z1UXEoJUhw==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.2