Configuration
ATS has two kinds of configuration, on purpose. Deployment settings (secrets, URLs, mail transport) come from the environment and are set by whoever runs it. Workspace settings (stages, sites, templates, retention, modules) live in the database and HR edits them in the app, without a redeploy.
Node.js and Docker#
Environment variables, validated at startup. Every problem is listed by name and the process exits before opening a port. The kit's apps/node/.env.example has the same list.
| Variable | Default | Notes |
|---|---|---|
PUBLIC_URL | required | Public origin, e.g. https://ats.example.com. Used for links in emails and the CSRF origin check. |
INGEST_SECRET | required | At least 32 characters. Shared with the website that posts applications. |
PROXY_SECRET | required with AUTH_MODE=proxy | At least 16 characters. Your auth proxy must send it on every request it forwards. |
BASE_PATH | /ats | UI at ${BASE_PATH}/, API at ${BASE_PATH}/api, intake at ${BASE_PATH}/ingest. |
PORT / HOST | 3000 / 0.0.0.0 | Use HOST=127.0.0.1 when the proxy runs on the same machine. |
DATA_DIR | ./data | Holds ats.db (SQLite, WAL mode) and files/. The Docker image sets /data. |
BOOTSTRAP_ADMINS | empty | Comma-separated emails that are always admins, even on an empty database. |
AUTH_MODE | proxy | proxy, or dev for local development (requires NODE_ENV=development; signs everyone in as DEV_USER_EMAIL). |
PROXY_EMAIL_HEADER | x-forwarded-email | Header with the signed-in user's email. Authelia: remote-email. |
PROXY_NAME_HEADER | none | Optional display-name header, e.g. remote-name. |
PROXY_SECRET_HEADER | x-ats-proxy-secret | Header that carries PROXY_SECRET. |
TRUST_PROXY | false | true: audit-log IP from the right-most X-Forwarded-For entry. |
MAIL_FROM + SMTP_URL | none | SMTP mail, e.g. smtps://user:pass@smtp.example.com:465. MAIL_FROM is required with SMTP_URL. |
GRAPH_TENANT_ID, GRAPH_CLIENT_ID, GRAPH_CLIENT_SECRET, GRAPH_MAILBOX | none | Microsoft Graph instead of SMTP: set all four or none, and not together with SMTP_URL. |
MAX_FILE_BYTES | 10485760 | Maximum size per uploaded file (10 MiB). |
UI_DIST | packages/ui/dist | Where the built web app lives. |
NODE_ENV | none | Anything other than development gives JSON-line logs. The Docker image sets production. |
DEV_USER_EMAIL | admin@example.com | Only with AUTH_MODE=dev. |
SEED_ADMIN_EMAIL | none | Admin created by npm run seed in proxy mode (demo data only). |
Without SMTP_URL or the Graph variables, ATS runs without sending mail.
Cloudflare#
Bindings and [vars] go in apps/cloudflare/wrangler.toml; secrets go in with npx wrangler secret put NAME. Invalid configuration is reported by variable name (never by value) in the Worker logs, and every request gets a generic 500 until it is fixed.
| Name | Kind | Notes |
|---|---|---|
DB | D1 binding | The database (wrangler d1 create ats). |
FILES | R2 binding | Uploaded files (bucket ats-files). |
ASSETS | assets binding | The prebuilt web app, ../../packages/ui/dist. |
PUBLIC_URL | var | Public origin for links and the CSRF origin check. |
BASE_PATH | var | /ats; must match the route. |
ACCESS_TEAM_DOMAIN, ACCESS_AUD | var | Your Zero Trust team domain and the Access application's AUD tag. Required outside local dev. |
BOOTSTRAP_ADMINS | var | Comma-separated emails that are always admins. |
MAIL_FROM, GRAPH_TENANT_ID, GRAPH_CLIENT_ID, GRAPH_MAILBOX | var | Optional Microsoft Graph mail: an app registration with Mail.Send, ideally restricted with an application access policy. |
MAX_FILE_BYTES | var | Optional; default 10 MiB per file. |
INGEST_SECRET | secret | At least 32 characters. |
GRAPH_CLIENT_SECRET | secret | Only with Graph mail. |
ENVIRONMENT, DEV_USER_EMAIL | .dev.vars only | Local development identity instead of Access. Never set ENVIRONMENT in production. |
The Worker also needs a routes entry for your hostname and a Cron Trigger (crons = ["0 * * * *"], hourly), both already in the shipped wrangler.toml.
In the app: Settings#
Admins change these in Settings; they apply immediately and are stored in the database. A fresh install starts with sensible defaults, so nothing here is required before the first application arrives.
| Tab | What it controls |
|---|---|
| General | Company name (used in email templates) and theme. |
| Pipeline stages | Stage names, colors and kind (open, hired, rejected). Defaults: New, Screening, Interview, Offer, Hired, Not moving forward. |
| Sites | Your locations or brands. New site ids from submissions are added automatically. |
| Email templates | Subject and body with {{firstName}}, {{lastName}}, {{position}}, {{siteName}}, {{companyName}} placeholders. |
| Modules | Turn optional features on or off and tune them (below). |
| Sensitive fields | Answer keys to mask, exact or prefix*. Default: convicted*, work_auth*. |
| Users & access | People, their role (viewer, recruiter, admin) and the sites they may see. |
| Audit log | Browse the compliance trail. |
Modules#
| Module | Default | Settings |
|---|---|---|
audit | on | Keep entries for keepDays (default 730, minimum 30), then prune. |
retention | on | Delete candidates with no activity for months (24), or talentPoolMonths (36) if they joined the talent pool; keepHired (on) never auto-deletes people who were hired. |
talentPool | on | requireConsent (on): only recommend people who opted in. maxAgeMonths (24): ignore older applicants. |
candidateEmail | off | Template sent automatically on receipt (default: "Application received"), and templates sent when an application enters a stage. Needs mail configured. |
hrNotify | off | Email HR about new applications: default recipients plus per-site overrides. Needs mail configured. |
Per-person preferences, such as row density and last-used inbox filters, are saved for each user. Light or dark is chosen per browser.