refactor: drop redundant env-naming prefixes (target, app, and shared APPS_) - #130
Merged
Conversation
…ble names Two prefixes were doing no real work here: the target-name prefix (RUBYKATZEN_COM_) only matters once a second target shares this repo's GitHub Secrets/Variables namespace, which isn't planned; the app-name prefix (TRAEFIK_) is redundant given vaults are already one-per-app. Drop both, and fix two names that had drifted from their app-side counterpart (CLOUDFLARE_TOKEN -> CLOUDFLARE_DNS_API_TOKEN matching APPS_CLOUDFLARE_DNS_API_TOKEN, CERT_RESOLVER -> CERTIFICATE_RESOLVER matching APPS_CERTIFICATE_RESOLVER). GitHub Secret/Variable name is now identical to the app-side var name it feeds, minus the APPS_ prefix where present. The actual GitHub Secrets/Variables still need updating to match - tracked separately since that's a live-state change outside git.
APPS_{VAR} was flightdeck's convention for "server-wide variable
shared across apps" (vs. {APPNAME}_{VAR} for a per-app override), but
the prefix wasn't distinguishing anything a bare name doesn't already
convey by not having an app prefix - it's dead weight the same way
the vault-level RUBYKATZEN_COM_/TRAEFIK_ prefixes were. Drop it across
the whole catalog: common.yml, every app's docker-compose.yml, the
shared database templates (postgres/mysql/mongodb/etc.), and the two
rendered *.tpl config files (traefik, codecov) - all now reference
plain names (DOMAIN, DATABASE_PASSWORD, KEY_HEX_32, ...). Per-app
override vars follow along automatically (CODECOV_APPS_DOMAIN ->
CODECOV_DOMAIN, TWOFAUTH_APPS_DOMAIN -> TWOFAUTH_DOMAIN), since they
were always just {APPNAME}_ glued onto the shared name.
One deliberate exception: AGENTS.md's UID/GID example now uses
PUID/PGID (the linuxserver.io convention), not bare UID/GID - unlike
every other bare name here, those two are actual shell-reserved
variable names, so dropping their prefix would trade a redundant
prefix for a real collision risk instead of removing dead weight.
Updated AGENTS.md/README.md's naming-convention writeup, the
encrypt-env action's example, and every test fixture that illustrated
the old convention. Verified: full deploy/ test suite (48 tests),
pre-commit, and `docker compose config` for traefik and rybbit.
Unlike the other shared vars (ADMIN_MAIL, CLOUDFLARE_DNS_API_TOKEN, DOMAIN, CERTIFICATE_RESOLVER, DATABASE_PASSWORD, KEY_HEX_32- each an inherently target-wide, singular concept), HTTP_PORT/HTTPS_PORT are specific to traefik being the one app that binds host ports directly. A bare HTTP_PORT/HTTPS_PORT GitHub Variable name doesn't self-document which app it belongs to the way the others do. App-side var names in traefik's docker-compose.yml are unaffected - still plain HTTP_PORT/ HTTPS_PORT; only the GitHub Variable name (the vault mapping's right side) gets TRAEFIK_ back.
${APPNAME_VAR:-${VAR}} let an app override a shared value while
falling back to it - a genuine capability, not just naming cruft, but
one nothing in the catalog or in flightdeck's own deploy state ever
actually used (grep across every vault: zero). Simplify all four
apps that carried it (gatus, codecov, semaphore, twofauth) to
reference the shared variable directly. The app-specific-only pattern
({APPNAME}_{VAR} with no fallback, e.g. BESZEL_AGENT_PUBLIC_KEY) is
real and stays - only the fallback/override half is gone.
Downstream consumers of the apps catalog lose the override option;
if one ever needs it, add an app-specific variable name instead of
reaching for a fallback nobody exercises today.
x-environment mixed vault-sourced inputs (DOMAIN, KEY_HEX_32, DATABASE_PASSWORD, the optional DISABLE_*/MAPBOX_TOKEN) with fixed or APP_NAME-derived internal wiring (postgres/clickhouse hostnames, ports, per-app db/user names) in one flat list - no way to tell which env vars this app actually needs from its vault without reading every line. Split into x-vault-env/x-internal-env, merged at the point of use via multi-anchor <<: [*a, *b] (verified docker compose supports this). Verified the resolved config is byte-identical to before this split, aside from the split x- blocks themselves. Trying this on rybbit first before deciding whether to make it a catalog-wide convention.
x-vault-env/x-internal-env names are self-explanatory.
Applies the same split trialed on rybbit to every other app that
mixes vault-sourced values with fixed/internal wiring: beszel-agent,
codecov, gatus, glitchtip, semaphore, twofauth. traefik and homepage
have only vault-sourced vars, so their single anchor is renamed to
x-vault-env with no internal-env counterpart, per the "only split
when there's actually a mix" rule.
Documents the convention in AGENTS.md: when to split vs. use a single
appropriately-named anchor, the classification rule (a var is
vault-env if its value references any vault-sourced variable, even
mixed with ${APP_NAME} or literal text), and updates the Field
Ordering Rules' x-fields order and "environment via anchor" principle
to match.
Verified every changed app's resolved services: config against an
isolated empty .env (no real .env files exist in any app directory
anymore - see prior cleanup) - correct var counts and no unexpected
keys in every case.
The compose file already overrides the container's healthcheck explicitly (test: ["CMD", "/agent", "health"]) - Docker never consults the image's built-in HEALTHCHECK when one is set at the compose level, so this env var (presumably meant to feed that built-in check) was never actually read. Only app with this var.
BESZEL_AGENT_DISK_1_DEVICE/_2_DEVICE are vault-configurable inputs
(same {APPNAME}_{VAR} pattern as any other app-specific vault var),
but they lived directly in devices:, invisible to the x-vault-env
convention meant to make an app's vault interface visible at a
glance. A YAML anchor isn't limited to holding a map - split it into
x-vault-devices: &vault-devices (a list) and reference it directly
with devices: *vault-devices.
Verified duplicate /dev/null:/dev/null device entries (the default
when neither var is set) don't break container startup - tested with
both `docker run --device` and `docker compose up` directly; compose
config even dedupes identical entries on its own.
Documented the general pattern in AGENTS.md: x-vault-{field} for any
non-environment compose field with vault-sourced values, not just
x-vault-env.
beszel-agent had x-internal-env sandwiched between x-vault-env and x-vault-devices. Vault-sourced anchors should stay grouped together regardless of which compose field they target, so the vault interface reads as one contiguous block at the top of the file. Documented in AGENTS.md's Field Ordering Rules.
Every compose/*.tpl file is already scoped to exactly one app, so a
BESZEL_AGENT_/CODECOV_/GLITCHTIP_-style prefix inside it disambiguates
nothing - it only ever mattered at the vault-to-GitHub-Secret mapping
layer, where a flat namespace across all of a target's apps genuinely
can collide (kept e.g. TRAEFIK_HTTP_PORT there for exactly that
reason). Renamed every app-name-prefixed variable reference to its
bare form: beszel-agent's PUBLIC_KEY/DISK_1_DEVICE/DISK_2_DEVICE,
codecov.yml.tpl's LICENSE/ADMIN_GITHUB_USERNAME/GITHUB_CLIENT_ID/
GITHUB_CLIENT_SECRET/GITHUB_WEBHOOK_SECRET/GITHUB_APP_ID/S3_BUCKET,
glitchtip's EMAIL_URL/DEFAULT_FROM_EMAIL.
Rewrote AGENTS.md's "App-specific vs. shared variables" and README's
matching paragraph: what used to be framed as a compose-file-level
{APPNAME}_{VAR} vs {VAR} naming rule is now correctly framed as a
vault-manifest-level judgment call (whether a GitHub Secret/Variable
name needs its app spelled out to be legible in a flat per-target
list) - the compose file itself never needs to know or care.
Verified: full deploy/ test suite (48 tests), pre-commit, and
`docker compose config` for all three changed apps against an
isolated empty .env.
Grouping keys by byte-length rather than role had a real consequence: twofauth and semaphore both drew from KEY_HEX_16, meaning two apps with completely unrelated encryption needs would silently share the same secret value if deployed together. Split by actual risk profile instead: - Session-signing keys (rybbit's BETTER_AUTH_SECRET, glitchtip's Django SECRET_KEY, codecov's cookie_secret) are safe to share - compromising one just forces re-authentication everywhere. One shared name and vault mapping: SESSION_KEY. - Encryption keys (twofauth's Laravel APP_KEY, semaphore's stored- credential encryption) must never be shared - compromising or rotating one can make already-stored ciphertext unrecoverable in a way that isn't reversible by re-login. Same semantic app-side name (ENCRYPTION_KEY) so the role is still obvious from the compose file, but each app's vault maps it to its own distinct, app- prefixed GitHub Secret (TWOFAUTH_ENCRYPTION_KEY, SEMAPHORE_ENCRYPTION_KEY) once either is actually deployed. Updates vaults/hawkeye-rybbit.yml (KEY_HEX_32 -> SESSION_KEY) since rybbit is the only one of these actually live. Documents the by-purpose-not-format naming principle in AGENTS.md. The live GH secret still needs the matching rename (KEY_HEX_32 -> SESSION_KEY) - that's outside git, done separately.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four rounds of naming/structure cleanup, all converging on the same principle: nothing here should exist "just in case."
hawkeye vault GitHub Secret/Variable names:
RUBYKATZEN_COM_target prefix - no second target shares this repo's Secrets/Variables namespace, and none is planned.TRAEFIK_app prefix fromADMIN_MAIL/CLOUDFLARE_DNS_API_TOKEN/etc. - redundant given vaults are already one-per-app.TRAEFIK_prefix specifically on the two port GitHub Variables (TRAEFIK_HTTP_PORT/TRAEFIK_HTTPS_PORT) - a bareHTTP_PORTdoesn't self-document which app it belongs to, since traefik is the one app binding host ports directly.CLOUDFLARE_TOKEN->CLOUDFLARE_DNS_API_TOKEN,CERT_RESOLVER->CERTIFICATE_RESOLVER.Catalog-wide
APPS_prefix:APPS_{VAR}was the convention for "shared across apps" - dropped it everywhere:common.yml, every app'sdocker-compose.yml, the shared database templates, and the two rendered*.tplconfig files.PUID/PGIDinstead of bareUID/GIDin a doc example - those two are actual shell-reserved variable names.Unused per-app override fallback:
${APPNAME_VAR:-${VAR}}let an app override a shared value while falling back to it - a real capability, but nothing in the catalog or in any vault ever used it (verified: zero matches). Simplified the four apps that carried it (gatus, codecov, semaphore, twofauth) to reference the shared variable directly.Vault-sourced vs. internal environment split:
x-environmentmixed values that must come from its vault with fixed/internal wiring, with no way to tell which was which at a glance. Split intox-vault-env/x-internal-envanchors, merged at point of use via<<: [*vault-env, *internal-env]. Trialed on rybbit first, confirmed, then rolled out to every app with a real mix (beszel-agent, codecov, gatus, glitchtip, semaphore, twofauth); traefik/homepage use a singlex-vault-envsince they have no internal-only vars. Documented as the required convention in AGENTS.md.Updated AGENTS.md/README.md's naming-convention writeup, the
encrypt-envaction's example, and every test fixture that illustrated the old conventions throughout.Not included here
The actual GitHub repo Secrets/Variables have already been updated to match live (done separately, outside git) - confirmed synced.
Test plan
deploy/unit test suite (48 tests) passespre-commit run --all-filespasses (yamllint, pymarkdown, ruff, actionlint)docker compose configvalidated for every changed app against an isolated empty.env- correct variable counts, correct merge, no unexpected keys in any case