Skip to content
archmax.ai

Environment variables

Environment variables say where the platform runs: the connections it opens, the keys it holds, the ports it binds, the directories it writes and the per-process settings the SDK reads. What a deployment offers (the design agent, the services, the skill registries, the OAuth apps) is not set here: it is the config, one document whose secrets are written as placeholders naming variables on this page.

The API and the dispatcher read the same variables. Each validates its environment when it starts, and then reads the config, before it connects to anything: a missing or malformed variable is the first line of output, naming the variable, and the process exits. Locally both load .env from the repository root when one exists (tsx --env-file-if-exists=../../.env); the image holds no .env, so a container gets its variables from --env-file, -e or its host.

A worker reads none of them. The API or dispatcher that starts one builds its environment from five of them (NODE_ENV, APP_VERSION, ARCHMAX_PROMPT_CACHE, ARCHMAX_PROMPT_CACHE_TTL, ARCHMAX_ALLOW_PRIVATE_URLS) and the worker’s own kind and memory limit, and a worker refuses to start holding anything else. A worker host reads only its own variables (see Worker host).

The API and the dispatcher refuse to start unless these hold:

Rule Variables
Always required MONGODB_URI, S3_BUCKET
Both or neither S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY
Required when NODE_ENV is production ARCHMAX_SIGN_IN (oidc or aws-alb, never dev-user), MASTER_KEY unless ARCHMAX_SECRET_STORE=aws-secrets-manager, REDIS_URL (the image’s all-in-one container sets it when it runs its own Redis)
Required with ARCHMAX_SIGN_IN=oidc ARCHMAX_OIDC_ISSUER, ARCHMAX_OIDC_CLIENT_ID
Required with ARCHMAX_SIGN_IN=aws-alb ARCHMAX_AWS_ALB_ARN (an Application Load Balancer’s ARN), ARCHMAX_AWS_ALB_SIGN_OUT_URL
Required with ARCHMAX_SECRET_STORE=aws-secrets-manager ARCHMAX_AWS_SECRETS_MANAGER_REGION
Only the named mode’s variables an ARCHMAX_OIDC_* or ARCHMAX_AUTH0_* variable without ARCHMAX_SIGN_IN=oidc, or an ARCHMAX_AWS_ALB_* one without ARCHMAX_SIGN_IN=aws-alb, refuses the start
Only the named secret store’s variables MASTER_KEY with ARCHMAX_SECRET_STORE=aws-secrets-manager, or an ARCHMAX_AWS_SECRETS_MANAGER_* variable without it, refuses the start
Within its allowed values every variable below with a fixed set of values, a URL rule or a positive-integer rule

Production has one more rule, in the config rather than here: the config’s designAgent section is required (see the config). The image sets NODE_ENV=production, so every container is held to the production rules unless it overrides the variable.

Variable Required Default Meaning
NODE_ENV no development development, test or production. production turns on the boot rules above and the config’s designAgent rule, and makes the platform ignore ARCHMAX_ALLOW_PRIVATE_URLS.
APP_VERSION no dev The release this process runs. GET /api/health answers it as version: { "status": "ok", "version": "1.5.0" }. The image sets it from its build argument: X.Y.Z for a release, unstable-<sha> for an unstable image, pr-<number> for a pull request image.
PORT no 3001 The port the API listens on. In the image, PORT is nginx’s public port instead (see The image).
API_HOST no every interface The address the API binds to. Leave it unset when another container reaches the API, as in single-process mode. The image’s all-in-one entrypoint sets 127.0.0.1 for the API it starts, so only the container’s own nginx reaches it, and with it nginx’s response headers and the client address nginx records.
CORS_ALLOWED_ORIGINS no http://localhost:5173 The browser origins the API allows, comma-separated; each entry is trimmed and empty entries are dropped.
APP_BASE_URL no the first CORS_ALLOWED_ORIGINS entry The platform’s public origin, where people and services outside reach it: shareable links such as organization invitations, and the callback address a callback trigger registers with its service. Must be a URL. The dispatcher needs it as well as the API, because it registers that address while a trigger activates.
ARCHMAX_ALLOW_NEWER_DATA no false true or false; any other value refuses the start. Lets the API and the dispatcher start against documents a newer release’s data update already rewrote, for rolling back to an older release, instead of refusing. Each boot then logs one [data-updates] warning per collection holding newer documents, leaves those documents as they are, and otherwise starts as usual; a boot with nothing newer left warns that the variable can be removed. It never lifts the refusal for documents below a collection’s baseline or without a schema version. Safe only when the newer release’s pull request says its data update can be read by the release before it; see rolling back past a data update. Honoured in production.
ARCHMAX_USER_RATE_PER_MINUTE no 60 Per-user ceiling, per minute, on expensive operations: starting turns, evaluations, design-agent turns, chat messages, trigger replays and connection tests. A request over it is answered 429 with Retry-After. A positive integer.

MongoDB holds the records, Redis backs the queues and locks, and S3 (or an S3-compatible store such as MinIO) is the only durable store for files: agent repositories as packed bundles, every environment’s draft plane and published versions, and every session’s files.

Variable Required Default Meaning
MONGODB_URI yes — The MongoDB connection string.
REDIS_URL production redis://localhost:6379 outside production The Redis connection URL. rediss://<user>:<password>@<host>:6379 brings TLS and an ACL user, as AWS MemoryDB needs. Whether the server runs in cluster mode is asked from the server when the process starts, so no setting says so. In the image’s all-in-one container, leaving it unset makes the container run its own Redis, for that container alone (see the image).
S3_BUCKET yes — The bucket every object is stored in.
S3_ACCESS_KEY_ID with the secret — The S3 access key id. Used only to construct the S3 client. Set neither key to sign with the AWS SDK’s default credential chain: an ECS task role, an EKS pod identity, an instance profile.
S3_SECRET_ACCESS_KEY with the key id — The S3 secret access key.
S3_REGION no us-east-1 The S3 region.
S3_ENDPOINT no — A custom endpoint for an S3-compatible store, such as http://minio:9000. Must be a URL. Leave it unset for AWS S3.
S3_PREFIX no — A key prefix every object is stored under, to share one bucket between deployments.
S3_FORCE_PATH_STYLE no false true or false. Path-style addressing, which MinIO and most S3-compatible stores need.

For AWS S3, set the bucket and the region, and either the two keys or neither (the host’s role). For MinIO, set the keys, S3_ENDPOINT and S3_FORCE_PATH_STYLE=true.

Only git touches local disk. The scratch and the registry clones are disposable: deleting either costs a re-materialization or a re-clone, never data, so neither needs a volume.

Variable Required Default Meaning
ARCHMAX_SCRATCH_DIR no <os tmpdir>/archmax The disposable git scratch: each git operation materializes the agent’s repository from its bundle here, runs, and writes the bundle back. A relative path resolves against the repository root.
ARCHMAX_TEMPLATE_REPO no .archmax-template The directory whose files seed every new agent repository. A relative path resolves against the repository root. When the directory is missing or empty, a built-in file set seeds it instead.
ARCHMAX_SKILL_REGISTRIES_DIR no .archmax-skills Where the registries are cloned, one directory per registry id. A relative path resolves against the process’s working directory. A missing or foreign directory is cloned again.

A deployment signs people in one way, its sign-in mode, which ARCHMAX_SIGN_IN names. Each mode reads only its own group below, and a variable of another mode’s group refuses the start, so a leftover from an earlier setup is caught rather than ignored. The SPA reads the mode from GET /api/sign-in-settings when it loads, so the image carries no sign-in setting and no frontend variable exists for one.

Variable Required Default Meaning
ARCHMAX_SIGN_IN in production the dev user, outside production The sign-in mode: oidc (OIDC sign-in), aws-alb (AWS ALB sign-in) or dev-user (one fixed local user for every request, refused in production). Any other value refuses the start.
ARCHMAX_OIDC_ISSUER with oidc — The issuer the SPA signs in at and the API verifies access tokens through, exactly as its discovery document names it (Auth0: https://<your-auth0-domain>/, with the trailing slash). Must be a URL.
ARCHMAX_OIDC_CLIENT_ID with oidc — The public client id of the SPA application, which the SPA signs in with. A token without an audience must name it in client_id or azp. Not a secret.
ARCHMAX_OIDC_AUDIENCE no — The API identifier access tokens must name in aud, such as https://api.example.com; the SPA asks for tokens for it. Auth0 needs it, or it issues tokens only Auth0 can read; leave it unset for Cognito, whose access tokens carry no audience.
ARCHMAX_OIDC_SIGN_OUT_URL no — Where signing out sends the browser when the provider’s logout isn’t standard OIDC, such as Cognito’s /logout?client_id=…&logout_uri=…. Unset, the discovery document’s end-session endpoint. Must be a URL.
ARCHMAX_AUTH0_DB_CONNECTION no — Auth0 only: the name of the database connection password users sign in with, under Authentication → Database in the Auth0 dashboard (Username-Password-Authentication unless renamed). Set, those users are offered Auth0’s change-password email; unset, nobody is.
ARCHMAX_AWS_ALB_ARN with aws-alb — The Application Load Balancer whose signed x-amzn-oidc-data header the API trusts. Its region names the host the header’s keys are fetched from. See AWS.
ARCHMAX_AWS_ALB_SIGN_OUT_URL with aws-alb — The identity provider’s full logout URL, returning to https://<host>/signed-out. Must be a URL.
ARCHMAX_AWS_ALB_SESSION_COOKIE no AWSELBAuthSessionCookie The name the load balancer’s authentication action gives its session cookie, whose shards signing out expires.
ARCHMAX_MANAGED_DOMAIN_CLAIM no https://archmax.ai/managed_domain The claim in which the identity provider asserts the domain that manages an identity, for auto-onboarding. Cognito exposes a custom attribute as custom:<name>.
ARCHMAX_EMAIL_VERIFICATION no on Whether a person’s email address must be one the identity provider verifies. Accepts 1/0, true/false, yes/no and on/off. Off, every address the provider supplies counts as verified, for access, invitations, taking over a user by email and auto-onboarding; safe only when the provider admits no address you don’t control (no self sign-up, federation limited to your own directories). The API logs a warning while it is off.

Where the credentials’ secret fields are kept, which ARCHMAX_SECRET_STORE names. Like the sign-in modes, each store reads only its own variables, and a variable of the store not named refuses the start. Switching stores moves the secrets with tools/move-secrets.ts (see AWS).

Variable Required Default Meaning
ARCHMAX_SECRET_STORE no db db, the database store: the secret fields sealed with AES-256-GCM under MASTER_KEY on each credential’s record in MongoDB. aws-secrets-manager: one AWS Secrets Manager secret per credential, signed for with the AWS SDK’s default credential chain (the task role), and no master key. Any other value refuses the start.
MASTER_KEY production, with db — The AES-256 key the database store seals every credential’s secret fields with: 32 bytes, base64-encoded. Generate one with openssl rand -base64 32. A key that does not decode to 32 bytes stops the boot. Outside production the platform starts without it, and only storing or using a credential fails.
ARCHMAX_AWS_SECRETS_MANAGER_REGION with aws-secrets-manager — The region the secrets live in, as an AWS region code such as eu-west-1.
ARCHMAX_AWS_SECRETS_MANAGER_PREFIX no archmax/credentials/ What every secret’s name begins with: a credential’s secret is <prefix><credential id>. Ends in /, at most 200 of the characters a secret’s name allows. The platform reaches no secret outside it, so the task role is granted secret:<prefix>* alone.
ARCHMAX_AWS_SECRETS_MANAGER_KMS_KEY_ID no the account’s aws/secretsmanager key A customer-managed KMS key (id, ARN or alias) new secrets are encrypted with.
ARCHMAX_AWS_SECRETS_MANAGER_ENDPOINT no — Another endpoint for the Secrets Manager API, such as a local stand-in in development. Must be a URL.
Variable Required Default Meaning
OAUTH2_REDIRECT_URL no — The one callback URL of the OAuth2 authorization-code flow, such as https://archmax.example.com/api/oauth2/callback. It must match the redirect URI registered on every OAuth app exactly. It also locates the platform’s client ID metadata document, which the authorization server of a Remote MCP Server that offers no dynamic client registration may fetch to learn the client a sign-in runs on: the same origin’s /api/oauth2/client-metadata, served only when this URL is https with the path /api/oauth2/callback, and reachable by such an authorization server only on a deployment it can reach. Unset, the platform still starts and only the Connect flow fails. Must be a URL.
ARCHMAX_DNS_VERIFICATION no on Whether an organization’s email domain needs a DNS TXT record before it counts as verified, for DNS verification and auto-onboarding. Accepts 1/0, true/false, yes/no and on/off, in any case; any other value refuses the start. On, the API reads TXT records over the host’s DNS when an Organization Owner verifies a domain and when someone at an onboarding domain first signs in, so it needs outbound DNS. Off, a domain is verified as soon as an Organization Owner adds it and nothing is read. Auto-onboarding is available either way and starts off on each domain. Turn it off only on a single-tenant deployment whose Organization Owners are all trusted: without the check, any organization’s Organization Owner can claim any unclaimed domain, a public email domain included. Read by the API only.
Variable Required Default Meaning
ARCHMAX_ALLOW_PRIVATE_URLS no false true or false. Lets owner-supplied outbound URLs (model provider base URLs, remote MCP servers, OIDC issuers) point at localhost or a private network and use plain http, for local development against a model or an MCP server on the same machine. With it set, the API and the dispatcher also leave every outbound request unguarded, which otherwise refuses a private address when it connects. Ignored when NODE_ENV is production.
Variable Required Default Meaning
ARCHMAX_TRIGGER_POLL_CONCURRENCY no 4 Trigger polls one dispatcher process runs at once. Every active polling trigger is polled once a minute by whichever dispatcher process claims it, so the polling capacity is this number times the dispatcher processes. Each poll runs in a call worker, so keep it below ARCHMAX_WORKER_CALL_SLOTS. A positive integer.

The dispatcher also reads APP_BASE_URL (see Runtime) and the prompt-cache switches it hands its workers (see The SDK).

Every turn and every call through a connection executes in a worker that the API or the dispatcher starts and supervises (see operating). Both read these:

Variable Required Default Meaning
ARCHMAX_WORKER_TURN_MEMORY_MB no 1024 Memory a turn worker may hold, in MiB. A turn past it fails naming the limit. At least 128.
ARCHMAX_WORKER_CALL_MEMORY_MB no 512 Memory a call worker may hold, in MiB: a poll, a dropdown, a connection test, a trigger’s lifecycle, a remote mount listing, the catalog. At least 128.
ARCHMAX_WORKER_TURN_SECONDS no 3600 How long a turn worker may run before it is ended.
ARCHMAX_WORKER_CALL_SECONDS no 300 How long a call worker may run before it is ended.
ARCHMAX_WORKER_TURN_SLOTS no 3 Turn workers one process runs at once: workflow, evaluation and design-agent turns together. Work beyond it waits for a slot.
ARCHMAX_WORKER_CALL_SLOTS no 6 Call workers one process runs at once.
ARCHMAX_WORKER_INSPECT no off on or off. Starts every worker with an inspector on a free port, which the dispatcher logs, and without the Node.js permission model. Refused in production.
ARCHMAX_WORKER_HOST no — A worker host, as host:port: workers start there instead of in this process. Requires ARCHMAX_WORKER_HOST_KEY.
ARCHMAX_WORKER_HOST_KEY with ARCHMAX_WORKER_HOST — The key the host and every API and dispatcher dispatching to it share, at least 32 bytes, base64-encoded (openssl rand -base64 32). It authenticates both sides of each connection and opens no data. Without ARCHMAX_WORKER_HOST, it refuses the start.

A worker host is the image’s worker-host command: a process that runs only workers. It reads these, and the two prompt-cache switches, NODE_ENV and APP_VERSION, which it builds its workers’ environment from:

Variable Required Default Meaning
ARCHMAX_WORKER_HOST_KEY yes — The same key the API and the dispatcher hold.
ARCHMAX_WORKER_HOST_PORT no 7340 The port it listens on for dispatchers.
ARCHMAX_WORKER_HOST_BIND no every interface The address it listens on.

It refuses to start, naming each variable and never its value, when its environment holds a data store’s address (MONGODB_URI, REDIS_URL, any S3_*), a key (MASTER_KEY, the secret store’s settings, an ARCHMAX_SERVICE_*_TOKEN, a client secret, a private key, a password), the config (ARCHMAX_CONFIG, ARCHMAX_CONFIG_FILE, or a config.yaml in its app directory), or a cloud credential (AWS keys and session tokens, an ECS task role’s or a pod identity’s endpoint, Google’s application credentials, Azure’s client secret or managed identity). Anything else an orchestrator adds is left alone.

Variable Required Default Meaning
ARCHMAX_CONFIG no — The config as one line of JSON, or that document base64-encoded. It wins over a config file, with one warning naming the file it shadowed. pnpm config:env produces it from a config.yaml.
ARCHMAX_CONFIG_FILE no — A config file other than config.yaml in the app directory. A named file that does not exist refuses the start. A relative path resolves against each process’s own working directory, so name an absolute one.

With neither set, the config is config.yaml in the app directory when one is there (the repository root locally, /app in the image), and otherwise the empty document. The whole rule is on the config page.

The config holds no secret. It writes each one as a ${{NAME}} placeholder, and the boot resolves it from the variable of that name. The names are the deployment’s own choice; these are the ones config.yaml.example uses:

Variable Required Default Meaning
ARCHMAX_DESIGN_AGENT_API_KEY when the config names it — The design agent’s key on the platform’s own OpenAI-compatible endpoint (designAgent.apiKey).
ARCHMAX_SERVICE_<ID>_TOKEN when the config names it — One per service, such as ARCHMAX_SERVICE_BROWSER_TOKEN: the token that one service checks (services[].token), at least 16 characters and the same value set on the service itself. Generate each with openssl rand -hex 32.
ARCHMAX_OAUTH_APP_<ID>_CLIENT_ID when the config names it — One per OAuth app: the client id of this deployment’s own app (oauthApps[].clientId).
ARCHMAX_OAUTH_APP_<ID>_CLIENT_SECRET when the config names it — One per OAuth app: its client secret (oauthApps[].clientSecret).
ARCHMAX_OAUTH_APP_GITHUB_SLUG when the config names it — The GitHub App’s slug, which the address of its installation page carries (oauthApps[].reach.url).

A placeholder whose variable is unset or empty leaves out the entry holding it, with one line on standard output naming the entry, the field and the variable, and the process starts with the rest of the config. The design agent is the exception in production: leaving out designAgent refuses the start. No variable sets a config field on its own: a variable reaches the config only through a placeholder that names it.

The SDK reads its own ARCHMAX_* variables from the environment of the worker a turn runs in, which receives the two prompt-cache switches from the process that started it and none of the others. The harness configuration reference lists all of them; these are the ones a deployment sets.

Variable Required Default Meaning
ARCHMAX_PROMPT_CACHE no on Provider prompt caching of the stable prefix (tool schemas and system prompt) on every model call. One setting governs both agents: the SDK reads it for the workflow agent and the platform for the design agent. Accepts 1/0, true/false, yes/no and on/off, in any case; any other value refuses the start.
ARCHMAX_PROMPT_CACHE_TTL no 5m The lifetime of a cached prefix: 5m or 1h.
ARCHMAX_API_BASE_URL for a rubric that names a model — The OpenAI-compatible endpoint the SDK builds a model on.
ARCHMAX_API_KEY for a rubric that names a model — The key for that endpoint.
ARCHMAX_MODEL for a rubric that names a model — The SDK’s default model id. The SDK requires it with the other two; the rubric’s own model: is the id that is called.

The platform hands the SDK the model of every turn, built from the agent’s model provider, so the SDK builds a model from ARCHMAX_API_BASE_URL, ARCHMAX_API_KEY and ARCHMAX_MODEL only for a rubric hook that names its own model:. A worker receives none of the three, so such a rubric fails with Missing required environment variable. With prompt caching on, also give the design agent its cache rates in the config (designAgent.prices.cacheRead and cacheWrite).

The image’s entrypoint reads these variables of its own. They mean nothing outside the image.

Variable Required Default Meaning
PORT no 8080 The public port nginx listens on. A host that injects PORT (Railway does) is honoured as it is. The API is started on API_PORT instead, whatever PORT holds.
API_PORT no 3001 The internal port the API binds to on 127.0.0.1, which nginx proxies /api/ to and the healthcheck probes. Not public.
ACCESS_LOG no errors How much of nginx’s HTTP access log is kept: errors (only the requests whose status is not 2xx or 3xx), all, or off. Any other value is read as errors, with a line on standard error.

A container started with a command (single-process mode) skips the entrypoint’s own steps and runs that command alone, so PORT is then the API’s own port again. See the image.

The image sets these, and a container can override any of them:

Variable Value in the image
NODE_ENV production
APP_VERSION the APP_VERSION build argument: X.Y.Z, unstable-<sha>, pr-<number>, or dev for a build without one
API_PORT 3001
ARCHMAX_SCRATCH_DIR /data/scratch
ARCHMAX_SKILL_REGISTRIES_DIR /data/skill-registries
ARCHMAX_TEMPLATE_REPO /app/.archmax-template

When REDIS_URL is unset, the entrypoint also runs the container’s own Redis, with a password it makes at each start, and points the API’s and the dispatcher’s REDIS_URL at it, password included. No other process receives the password, and the Redis refuses a client without it. It also sets ARCHMAX_EMBEDDED_REDIS=on, which makes the dispatcher take the single-dispatcher lease; it is the entrypoint’s to set, never a deployment’s. One variable of the entrypoint tunes that Redis:

Variable Required Default Meaning
ARCHMAX_EMBEDDED_REDIS_MAXMEMORY no 256mb The memory ceiling of the container’s own Redis. The job queue needs it never to evict, so a full Redis refuses new jobs rather than dropping old ones.

One more tells the container’s nginx which proxies in front may name the client:

Variable Required Default Meaning
ARCHMAX_TRUSTED_PROXIES behind a load balancer — The addresses or CIDR ranges of the proxies in front, comma-separated (a VPC’s range behind an ALB). A request from one of them is counted as from the client its X-Forwarded-For names; anything else, as from the address it came from. Unset, every request counts as from the load balancer itself, so the API’s per-caller rate limits share one count among every caller. An entry that is no address or range stops the container.
Variable Required Default Meaning
VITE_API_BASE_URL no empty The base URL the SPA calls the API at, inlined into the bundle when it is built. Empty or unset, the SPA calls the origin it is served from, which is what the image does: nginx proxies /api/ on that origin to the API. In development the Vite dev server proxies /api to http://localhost:3001.
APP_VERSION no dev The build argument the image’s APP_VERSION is set from.

The SPA has no other build-time setting: everything a deployment differs by is read when the container starts, which is what lets one image serve every deployment.