Context
docker-compose.yml's own header comment describes .env.example as "the exhaustive reference" an
operator should copy to .env before running the stack. Cross-referencing every ${VAR} /
${VAR:-default} interpolation actually used in docker-compose.yml against every variable name
.env.example documents (live NAME= lines and commented # NAME= reference lines alike) turns up
about 15 real, currently-undocumented gaps. These are not Docker-secrets-file vars (those are
intentionally covered separately in secrets/README.md, e.g. GITHUB_APP_PRIVATE_KEY_FILE) — they are
plain .env values with no documentation anywhere in the sample file:
- Backup profile (
--profile backup), entirely undocumented in .env.example:
BACKUP_RETAIN (retention count, default 7), BACKUP_INTERVAL_SECONDS (default 86400),
VERIFY_RESTORE_SCRATCH + LOOPOVER_VERIFY_SCRATCH_DATABASE_URL (the opt-in restore-drill pair used by
scripts/verify-backup.sh). None of the four appear in .env.example, even though the backup and
backup-exporter services (docker-compose.yml ~lines 1103-1160) read all of them.
- AMS reporting-exporter path overrides:
.env.example documents
LOOPOVER_MINER_CONFIG_DIR and LOOPOVER_AMS_REPORTING_EXPORT_INTERVAL_SECONDS, but not the four
source/reporting DB path overrides the same ams-reporting-exporter service reads:
LOOPOVER_AMS_ATTEMPT_LOG_SOURCE_DB, LOOPOVER_AMS_PREDICTION_LEDGER_SOURCE_DB,
LOOPOVER_AMS_ATTEMPT_LOG_REPORTING_DB, LOOPOVER_AMS_PREDICTION_LEDGER_REPORTING_DB.
- Core reporting-exporter:
LOOPOVER_REPORTING_SOURCE_DB is documented, but the sibling
LOOPOVER_REPORTING_DB (output path override) is not.
LOOPOVER_REPO_CONFIG_DIR — the per-repo .loopover.yml config-directory override used directly by
the always-on loopover service (docker-compose.yml, LOOPOVER_REPO_CONFIG_DIR env entry) — not
mentioned anywhere in .env.example.
- OTEL metrics vars:
OTEL_TRACES_EXPORTER and OTEL_EXPORTER_OTLP_ENDPOINT are documented, but the
metrics-side siblings actually read by the loopover service are not: OTEL_METRICS_EXPORTER,
OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE.
PROMETHEUS_RETENTION_TIME — overrides Prometheus's --storage.tsdb.retention.time (default 90d).
docker-compose.yml carries an unusually long comment explaining why 90d was chosen over the prior
180d default, which makes the total absence of this override from .env.example more notable, not less.
REES_MEM_LIMIT — the one memory-limit variable missing from .env.example's otherwise-exhaustive
*_MEM_LIMIT block (lines ~347-364, which lists LOOPOVER_MEM_LIMIT through REDIS_EXPORTER_MEM_LIMIT
but skips REES_MEM_LIMIT, used by the rees service).
INSTALL_VISUAL_REVIEW — the build arg controlling whether puppeteer-core is installed for local
builds (referenced in the loopover service's build.args and explained in the browserless service
comment), not mentioned in .env.example.
Requirements
- Add each variable above to
.env.example, matching the existing convention already used throughout the
file: a commented # NAME=default # one-line explanation, --profile X, and/or a #issue reference
line, grouped under the section it belongs to (backup vars near any existing backup mention or as a new
"Backups" section; OTEL metrics vars alongside the existing OTEL_TRACES_EXPORTER block; etc.).
- Do not invent new defaults or behavior — every value/default documented must match what
docker-compose.yml already uses (e.g. BACKUP_RETAIN defaults to 7, PROMETHEUS_RETENTION_TIME
defaults to 90d).
Deliverables
Test Coverage Requirements
.env.example itself is a config file outside coverage.include (not under src/**), so the Codecov
patch gate does not apply to the documentation edit. The new drift-guard test under test/unit/** IS
measured and must hit 99%+ patch coverage, including both the pass case (current file, no gaps) and a
fail case (a deliberately-stripped fixture missing one variable) to prove the check actually catches
drift, not just that it passes today.
Expected Outcome
.env.example is a genuinely exhaustive reference for every docker-compose.yml variable, including the
backup, AMS-observability-exporter, OTEL-metrics, and Prometheus-retention knobs that are silently
undocumented today. A future PR that reads a new env var from docker-compose.yml without adding it to
.env.example fails CI instead of shipping another silent gap.
Links & Resources
docker-compose.yml (backup/backup-exporter services, ams-reporting-exporter service,
reporting-exporter service, prometheus service, rees service, loopover service build.args)
.env.example (existing *_MEM_LIMIT block ~lines 347-364, OTEL block ~lines 502-541)
scripts/backup-metrics.sh, scripts/verify-backup.sh, scripts/export-ams-reporting-db.sh
- Related prior art:
npm run selfhost:env-reference (the existing env-var drift-guard command referenced
in this repo's own CLAUDE.md/AGENTS.md contributor guide)
Context
docker-compose.yml's own header comment describes.env.exampleas "the exhaustive reference" anoperator should copy to
.envbefore running the stack. Cross-referencing every${VAR}/${VAR:-default}interpolation actually used indocker-compose.ymlagainst every variable name.env.exampledocuments (liveNAME=lines and commented# NAME=reference lines alike) turns upabout 15 real, currently-undocumented gaps. These are not Docker-secrets-file vars (those are
intentionally covered separately in
secrets/README.md, e.g.GITHUB_APP_PRIVATE_KEY_FILE) — they areplain
.envvalues with no documentation anywhere in the sample file:--profile backup), entirely undocumented in.env.example:BACKUP_RETAIN(retention count, default 7),BACKUP_INTERVAL_SECONDS(default 86400),VERIFY_RESTORE_SCRATCH+LOOPOVER_VERIFY_SCRATCH_DATABASE_URL(the opt-in restore-drill pair used byscripts/verify-backup.sh). None of the four appear in.env.example, even though thebackupandbackup-exporterservices (docker-compose.yml~lines 1103-1160) read all of them..env.exampledocumentsLOOPOVER_MINER_CONFIG_DIRandLOOPOVER_AMS_REPORTING_EXPORT_INTERVAL_SECONDS, but not the foursource/reporting DB path overrides the same
ams-reporting-exporterservice reads:LOOPOVER_AMS_ATTEMPT_LOG_SOURCE_DB,LOOPOVER_AMS_PREDICTION_LEDGER_SOURCE_DB,LOOPOVER_AMS_ATTEMPT_LOG_REPORTING_DB,LOOPOVER_AMS_PREDICTION_LEDGER_REPORTING_DB.LOOPOVER_REPORTING_SOURCE_DBis documented, but the siblingLOOPOVER_REPORTING_DB(output path override) is not.LOOPOVER_REPO_CONFIG_DIR— the per-repo.loopover.ymlconfig-directory override used directly bythe always-on
loopoverservice (docker-compose.yml,LOOPOVER_REPO_CONFIG_DIRenv entry) — notmentioned anywhere in
.env.example.OTEL_TRACES_EXPORTERandOTEL_EXPORTER_OTLP_ENDPOINTare documented, but themetrics-side siblings actually read by the
loopoverservice are not:OTEL_METRICS_EXPORTER,OTEL_EXPORTER_OTLP_PROTOCOL,OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE.PROMETHEUS_RETENTION_TIME— overrides Prometheus's--storage.tsdb.retention.time(default90d).docker-compose.ymlcarries an unusually long comment explaining why 90d was chosen over the prior180d default, which makes the total absence of this override from
.env.examplemore notable, not less.REES_MEM_LIMIT— the one memory-limit variable missing from.env.example's otherwise-exhaustive*_MEM_LIMITblock (lines ~347-364, which listsLOOPOVER_MEM_LIMITthroughREDIS_EXPORTER_MEM_LIMITbut skips
REES_MEM_LIMIT, used by thereesservice).INSTALL_VISUAL_REVIEW— the build arg controlling whether puppeteer-core is installed for localbuilds (referenced in the
loopoverservice'sbuild.argsand explained in thebrowserlessservicecomment), not mentioned in
.env.example.Requirements
.env.example, matching the existing convention already used throughout thefile: a commented
# NAME=default # one-line explanation, --profile X, and/or a #issue referenceline, grouped under the section it belongs to (backup vars near any existing backup mention or as a new
"Backups" section; OTEL metrics vars alongside the existing
OTEL_TRACES_EXPORTERblock; etc.).docker-compose.ymlalready uses (e.g.BACKUP_RETAINdefaults to7,PROMETHEUS_RETENTION_TIMEdefaults to
90d).Deliverables
.env.exampledocuments all ~15 variables listed above, in the file's existing comment style andsection grouping.
npm run selfhost:env-reference, which scansprocess.env.*reads undersrc/selfhost/**and does not look atdocker-compose.ymlor.env.exampleat all — a new, purpose-built check is needed here, e.g.test/unit/docker-compose-env-example-parity.test.ts) that parses every${VAR}/${VAR:-default}interpolation indocker-compose.yml, parses every live/commentedNAME=line in.env.exampleandsecrets/README.md's table, and asserts every compose variable is documented inone of the two, so this class of gap cannot silently reopen.
Test Coverage Requirements
.env.exampleitself is a config file outsidecoverage.include(not undersrc/**), so the Codecovpatch gate does not apply to the documentation edit. The new drift-guard test under
test/unit/**ISmeasured and must hit 99%+ patch coverage, including both the pass case (current file, no gaps) and a
fail case (a deliberately-stripped fixture missing one variable) to prove the check actually catches
drift, not just that it passes today.
Expected Outcome
.env.exampleis a genuinely exhaustive reference for everydocker-compose.ymlvariable, including thebackup, AMS-observability-exporter, OTEL-metrics, and Prometheus-retention knobs that are silently
undocumented today. A future PR that reads a new env var from
docker-compose.ymlwithout adding it to.env.examplefails CI instead of shipping another silent gap.Links & Resources
docker-compose.yml(backup/backup-exporter services, ams-reporting-exporter service,reporting-exporter service, prometheus service, rees service, loopover service
build.args).env.example(existing*_MEM_LIMITblock ~lines 347-364, OTEL block ~lines 502-541)scripts/backup-metrics.sh,scripts/verify-backup.sh,scripts/export-ams-reporting-db.shnpm run selfhost:env-reference(the existing env-var drift-guard command referencedin this repo's own CLAUDE.md/AGENTS.md contributor guide)