Configuration

Gooseplicant 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.

VariableDefaultNotes
PUBLIC_URLrequiredPublic origin, e.g. https://ats.example.com. Used for links in emails and the CSRF origin check.
INGEST_SECRETrequiredAt least 32 characters. Shared with the website that posts applications.
PROXY_SECRETrequired with AUTH_MODE=proxyAt least 16 characters. Your auth proxy must send it on every request it forwards.
BASE_PATH/atsUI at ${BASE_PATH}/, API at ${BASE_PATH}/api, intake at ${BASE_PATH}/ingest.
PORT / HOST3000 / 0.0.0.0Use HOST=127.0.0.1 when the proxy runs on the same machine.
INGEST_PORToffOptional intake-only listener: serves only ${BASE_PATH}/ingest and ${BASE_PATH}/api/health, everything else is a 404. Must differ from PORT. See Public intake, private app.
INGEST_HOSTHOSTInterface for the intake-only listener.
MAIN_INGESTtruefalse: PORT answers 404 for ${BASE_PATH}/ingest, so applications arrive only on INGEST_PORT (required).
DATA_DIR./dataHolds ats.db (SQLite, WAL mode) and files/. The Docker image sets /data.
BOOTSTRAP_ADMINSemptyComma-separated emails that are always admins, even on an empty database.
AUTH_MODEproxyproxy, or dev for local development (requires NODE_ENV=development; signs everyone in as DEV_USER_EMAIL).
PROXY_EMAIL_HEADERx-forwarded-emailHeader with the signed-in user's email. Authelia: remote-email.
PROXY_NAME_HEADERnoneOptional display-name header, e.g. remote-name.
PROXY_SECRET_HEADERx-ats-proxy-secretHeader that carries PROXY_SECRET.
TRUST_PROXYfalsetrue: audit-log IP from the right-most X-Forwarded-For entry.
MAIL_FROM + SMTP_URLnoneSMTP 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_MAILBOXnoneMicrosoft Graph instead of SMTP: set all four or none, and not together with SMTP_URL.
MAX_FILE_BYTES10485760Maximum size per uploaded file (10 MiB). For imports, the source email or PDF may be three times that, and one import request four times plus 1 MiB.
EDITIONfullfull: the Gooseplicant HR app at ${BASE_PATH}/, with Gosling alongside at ${BASE_PATH}/lite/. lite: Gosling only; ${BASE_PATH}/ redirects to /lite/, full-only API routes answer 404 not_in_edition, only the audit and retention modules run. Switching back to full keeps all data. See Gosling.
UI_DISTpackages/ui/distWhere the built web app lives.
NODE_ENVnoneAnything other than development gives JSON-line logs. The Docker image sets production.
DEV_USER_EMAILadmin@example.comOnly with AUTH_MODE=dev.
SEED_ADMIN_EMAILnoneAdmin created by npm run seed in proxy mode (demo data only).

Without SMTP_URL or the Graph variables, Gooseplicant runs without sending mail. To keep the app on your own network and publish only the intake, see Public intake, private app.

One-server install (goose)#

With the one-server setup you don't write the variables above yourself: ./goose setup turns your answers into them. Everything lives in the kit's deploy/ folder:

FileHoldsChange it with
deploy/.envYour answers and the secrets: INGEST_SECRET, PROXY_SECRET, the Cloudflare API token and the tunnel token. Readable only by you (mode 600); Docker Compose reads it../goose setup again. A value edited by hand (plain, no spaces or quotes) takes effect after ./goose render and ./goose up.
deploy/config/Generated from .env: the Caddyfile, Authelia's configuration.yml, gooseplicant.env and authelia.env.Don't edit by hand. setup, render, add-domain, update and upgrade rewrite these files.
deploy/config/authelia/users_database.ymlThe sign-in accounts, with argon2id password hashes../goose add-user, passwd, remove-user. Authelia also writes it when someone resets their password.
deploy/secrets/Authelia's session, storage-encryption and password-reset keys, and the SMTP password, mounted as Docker secrets.Nothing: generated once and kept. Don't edit or delete them (the storage key protects the registered two-step devices).
deploy/templates/The templates config/ is generated from. They ship with the kit.Edit a template, then ./goose render and ./goose up. ./goose update replaces them with the new version's templates, so redo such a change after updating.
deploy/backups/./goose backup archives (they contain the secrets too); update also keeps the previous kit here (kit-<version>.tar.gz), its log update.log and, after a failed update, update-failed-*.log../goose backup / restore, ./goose rollback.

Apart from templates/, none of these files are in the release archives. ./goose update leaves them where they are (it replaces only the kit's own files, listed in deploy/.kit-files), and the side-by-side ./goose upgrade carries .env, config/ and secrets/ over to the new folder. The Gooseplicant settings goose writes into config/gooseplicant.env:

VariableSet to
PUBLIC_URLhttps:// plus your app hostname (and port, if not 443). Hostnames added with add-domain work too, but links use this one.
BASE_PATH, EDITION/ats, and full or lite from your answer.
DATA_DIR, PORT, HOST/data (a Docker volume), 3000, 0.0.0.0 (reachable on the internal Docker network only).
INGEST_PORT, MAIN_INGEST, INGEST_SECRET3001, false and the generated secret: applications arrive only on the intake-only port.
AUTH_MODE, PROXY_SECRET, PROXY_SECRET_HEADERproxy, the generated secret, x-ats-proxy-secret (Caddy adds it).
PROXY_EMAIL_HEADER, PROXY_NAME_HEADERremote-email, remote-name (what Authelia sends).
TRUST_PROXY, BOOTSTRAP_ADMINStrue, and the admin email from setup.
MAIL_FROM, SMTP_URLOnly if you gave SMTP details and let Gooseplicant use them.

Need a variable goose doesn't ask about, such as MAX_FILE_BYTES? Add it as a line to deploy/templates/gooseplicant.env.tmpl, then:

Terminal
./goose render     # rewrites config/ from .env and templates/, and validates it
./goose up         # applies it

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.

NameKindNotes
DBD1 bindingThe database (wrangler d1 create ats).
FILESR2 bindingUploaded files (bucket ats-files).
ASSETSassets bindingThe prebuilt web app, ../../packages/ui/dist.
PUBLIC_URLvarPublic origin for links and the CSRF origin check.
BASE_PATHvar/ats; must match the route.
ACCESS_TEAM_DOMAIN, ACCESS_AUDvarYour Zero Trust team domain and the Access application's AUD tag. Required outside local dev.
BOOTSTRAP_ADMINSvarComma-separated emails that are always admins.
MAIL_FROM, GRAPH_TENANT_ID, GRAPH_CLIENT_ID, GRAPH_MAILBOXvarOptional Microsoft Graph mail: an app registration with Mail.Send, ideally restricted with an application access policy.
MAX_FILE_BYTESvarOptional; default 10 MiB per file (an import's source email or PDF may be three times that).
EDITIONvarfull (default in the shipped wrangler.toml): the HR app with Gosling alongside at /lite/. lite: Gosling only, as described in Gosling. Data is the same either way.
INGEST_SECRETsecretAt least 32 characters.
GRAPH_CLIENT_SECRETsecretOnly with Graph mail.
ENVIRONMENT, DEV_USER_EMAIL.dev.vars onlyLocal 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.

TabWhat it controls
GeneralCompany name (used in email templates) and theme.
Pipeline stagesStage names, colors and kind (open, hired, rejected). Defaults: New, Screening, Interview, Offer, Hired, Not moving forward.
SitesYour locations or brands, with aliases (Also known as) that help the importer sort old applications. New site ids from submissions are added automatically.
ImportBring in past applications from .msg, .eml and PDF files. See Import old applications.
Email templatesSubject and body with {{firstName}}, {{lastName}}, {{position}}, {{siteName}}, {{companyName}} placeholders.
ModulesTurn optional features on or off and tune them (below).
Sensitive fieldsAnswer keys to mask, exact or prefix*. Default: convicted*, work_auth*.
Users & accessPeople, their role (viewer, recruiter, admin) and the sites they may see.
Audit logBrowse the compliance trail.

Modules#

ModuleDefaultSettings
auditonKeep entries for keepDays (default 730, minimum 30), then prune.
retentiononDelete 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.
talentPoolonrequireConsent (on): only recommend people who opted in. maxAgeMonths (24): ignore older applicants.
candidateEmailoffTemplate sent automatically on receipt (default: "Application received"), and templates sent when an application enters a stage. Needs mail configured.
hrNotifyoffEmail HR about new applications: default recipients plus per-site overrides. Needs mail configured.

In EDITION=lite only audit and retention run; the others stay off whatever is stored here. Gosling has its own, smaller Admin area: Locations, Users, General, and Retention & audit.

Per-person preferences, such as row density and last-used inbox filters, are saved for each user. Light or dark is chosen per browser.