# Environment variables Every environment variable the repo's code reads, by who it is for. The list is code (`common/lib/envVars.ts`), and a test fails when the code reads a variable the list does not declare, or the list declares one that nothing outside the list names any more. umtool's own knobs are documented in [umtool/docs](umtool/docs/README.md). Regenerate this file with `pnpm archilyzer docs env`. `pnpm archilyzer doctor` prints which of the paths overrides are set on this machine. ## Paths and binaries The one override surface for where things live and which binary runs. Every one is read by `getPaths()` (`common/lib/paths.ts`) and nowhere else; nothing hardcodes a location. | Variable | Default | What it does | Read by | |---|---|---|---| | `TRANSCRIPTS_DIR` | `/transcripts` | The corpus: channels, sites, the LMDB index, job logs, the saved-video store. umtool reads `/channels` too, when its own `CHANNELS_DIR` is unset. | common/lib/paths.ts (getPaths) | | `SAVED_VIDEOS_DIR` | `/saved-videos` | The persisted source-video store, when it should live on another disk. | common/lib/paths.ts (getPaths) | | `SITES_DIR` | `/sites` | Per-site config (`/site.json`, every key in [SITE.md](SITE.md)) and the homepage's `_homepage/`. | common/lib/paths.ts (getPaths) | | `SETTINGS_FILE` | `/settings.json` | The settings file (every key in [SETTINGS.md](SETTINGS.md)). | common/lib/paths.ts (getPaths) | | `EXPORT_PUBLIC_DIR` | `/export/public` | The dir the export site serves at `/`, composed one site at a time. | common/lib/paths.ts (getPaths) | | `EXPORT_INDEX_DIR` | `.export-index` beside `EXPORT_PUBLIC_DIR` | The build's staging area (not served): the shared index and per-site aggregates. | common/lib/paths.ts (getPaths) | | `EXPORT_BUILDS_DIR` | `.export-builds` beside `EXPORT_PUBLIC_DIR` | Per-site `out/` bundles from a docker-mode build. | common/lib/paths.ts (getPaths) | | `EDITOR_CHANGELOG_FILE` | `/editor/CHANGELOG.md` | The editor changelog the release cutter reads and rewrites. The e2e server points it at a gitignored copy. | common/lib/paths.ts (getPaths) | | `EXPORT_CHANGELOG_FILE` | `/export/CHANGELOG.md` | The export changelog, likewise. | common/lib/paths.ts (getPaths) | | `CHARTS_CONFIG_FILE` | `/chart-templates.json` | The legacy chart-templates file, read only by a migration. | common/lib/paths.ts (getPaths) | | `SEARCH_ALIASES_FILE` | `/search-aliases.json` | The corpus-wide search-alias dictionary. | common/lib/paths.ts (getPaths) | | `CURATED_TAGS_FILE` | `/tags.json` | Curated per-video tags. Written only through `applyTagAssignments`. | common/lib/paths.ts (getPaths) | | `YTDLP_BIN` | `yt-dlp` on PATH | The downloader. Every fetch goes through it. | common/lib/paths.ts (getPaths) | | `FFMPEG_BIN` | `ffmpeg` on PATH | Audio extraction for transcription and diarization. | common/lib/paths.ts (getPaths) | | `FFPROBE_BIN` | `ffprobe` on PATH | Duration checks (the short-audio guard, windowing). | common/lib/paths.ts (getPaths) | | `WHISPER_BIN` | `whisper-cli` on PATH | whisper.cpp, one of the three transcription engines (with chough and parakeet.cpp). | common/lib/paths.ts (getPaths) | | `WHISPER_MODEL` | `~/whispercpp/whisper.cpp/models/ggml-base.en.bin` | whisper.cpp's model, when a worker names none. | common/lib/paths.ts (getPaths) | | `PARAKEET_STITCH_BIN` | `/scripts/parakeet-stitch.mjs` | The parakeet.cpp engine's wrapper (overlapping windows, stitched). | common/lib/paths.ts (getPaths) | | `PARAKEET_CLI` | `parakeet-cli` on PATH | The parakeet.cpp binary the wrapper drives (the wrapper reads it too). | common/lib/paths.ts (getPaths) | | `PARAKEET_MODEL` | none | parakeet.cpp's `.gguf`, when a worker names none (the wrapper reads it too). | common/lib/paths.ts (getPaths) | | `DIARIZE_BIN` | `/scripts/diarize.mjs` | The speaker-diarization wrapper. The e2e suite swaps in a fake here. | common/lib/paths.ts (getPaths) | | `RSYNC_BIN` | `rsync` on PATH | Mirrors the saved-video store to a backup destination. | common/lib/paths.ts (getPaths) | | `FINDMNT_BIN` | `findmnt` on PATH | The read-only volume-identity probe behind storage locations. Optional. | common/lib/paths.ts (getPaths) | | `UDISKSCTL_BIN` | `udisksctl` on PATH | Mounts an attached volume from `/storage`. Optional. | common/lib/paths.ts (getPaths) | | `GALLERY_DL_BIN` | `gallery-dl` on PATH | The X/Twitter post fetcher, for social channels. | common/lib/paths.ts (getPaths) | | `ARIA2C_BIN` | `aria2c` on PATH | Fetches an archive.org file over BitTorrent (the item's torrent, archive.org as web seed), then seeds it for a while. Optional: without it archive.org files are downloaded directly. See `archiveOrg` in [SETTINGS.md](SETTINGS.md). | common/lib/paths.ts (getPaths) | | `OLLAMA_URL` | `http://127.0.0.1:11434` | The local ollama server, the local digest and attribution engine. | common/lib/paths.ts (getPaths) | | `CLAUDE_BIN` | `claude` on PATH | The `claude` CLI, driving the opt-in metered digest lane. | common/lib/paths.ts (getPaths) | | `ARCHILYZER_CONFIG_DIR` | `~/.config/archilyzer` | The operator's private config dir, outside the repo: the two inputs of `archilyzer source publish` below. Never committed. In Docker it is `/data/config/archilyzer`, in the config volume (docker-compose.yml). | common/lib/paths.ts (getPaths) | | `SOURCE_SCRUB_FILE` | `/source-scrub.txt` | git-filter-repo `lhs==>rhs` rules applied to file contents AND commit messages when the source mirror is generated (`==>/home/user` is built in and runs first). Every rule's left side is also denied. See [PUBLISH.md](PUBLISH.md). | common/lib/paths.ts (getPaths) | | `SOURCE_DENYLIST_FILE` | `/source-denylist.txt` | Literals the published source must never contain, one per line (`i:` = any case). One hit anywhere in the mirror, the tree or the tarball refuses the publish. | common/lib/paths.ts (getPaths) | | `ARCHILYZER_SOURCE_SCRATCH` | the OS temp dir | Where `source publish` makes its scratch clone and stage (removed afterwards unless `--keep-scratch`). | common/lib/paths.ts (getPaths) | | `XDG_CACHE_HOME` | `~/.cache` | The cache root: `source publish` keeps the history pages' render cache in `/archilyzer/source-history/` (about 140 MB; never inside the checkout). | common/lib/paths.ts (getPaths) | | `STAGIT_BIN` | `stagit` on PATH, then `~/.local/bin/stagit` | stagit, which renders the source's history pages (`/source/git/`: the log and a page per commit with its diff). Optional: without it the source is published without them. See [PUBLISH.md](PUBLISH.md). | common/lib/paths.ts (getPaths) | ## Runtime Tokens, credentials and knobs a running process reads. Most configuration is not here but in `settings.json` ([SETTINGS.md](SETTINGS.md)). | Variable | Default | What it does | Read by | |---|---|---|---| | `WORKER_TOKEN` | unset (both surfaces off) | Bearer token for the remote-worker API and for `/api/ops/*` (`pnpm ops`; the MCP's `fetch_clip`, `enqueue`, `get_job`, `channel_coverage` and `get_transcript`'s editor fallback). Set the same value on both ends. | common/lib/workerToken.ts, scripts/archilyzer-ops.mjs, mcp/src/fetchClip.ts, mcp/src/editorOps.ts | | `SYNC_HEARTBEAT_SECONDS` | `settings.syncScheduler.heartbeatSeconds` | Overrides the editor's in-process sync heartbeat. `0` = no internal timer (tick from cron instead). | editor/app/scheduler/heartbeat.ts | | `SYNC_TICK_URL` | `http://127.0.0.1:3001/api/scheduler/tick` | Where `archilyzer sync tick` (cron's heartbeat) posts. | common/bin/sync-tick.ts | | `SYNC_TICK_TOKEN` | unset (no auth) | Bearer token for the tick endpoint; set on both the editor and the cron job. | common/bin/sync-tick.ts, editor/app/scheduler/auth.ts | | `R2_ACCESS_KEY_ID` | — | R2 S3 credentials for uploading oversize archives at deploy time (with `R2_SECRET_ACCESS_KEY` and `CLOUDFLARE_ACCOUNT_ID`), needed only when `archiveStorage.bucket` is set. In Docker they come from `.env`. See [PUBLISH.md](PUBLISH.md). | common/publish/build.ts, common/bin/doctor.ts (set or not) | | `R2_SECRET_ACCESS_KEY` | — | See `R2_ACCESS_KEY_ID`. | common/publish/build.ts, common/bin/doctor.ts (set or not) | | `CLOUDFLARE_ACCOUNT_ID` | — | The Cloudflare account: the R2 endpoint's, and the one wrangler deploys to when the token can see more than one. In Docker it comes from `.env`. | common/publish/build.ts, wrangler, common/bin/doctor.ts (set or not) | | `CLOUDFLARE_API_TOKEN` | unset (wrangler's own `wrangler login` config, on a host) | The API token every deploy's wrangler authenticates with (Cloudflare Pages: Edit). The way a container deploys — there is no browser for `wrangler login` in one; set it in `.env`. | wrangler (every deploy), common/bin/doctor.ts, common/lib/pagesDeploy.ts (set or not, never the value) | | `ARCHILYZER_HOST_ID` | the hostname | Which host the publish lock (`/.publish.lock`) names as its holder's: a lock from this host whose pid is dead is stale and taken over; another host's is waited on. docker-compose.yml fixes it for the editor (`archilyzer-editor`), whose hostname is a container id that changes on every recreate. | common/publish/stageLock.ts (the publish lock) | | `ARCHILYZER_SOURCE_REPO` | this checkout's git common dir | The git DIR `archilyzer source publish` mirrors `main` from, when the checkout has none: in Docker, `/data/source.git`, the host's git common dir mounted read-only by docker-compose.source.yml. A value that names nothing refuses the publish. | common/publish/source.ts, common/bin/doctor.ts, docker/entrypoint.sh | | `YTDLP_SOURCE_HOST_DIR` | — (required by the overlay) | Docker: the HOST path of a yt-dlp source checkout (the directory holding `yt_dlp/`), mounted read-only at `/opt/yt-dlp-src` by docker-compose.ytdlp.yml. See [RUNNING_IN_DOCKER.md](RUNNING_IN_DOCKER.md), "Substituting yt-dlp". | docker-compose.ytdlp.yml | | `YTDLP_AUTO_UPDATE` | off | Docker: `1` runs `yt-dlp -U` on every editor boot — on the image's yt-dlp only; an override (`YTDLP_BIN` naming another) is left alone, with a warning. | docker/entrypoint.sh, common/bin/doctor.ts | | `XDG_CONFIG_HOME` | `~/.config` | Where `wrangler login` keeps its config (`/.wrangler/config/default.toml`); the doctor looks for it there, by path, never reading it. | common/bin/doctor.ts, common/lib/pagesDeploy.ts | | `YTDLP_SOURCE_DIR` | `/opt/yt-dlp-src` | Docker: where `/usr/local/bin/yt-dlp-from-source` finds the yt-dlp source tree it runs with the image's python. | docker/yt-dlp-from-source.sh | | `DOCKER_BIN` | `docker` | The container engine for docker-mode builds (e.g. `podman`). | common/publish/build.ts | | `DOCKER_BUILD_MEMORY` | no cap | Per-container memory cap for a docker-mode build (`--memory`). | common/publish/build.ts | | `DOCKER_BUILD_CPUS` | no cap | Per-container CPU cap for a docker-mode build (`--cpus`). | common/publish/build.ts | | `ARCHIVE_CHANNEL_CONCURRENCY` | `4` | How many channels' archive zips `build archives` builds at once. | common/bin/build-archives.ts | | `HOST` | every interface | The address `pnpm start:export` (serve-out) listens on; `127.0.0.1` keeps a private site on this machine. | export/scripts/serve-out.mjs | | `MAX_ARCHIVE_BYTES` | the Cloudflare-safe cap | The served-file size cap for archives, in bytes; `0` = no cap. A site's own `archiveMaxBytes` wins. | common/bin/compose-site.ts | | `EXPORT_NEXT_BIN` | unset: `pnpm exec next build` in export/ | The `next` a site's or the hub's build runs as ` build` in export/, in place of `pnpm exec next build`. The editor's e2e suite points it at its fake, which copies the composed public dir to export/out. | common/publish/build.ts (nextBuildStep) | | `WRANGLER_BIN` | `common/node_modules/.bin/wrangler` (the pinned devDependency) | The wrangler a deploy spawns. The editor's e2e suite points it at its fake. | common/lib/pagesDeploy.ts (wranglerBin), common/publish/deployStage.ts, common/bin/doctor.ts | | `CHOUGH_BIN` | `chough` on PATH | The chough transcription engine, when a worker names no binary. | common/lib/transcriptionApps.ts | | `CHOUGH_MODEL` | chough's own | Passed to chough from a worker's model field; chough auto-downloads one when unset. | chough (set by common/lib/transcriptionApps.ts) | | `CHOUGH_URL` | local | Passed to chough from a worker's remote-server field. | chough (set by common/lib/transcriptionApps.ts) | | `OLLAMA_DIGEST_MODEL` | `qwen2.5:7b` | The ollama model the local digest lane asks for when settings name none. | common/lib/digestApps.ts | | `CLAUDE_DIGEST_MODEL` | the CLI's default | The model the metered digest lane asks `claude` for when settings name none. | common/lib/digestApps.ts | | `NITTER_INSTANCES` | a built-in list | Comma-separated Nitter instances for the X fallback fetcher, in order of preference. | common/social/xNitterFetcher.ts | | `ARCHILYZER_X_BROWSER` | the first of `chromium`, `google-chrome`, `google-chrome-stable`, `chrome` on PATH, else Playwright's bundled Chromium | The Chromium-family browser /settings' "Connect X account" opens (a path, or a name looked up on PATH), and a forum-thread channel's "Connect forum session" too. It is launched without the automation signals, in the X session profile (or the forum host's profile); a value that is not an executable refuses the connect rather than opening another browser. | common/social/xBrowser.ts | | `UMTOOL_URL` | unset (no link; the MCP's `notes` off) | umtool's front door; when set, the video page links to it, and the MCP's `notes` tool reads the operator's notes there (e.g. `http://localhost:3050`). | editor/app/channels/[slug]/videos/[id]/page.tsx, mcp/src/archivalTools.ts | | `TRANSCRIPT_SITE_URL` | — | MCP server: one published archive to read over HTTP. | mcp/src/sources.ts | | `TRANSCRIPT_HUB_URL` | — | MCP server: a hub, federating every archive it lists. | mcp/src/sources.ts | | `TRANSCRIPT_LOCAL_DIR` | — | MCP server: a composed public dir on disk. | mcp/src/sources.ts | | `TRANSCRIPT_PLATFORM_LINKS` | off | `1` cites platform watch pages instead of the archive's own pages. | common/lib/archive/reader-fs.ts | | `AUDIO_CHECK_RESUME_DURING_PROBE` | the channel's `audioCheck.resumeDuringProbe` | `1` or `true` resumes yt-dlp during the audio check's probe, anything else holds it, for a one-off comparison run; unset = the channel's setting. | common/ytdlp/audioCheckedDownload.ts | | `AUDIO_CHECK_BACKOFF_FACTOR` | the built-in factor | The audio check's interval backoff factor, in (0, 1], for a one-off run. | common/ytdlp/audioCheckedDownload.ts | | `ARCHILYZER_STATS_ALLOW_DOWNGRADE` | off | `1` lets a stats build clear a stats cache that a NEWER build wrote, for a deliberate rollback. Unset, such a build refuses and names both versions. | common/controller/buildStats.ts | | `ARCHILYZER_INDEX_ALLOW_HELD` | off | `1` lets a FULL index rebuild (a schema change, or no index yet) proceed while a channel's media cannot be read; that channel stays out of the index until its media is back and the index is built again. Unset, such a build refuses and names each channel. | common/controller/buildIndex.ts | | `UV_THREADPOOL_SIZE` | `16` for the editor (`4` is Node's own) | Threads in Node's pool for filesystem calls. A call on a stalled drive holds one until the drive answers, so the editor starts with 16. It buys time for calls already in flight and isolates nothing: the storage health probe and its gate keep new calls off a stalled drive. | Node's libuv (set by editor/package.json `start` and docker/entrypoint.sh) | | `MCP_IO_STATS` | off | `1` turns on per-call I/O accounting, for `mcp/bench`. | common/lib/archive/io-stats.ts | | `ARCHILYZER_EDITOR_URL` | `http://localhost:3001` | Which editor `pnpm ops` and the MCP's editor-backed tools (`fetch_clip`, `enqueue`, `get_job`, `channel_coverage`, `get_transcript`'s fallback) talk to, and whose `/api/pulse` `archilyzer storage migrate-tier` asks before it refuses to run beside it. | scripts/archilyzer-ops.mjs, mcp/src/fetchClip.ts, mcp/src/editorOps.ts, umtool, common/bin/migrate-media-tier.ts | | `ARCHILYZER_AGENT` | `cli` | Who is asking, recorded as the provenance of a curated-tag write through `pnpm ops`. | scripts/archilyzer-ops.mjs | | `DIARIZE_ENGINE_KIND` | `sherpa-onnx` | The diarization engine: `sherpa-onnx` or `sortformer`. | scripts/diarize.mjs | | `DIARIZE_ENGINE_CMD` | the bundled sherpa script | The engine command the wrapper runs. | scripts/diarize.mjs | | `DIARIZE_PYTHON` | `python3` | The python for the default engine. | scripts/diarize.mjs | | `DIARIZE_SEG_MODEL` | — (required) | Segmentation model. The editor passes the settings' value as a flag. | scripts/diarize.mjs | | `DIARIZE_EMB_MODEL` | — (required) | Speaker-embedding model. The editor passes the settings' value as a flag. | scripts/diarize.mjs | | `DIARIZE_THRESHOLD` | `0.5` | Clustering threshold. | scripts/diarize.mjs | | `DIARIZE_THREADS` | `4` | Engine threads. | scripts/diarize.mjs | | `DIARIZE_WINDOW_MINUTES` | `45` | Window length for long files; `0` never windows. | scripts/diarize.mjs | | `DIARIZE_WINDOW_AFTER_MINUTES` | `90` | Only files longer than this are windowed. | scripts/diarize.mjs | | `SORTFORMER_BIN` | — (required for sortformer) | The sortformer engine binary. | scripts/diarize.mjs, scripts/diarize-sortformer.mjs | | `SORTFORMER_MODEL` | — (required for sortformer) | The sortformer `.gguf`. | scripts/diarize.mjs, scripts/diarize-sortformer.mjs | | `PARAKEET_SEGMENT_SEC` | `480` | parakeet window length, seconds (a worker's chunk size wins). | scripts/parakeet-stitch.mjs | | `PARAKEET_OVERLAP_SEC` | `6` | parakeet window overlap, seconds. | scripts/parakeet-stitch.mjs | | `PARAKEET_DECODER` | parakeet-cli's | `ctc` or `tdt`, passed through to parakeet-cli. | scripts/parakeet-stitch.mjs | | `PARAKEET_LANG` | parakeet-cli's | A locale, passed through to parakeet-cli. | scripts/parakeet-stitch.mjs | | `PARAKEET_DEVICE` | parakeet-cli's | Compute device (`cpu`, `CUDA0`, `Vulkan1`, …), exported to parakeet-cli. | scripts/parakeet-stitch.mjs | | `HEAVY` | on | `0` skips the heavy slot AND the memory floor: the machine-wide one-at-a-time gate that `pnpm heavy -- `, every e2e entry point and the publish stages' `next build` go through. | scripts/queue-lock.mjs | | `HEAVY_MIN_FREE_MB` | `6000` | The memory floor: a heavy job, once it holds the slot, waits until /proc/meminfo's MemAvailable is at least this many MB. `0` turns the floor off; a machine whose MemTotal is under it runs without waiting. | scripts/queue-lock.mjs | | `HEAVY_TIMEOUT` | wait forever | Seconds a `pnpm heavy` run waits for the slot, and then for the floor, before giving up (exit 3). An e2e run uses `E2E_QUEUE_TIMEOUT` for both. | scripts/queue-lock.mjs | ## Ports Every local server's default port, from `common/lib/ports.mjs`. The primary checkout uses these; worktree N adds N × 100 (`pnpm wt list`). | Variable | Default | What it does | Read by | |---|---|---|---| | `EDITOR_PORT` | `3001` | Editor real dev/start (`pnpm dev:editor`). A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `PORT` | `3011` | Editor test server + Playwright editor baseURL. A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `EXPORT_PORT` | `3010` | Export server launched by the editor e2e. A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `EXPORT_DEV_PORT` | `3000` | Export real dev (`pnpm dev:export`) and its built `out/` (`pnpm start:export`). A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `EXPORT_E2E_PORT` | `3020` | Export's own Playwright suite. A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `OLLAMA_STUB_PORT` | `11435` | Digest-lane stub server in the editor e2e suite. A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `HOMEPAGE_DEV_PORT` | `3030` | Homepage real dev (`pnpm dev:homepage`). A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `HOMEPAGE_PORT` | `3031` | Homepage static `serve out` (start:homepage). A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `HOMEPAGE_E2E_PORT` | `3040` | Homepage's own Playwright suite. A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `HUB_PORT` | `3041` | Export's hub Playwright suite (e2e:hub). A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `REPORT_SITE_E2E_PORT` | `3042` | Export's cited report-site Playwright suite (e2e:report). A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `UMTOOL_PORT` | `3050` | Umtool real dev/start (`pnpm dev:umtool`). A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `UMTOOL_E2E_PORT` | `3051` | Umtool's own Playwright suite. A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `EDITOR_STUB_PORT` | `3052` | Stub editor the umtool e2e suite fetches clips from. A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `ORIGIN_B_PORT` | `4610` | Export's two-origin suite: the member site (e2e:2origin). A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | | `HUB_A_PORT` | `4611` | Export's two-origin suite: the hub (e2e:2origin). A worktree adds its offset (`pnpm wt list`). | common/lib/ports.mjs | ## Set by the pipeline The publish pipeline sets these for a process it spawns. Listed so a reader knows what they are; nobody sets them by hand. | Variable | Default | What it does | Read by | |---|---|---|---| | `SITE_ID` | — | Which site a compose or an export build is for. The build stage (`archilyzer publish build `) sets it for its children; `compose site`, `build site` and `deploy site` fall back to it when no id is given. | common/bin/compose-site.ts, export/app/lib/site.ts | | `INSTANCE_MODE` | a site | `hub` makes the export build the hub. Set by `archilyzer build hub`. | export/app/lib/mode.ts, common/lib/archive/contract.ts | | `BUILD_ARCHIVES` | on | `0` skips archive-zip generation for one build (`--skip-archives`). | common/bin/compose-site.ts, common/bin/build-archives.ts | | `REPORTS_ALLOW_MISSING_MEDIA` | off | `1` lets a report citation whose evidence media was not prepared through compose (`--allow-missing-media`): its moment page renders without a clip. Off, compose fails with the list. | common/bin/compose-site.ts | | `ARCHIVES_READONLY` | off | `1` inside a docker-mode build container: materialize archives, never write the shared cache. | common/bin/compose-site.ts | | `HOMEPAGE_PUBLIC_DIR` | `/homepage/public` | Where `compose homepage` and `source publish` write. | common/bin/compose-homepage.ts, common/publish/source.ts | ## Docker The container's own set, read by `docker/*.sh`, the compose files and Caddy — not by the apps' code (except `ARCHILYZER_IDLE_BOOT`, and the image's facts `archilyzer doctor` and the publish stamps read). See [RUNNING_IN_DOCKER.md](RUNNING_IN_DOCKER.md). | Variable | Default | What it does | Read by | |---|---|---|---| | `ARCHILYZER_TRANSCRIBER` | baked per image target (`whisper-cpp` in `runtime`) | `whisper-cpp` or `parakeet`: which worker the first boot seeds and which model it fetches. | docker/entrypoint.sh | | `ARCHILYZER_FETCH_MODEL` | per transcriber | Which model the first boot downloads; `none` skips it. | docker/entrypoint.sh | | `ARCHILYZER_MODELS_DIR` | `/data/models` | Where models live in the container. | docker/entrypoint.sh | | `ARCHILYZER_BUILDS_DIR` | `/data/builds` | Where the container keeps built sites. | docker/entrypoint.sh | | `ARCHILYZER_SITE_OUT` | `/data/builds/site` | The built export site the `site` service serves. | docker/entrypoint.sh, docker/publish-site.sh, common/publish/deployStage.ts | | `ARCHILYZER_HOMEPAGE_OUT` | `/data/builds/homepage` | The locally deployed homepage (`publish homepage --deploy --to local`). The `homepage` service serves it when it is non-empty, else the image's baked build. | docker/entrypoint.sh, common/publish/deployStage.ts | | `ARCHILYZER_IMAGE_YTDLP` | baked: `/usr/local/bin/yt-dlp` | The yt-dlp the image ships. `YTDLP_BIN` naming anything else is an OVERRIDE: the boot's `yt-dlp:` line and `archilyzer doctor` say so, and `YTDLP_AUTO_UPDATE` leaves it alone. | docker/entrypoint.sh, common/bin/doctor.ts | | `ARCHILYZER_COMMIT` | baked: empty unless the build passed it | The commit the image was built from — the publish stamps' `commit` where there is no .git. `ARCHILYZER_COMMIT=$(git rev-parse HEAD) docker compose build`. | the Dockerfile (a build arg), common/publish/stageBodies.ts (`imageBuildFacts`, the stamps' fallback) | | `ARCHILYZER_BRANCH` | baked: empty unless the build passed it | The branch the image was built from — the stamps' `branch`, which a production deploy checks. | the Dockerfile (a build arg), common/publish/stageBodies.ts (`imageBuildFacts`, the stamps' fallback) | | `ARCHILYZER_SOURCE_HOST_DIR` | `./.git` | The HOST's git common dir docker-compose.source.yml mounts at `/data/source.git`. In a git worktree, the primary checkout's `.git`. | docker-compose.source.yml | | `ARCHILYZER_IDLE_BOOT` | off | `1` boots the editor without arming the heartbeat or any auto-queue runner. | common/lib/idleBoot.ts (the editor) | | `ARCHILYZER_AUTH_MODE` | `basic` | `basic`, `forward` or `none` — the only escape hatch from the exposure guard. | docker/guard-exposure.sh, docker/caddy-start.sh | | `ARCHILYZER_AUTH_USER` | `archilyzer` | Basic-auth user. | docker/Caddyfile | | `ARCHILYZER_AUTH_HASH` | — | Basic-auth bcrypt hash (`caddy hash-password`). | docker/Caddyfile, docker/guard-exposure.sh | | `ARCHILYZER_AUTH_IMPORT` | derived from the mode | Set by docker/caddy-start.sh from the mode: which auth snippet the private sites import. | docker/Caddyfile | | `ARCHILYZER_FORWARD_AUTH_UPSTREAM` | — | Forward-auth server (Authelia, tinyauth, …), `host:port`. | docker/Caddyfile | | `ARCHILYZER_FORWARD_AUTH_URI` | `/api/auth/caddy` | The forward-auth server's verify path. | docker/Caddyfile | | `ARCHILYZER_TAG` | `local` | The image tag the compose files build and run. | docker-compose*.yml | ## Tests only Read only by a test harness, a fake binary or a test-mode branch. Never set one on a real instance. | Variable | Default | What it does | Read by | |---|---|---|---| | `E2E_TEST_ROUTES` | off | `1` opens the editor's `/api/test/*` routes and marks a test server at boot. Set by `editor/playwright.config.ts` on its test server, and by nothing else. | editor/app/api/test/_guard.ts, editor/instrumentation.ts | | `E2E_MODE` | `start` | `start`: the editor and umtool suites run against `next start` from a build `scripts/e2e-stamp.mjs` vouches for, rebuilt through the heavy slot when the tree moved. `dev`: against `next dev`, for iterating on one spec. The export and homepage suites always run `next dev` (static exports) and say so when asked for `start`. | editor/playwright.config.ts, umtool/playwright.config.ts, scripts/e2e-stamp.mjs | | `E2E_NEXT_DIST_DIR` | `.next` | The editor's build directory for the e2e suite's start mode (`.next/e2e`), so a test build never replaces the `.next` a running editor serves from. Set by `scripts/e2e-stamp.mjs` for its build and by `editor/playwright.config.ts` for its test server. | editor/next.config.ts | | `E2E_BUILD_CHECKED` | — | Set by a suite's config once it has checked the start-mode build's stamp, so a worker's second load of the config does not check again. | editor/playwright.config.ts, umtool/playwright.config.ts | | `E2E_QUEUE` | on | `0` skips the machine-global e2e queue (the port check still runs). | scripts/queue-lock.mjs | | `E2E_PORT_CHECK` | on | `0` skips the pre-run check that the suite's ports are free. | scripts/queue-lock.mjs | | `E2E_QUEUE_TIMEOUT` | wait forever | Seconds to wait for the queue before giving up. | scripts/queue-lock.mjs | | `E2E_PORT_GRACE_MS` | `3000` | How long the port check waits for a just-freed port. | scripts/queue-lock.mjs | | `E2E_QUEUE_LOCK_FILE` | one per machine | The queue's lock file; the queue's own tests point it elsewhere. | scripts/queue-lock.mjs | | `QUEUE_LOCK_HELD` | — | Set by the queue for the command it runs, so a nested wrapper passes through. | scripts/queue-lock.mjs | | `HEAVY_HELD` | — | Set by the heavy slot for the command it runs, so a heavy command inside it (a `pnpm heavy -- pnpm e2e`, a build stage under an e2e suite's editor) passes through. | scripts/queue-lock.mjs | | `HEAVY_LOCK_FILE` | one per machine | The heavy slot's lock file; the gate's own tests point it elsewhere. | scripts/queue-lock.mjs | | `HEAVY_MEMINFO_FILE` | `/proc/meminfo` | Where the memory floor reads MemAvailable; the gate's tests hand it a fake. | scripts/queue-lock.mjs | | `HEAVY_POLL_MS` | `5000` | How often a run waiting for the memory floor re-reads it. | scripts/queue-lock.mjs | | `PLAYWRIGHT_BASE_URL` | `http://localhost:` | The editor test server's URL; the worktree injector sets it. | editor/playwright.config.ts, editor/e2e/baseUrl.ts | | `E2E_AUDIO_CHECK_INTERVAL_MS` | the real cadence | Shrinks the mid-download audio check so the e2e suite sees it fire. | common/ytdlp/audioCheckedDownload.ts | | `E2E_AUDIO_CHECK_SIZE_GATE` | the real gate | Likewise, the size gate. | common/ytdlp/audioCheckedDownload.ts | | `E2E_AUDIO_CHECK_INTERVAL_FLOOR_MS` | the real floor | Likewise, the interval floor. | common/ytdlp/audioCheckedDownload.ts | | `E2E_AUDIO_CHECK_RECOVER_STEP_MS` | the real step | Likewise, the recovery step. | common/ytdlp/audioCheckedDownload.ts | | `E2E_AUDIO_CHECK_RECOVER_AFTER` | the real count | Likewise, the recovery count. | common/ytdlp/audioCheckedDownload.ts | | `E2E_BACKOFF_BASE_MS` | `60000` (the real base) | The first rate-limit cooldown, which every doubling starts from; the cap and the hold arithmetic keep the real constants. The editor's e2e server sets 20 s, so pacing.spec watches one lapse. | common/jobs/platformBackoff.ts | | `E2E_CLIP_WINDOW_GAP_MS` | the real gap (30–45 s, more for Rumble) | The pause between two clip-window fetches in one batch. The editor's e2e server sets 2 s, so fetch-window.spec sees the one it owes. | common/controller/fetchWindows.ts | | `E2E_AUDIO_CHECK_DEBUG_PAUSE_MS` | off | A debugging pause inside the audio check. | common/ytdlp/audioCheckedDownload.ts | | `E2E_FAKE_YTDLP_AUDIO_CHECK_MODE` | — | Fake yt-dlp: which audio-check scenario to act out. | editor/e2e/fixtures/bin/fake-ytdlp.mjs | | `E2E_FAKE_YTDLP_CHUNK_DELAY_MS` | — | Fake yt-dlp: delay between written chunks. | editor/e2e/fixtures/bin/fake-ytdlp.mjs | | `E2E_FAKE_YTDLP_CORRUPT_AFTER_CHUNK` | — | Fake yt-dlp: start corrupting after this chunk. | editor/e2e/fixtures/bin/fake-ytdlp.mjs | | `E2E_FAKE_YTDLP_CORRUPT_RUNS` | — | Fake yt-dlp: how many runs corrupt. | editor/e2e/fixtures/bin/fake-ytdlp.mjs | | `E2E_FAKE_YTDLP_DETERMINISTIC_CORRUPT` | — | Fake yt-dlp: corrupt deterministically. | editor/e2e/fixtures/bin/fake-ytdlp.mjs | | `E2E_FAKE_YTDLP_RECOVER_ON_RESUME` | — | Fake yt-dlp: a resumed run recovers. | editor/e2e/fixtures/bin/fake-ytdlp.mjs | | `E2E_FAKE_YTDLP_TOTAL_CHUNKS` | — | Fake yt-dlp: how many chunks a download has. | editor/e2e/fixtures/bin/fake-ytdlp.mjs | | `E2E_FAKE_WRANGLER_AUTH_FAIL` | — | Fake wrangler: fail as Cloudflare refusing the API token (`Authentication error [code: 10000]`). | editor/e2e/fixtures/bin/fake-wrangler.mjs | | `E2E_LIVE_CHECK` | on | `skip`: a deploy's live check reads nothing and records `skipped`. Set for the editor's test server, whose fake wrangler deploys nothing. | common/publish/liveCheck.ts | | `E2E_FAKE_GALLERY_DL_AUTH_FAIL` | — | Fake gallery-dl: fail as an auth error. | editor/e2e/fixtures/bin/fake-gallery-dl.mjs | | `E2E_FIXTURE_MAX_LIFETIME_MS` | the watchdog's | How long a fake binary may live before its watchdog kills it. | editor/e2e/fixtures/bin/_watchdog.mjs | | `E2E_OLLAMA_STUB_MODEL` | `qwen2.5:7b` | The model the ollama stub claims to serve. | editor/e2e/fixtures/ollama-stub.mjs | | `E2E_RACK_SHOTS` | off (spec skipped) | Runs the `/channels` rack screenshot audit. | editor/e2e/channels-rack-audit.spec.ts | | `E2E_TWO_ORIGIN_REBUILD` | off | `1` rebuilds the two-origin suite's cached hub bundle. | export/e2e-2origin/globalSetup.ts | | `E2E_REPORT_SITE_REUSE` | off (build again) | `1` serves the cited report-site suite's existing stage build (`.e2e-report-site/out`) instead of staging and building the fixture site again — for iterating on the specs alone; it does not see a change to the app. | export/playwright.report.config.ts | | `E2E_SHARDS` | min(max(2, cpus/2), 8) | How many containers `pnpm e2e:sharded` splits the editor suite across (`--shards N` wins). | scripts/run-sharded-e2e.mjs | | `E2E_RETRIES` | `0` | Retries per shard (`--retries N` wins); 0 keeps a sharded run comparable to a serial one. | scripts/run-sharded-e2e.mjs | | `E2E_IMAGE` | `yt-dlp-transcript-browser-e2e` | The sharded e2e run's image tag. | scripts/run-sharded-e2e.mjs | | `E2E_SKIP_BUILD` | off | `1` reuses the sharded e2e image instead of rebuilding it (`--no-build`). | scripts/run-sharded-e2e.mjs | | `E2E_SOURCE_PUBLIC_DIR` | unset (the page reads `homepage/public`) | The fixture publish the homepage's e2e dev server reads the `/source/` page from while it holds a manifest; set by `homepage/playwright.config.ts`, written and removed by `homepage/e2e/source-history.spec.ts`, ignored by a production build. | homepage/app/lib/source.ts | | `E2E_EXPECT_SOURCE` | off (both states pass) | `1` makes the homepage suite's `/source/` specs fail on the empty state; a gate that ran `archilyzer source publish` first sets it. | homepage/e2e/source.spec.ts | | `E2E_HOMEPAGE_SUMMARY_FILE` | `homepage/public/homepage-summary.json` | The synthetic summary the homepage's e2e dev server reads; set by `homepage/playwright.config.ts`, ignored by a production build. | homepage/app/lib/summary.ts |