Skip to content

ENV vars

masks reads its configuration from the environment. This page lists every variable read by the server, the client libraries, and the development tooling, in one section for each context.

Context Reads
Server The ghcr.io/masksrb/masks image and any other way of running server/. Nearly everything on this page.
Client engine Masks::Rails mounted in your application. One variable, and only through the initializer the install generator writes.
Client Masks::Client in Ruby and @masks/client in the browser. Nothing.
Development ./dev, the compose stack, and the test suites. Not read by a deployed server.

./dev reference fails when the code reads a variable this page does not list, or when this page lists one the code no longer reads.

These are read when the server boots. Changing one requires a restart. Values are strings, so a boolean is the text true or false.

Variable Default
MASKS_MODE server server runs masks as an app of its own. engine runs it mounted inside another Rails app, with a database, a session, and secrets of its own. config.masks.mode sets the same thing. See Rails apps.

The server refuses to boot in production without these, and the error names the missing variable. The container image’s entrypoint derives the first four from a master key. See encryption and secrets.

Variable Default
SECRET_KEY_BASE derived Signs and encrypts sessions and cookies. Read by Rails.
ENCRYPTION_PRIMARY_KEY derived Encrypts the columns that hold secrets: signing keys, authenticator app secrets, provider and adapter credentials, connected accounts’ tokens, stream secrets, pairwise salts, and setup tokens.
ENCRYPTION_DETERMINISTIC_KEY derived Encrypts columns that are looked up by value. No column uses it yet, and Rails requires it to be set.
ENCRYPTION_KEY_DERIVATION_SALT derived Derives the per-column keys from the two above.
MASKS_MASTER_KEY generated The key the four above are derived from, when they aren’t set directly. Takes precedence over MASKS_MASTER_KEY_FILE.
MASKS_MASTER_KEY_FILE /rails/storage/master.key Where the entrypoint reads and writes the generated master key.
MASKS_PUBLIC_ORIGIN_TEMPLATE none The public origin of each tenant, with %{subdomain} standing in for the tenant, such as https://%{subdomain}.auth.example.com. A template without %{subdomain} serves every tenant from one host. The issuer, the registration endpoint, and every emailed link are built from it, and the server answers only hosts that match it.

Development and test fall back to keys checked into the source and to the request’s own Host header. SECRET_KEY_BASE_DUMMY=1 skips these checks for a build step, such as assets:precompile, that boots the application without serving it.

Variable Default
MASKS_TENANTS none Declares Multi tenancy: the subdomains this server serves, separated by commas or spaces, such as demo,acme. The Docker entrypoint creates any that do not exist before the server starts.
MASKS_TENANT none Declares Single tenancy: serves one tenant at every hostname. Cannot be combined with MASKS_TENANTS.
MASKS_SETUP_TOKEN none Replaces the setup token masks generates for each tenant, and applies to every tenant. Without it, each tenant gets a token of its own, printed in the server’s logs. The first-run screen asks for the token before it creates a tenant’s first account. Must be at least 24 characters outside development. The dev stack sets it to masks-dev.

Leaving both unset declares Dynamic tenancy. See declaring tenants for how the three modes differ.

Each of these, except MASKS_DYNAMIC_CLIENT_SCOPES, pins a setting a tenant’s managers would otherwise choose. A pinned setting applies to every tenant, and the manage API refuses to change it.

Variable Default
MASKS_NAMED_BY chosen on the first-run screen What an account signs in with: nickname, email, or either. Any other value stops the server from booting.
MASKS_DYNAMIC_REGISTRATION chosen on the first-run screen Whether clients may register themselves: off, anything (any scope a manager does not have to grant), or bounded (only the scopes in the ceiling). Any other value stops the server from booting.
MASKS_DYNAMIC_CLIENT_SCOPES the standard OIDC scopes The ceiling of scopes a self-registered client may ask for, separated by spaces or commas. A tenant’s own ceiling takes precedence. Setting it without MASKS_DYNAMIC_REGISTRATION makes an unset tenant bounded.
MASKS_BROWSERS_ONLY per tenant true refuses sign-in from any user agent that is not a web browser. false allows them.
MASKS_BLOCKED_AGENTS per tenant User agents to refuse, separated by commas or newlines. Each entry matches any user agent that contains it, ignoring case.

Each limit counts requests per tenant within a fixed window. A request over the limit receives a 429.

Variable Default
MASKS_ATTEMPT_LIMIT 10 Sign-in attempts from one IP address in 3 minutes, including sign-in through a provider.
MASKS_ACCOUNT_ATTEMPT_LIMIT 5 Sign-in attempts against one account in 3 minutes, from any IP address. Also limits password changes by a signed-in account.
MASKS_RECOVERY_LIMIT 5 Codes and links sent by email or text in 15 minutes, per IP address during sign-in and per account when confirming an address.
MASKS_REGISTRATION_LIMIT 10 Dynamic client registrations from one IP address in 10 minutes.
MASKS_DEVICE_CODE_LIMIT 30 Device user codes entered from one IP address in 3 minutes.
MASKS_CLAIM_LIMIT 3 Tenants claimed from one IP address in an hour, when a subdomain template claims a tenant on first visit. Counted across the server.
MASKS_CLAIM_CEILING 30 Tenants claimed from any IP address in an hour, under the same template. Counted across the server.

In seconds.

Variable Default
MASKS_INVITATION_LIFETIME 604800 (7 days) How long an invitation link stays valid.
MASKS_ORGANIZATION_INVITATION_LIFETIME 1209600 (14 days) How long an invitation to an organization can be accepted. Sending it again starts it over.
MASKS_PASSWORD_RESET_LIFETIME 1800 (30 minutes) How long a password reset link stays valid.
MASKS_EMAIL_VERIFICATION_LIFETIME 172800 (2 days) How long an email confirmation link stays valid.

Without MASKS_SMTP_ADDRESS, the server sends no mail of its own, and a tenant needs a mail adapter to send invitations, codes, and resets. A tenant’s mail adapter takes precedence over these settings.

Variable Default
MASKS_MAIL_FROM none The From address, such as masks <auth@example.com>. Requires MASKS_SMTP_ADDRESS in production.
MASKS_SMTP_ADDRESS none The SMTP server’s hostname. Setting it turns on SMTP delivery.
MASKS_SMTP_PORT 587 The SMTP server’s port.
MASKS_SMTP_TLS true on port 465, otherwise false true connects over TLS. false connects in plain text and upgrades with STARTTLS. The certificate is verified either way.
MASKS_SMTP_USERNAME none
MASKS_SMTP_PASSWORD none
MASKS_SMTP_AUTHENTICATION plain plain, login, or cram_md5.
MASKS_SMTP_DOMAIN none The domain sent in HELO.

masks uses three PostgreSQL databases: the primary, a cache, and a job queue. It connects as two roles. One owns the schema and runs migrations, and the other serves requests with only the table access bin/rails masks:grants hands it. Tenant isolation depends on row-level security, which a superuser bypasses and a table owner can switch off, so the production server refuses to serve as a role that is either one. See the database roles.

Variable Default
POSTGRES_HOST 127.0.0.1
POSTGRES_PORT 5432 in production, 5435 otherwise Development and test default to the port the dev stack publishes.
POSTGRES_USER masks The role that serves requests.
POSTGRES_PASSWORD masks
MASKS_MIGRATION_USER none The role that owns the schema. The container image’s entrypoint runs db:prepare and masks:grants as this role before it starts the server, and refuses to start without it. Required in production.
MASKS_MIGRATION_PASSWORD none That role’s password.
MASKS_SERVING_USER none The role bin/rails masks:grants grants table access to. The entrypoint sets it to POSTGRES_USER.
POSTGRES_DATABASE masks_production The primary database’s name in production. The cache and queue databases add _cache and _queue to it. Development and test use fixed names.

The image runs Puma in front of Rails, on PORT. See self-hosting.

Variable Default
MASKS_JOBS_IN_WEB_SERVER true Runs the job supervisor inside the web server, so one container does both. false leaves jobs to a separate bin/jobs process.
JOB_CONCURRENCY 1 Worker processes started by the job supervisor. Each runs 3 threads.
RAILS_MAX_THREADS 3 Puma threads per process. Also the size of each database connection pool, which defaults to 5 when this is unset.
PORT 3000 The port Puma listens on.
PIDFILE none Where Puma writes its process ID.
Variable Default
RAILS_ENV production in the image production, development, or test.
RAILS_LOG_LEVEL info debug, info, warn, error, or fatal. Production only.
RAILS_ASSUME_SSL true Treats every request as HTTPS, for a server behind a proxy that terminates TLS. Production only.
RAILS_FORCE_SSL true Redirects HTTP to HTTPS and sends Strict-Transport-Security. Production only.
MASKS_TRUSTED_PROXIES none Addresses or ranges of the proxies in front of masks, separated by commas, added to the private ranges Rails already trusts. masks reads X-Forwarded-For, X-Forwarded-Host, Client-IP, and Forwarded only on a request that came from one of them and drops them from any other, so a visitor cannot claim another address. Rate limits and risk checks use the address that results. Set it when a proxy on a public address, such as a CDN, forwards requests to masks. Otherwise every visitor shares that proxy’s address and its limits.
SECRET_KEY_BASE_DUMMY unset Boots with a throwaway secret and skips the production checks above. For build steps only. A server running with it set is not secure.

masks refuses to call an address on a private, loopback, or reserved network when it fetches a client’s keys, logo, sector identifier, metadata document, or resource metadata, when it calls a provider or an SMS adapter, or when it delivers a back-channel logout, a security event, or an event stream.

Variable Default
MASKS_OUTBOUND_ALLOWED none Address ranges masks may call anyway, such as 100.64.0.0/10 for apps on a private network, separated by commas. Every client that can register itself can then make masks call them, so list only the ranges you mean.
Variable Default
MASKS_THEMES_PATH /rails/themes The directory of theme stylesheets, read at boot. See themes.
Variable Default
MASKS_DOCS_URL https://masks.pages.dev The documentation linked from the first-run screens and from SCIM’s /ServiceProviderConfig.

Masks::Rails reads no ENV vars, and its configuration goes in an initializer. The initializer that bin/rails generate masks:install writes reads the issuer from the environment.

Variable Default
MASKS_ISSUER https://auth.example.com The issuer your application signs in against, such as https://demo.auth.example.com.

The initializer is your code, so you can rename the variable or remove it. The handshake with the issuer writes the client’s ID and secret to config/masks.json, and masks never reads them from the environment. See Masks::Rails.

Masks::Client and @masks/client read no ENV vars. The issuer and the client’s credentials are passed to them in code. See Masks::Client and @masks/client.

Read by ./dev, the compose files, and the test suites. A deployed server ignores them.

Variable Default
MASKS_PORT 12345 The host port of the dev server.
MASKS_DOCS_PORT 12346 The host port of the docs site.
MASKS_VITE_PORT 3036 The host port of the Vite dev server.
MASKS_IMAGE_PORT 5556 The host port of the production image, built and run by the image compose profile.
MASKS_POSTGRES_PORT 5435 The host port of PostgreSQL.
MASKS_HOST_SUFFIX auth.test The domain whose subdomains the development server answers. Compose sets it to masks.localhost. Development only.
PG_BIN_PATH none A directory put first on PATH, for a pg_dump that matches the server’s PostgreSQL version when db/structure.sql is dumped.
DEV_ALLOWED_HOSTS none Hosts the Vite and Astro dev servers answer, separated by commas. Set by compose.
DEV_CLIENT_PORT none The port the browser uses to reach the Vite or Astro dev server for live reload. Set by compose.
VITE_RUBY_HOST localhost Where Rails finds the Vite dev server. Read by vite_ruby, and set by compose.
VITE_RUBY_SKIP_PROXY false Read by vite_ruby, and set by compose.
DOCS_SITE https://masks.pages.dev The docs site’s published URL, for canonical links and the sitemap. The docs workflow sets it for preview deploys.
Variable Default
MASKS_SERVER_ROOT /rails Where the provider suite finds the Server-mode app’s built test assets, for the dummy Engine-mode host it boots.
TEST_ENV_NUMBER none Added to the test database names, such as masks_test2, so a second run does not share a database with the first.
CI unset Eager loads the application in tests, as production does, and has ./dev test use compose.test.ci.yml.
RUBOCOP_FORMAT progress The RuboCop formatter used by ./dev fmt --check.
SUITE_REF release-v5.2.4 The git ref of the OpenID conformance suite that ./dev conformance checks out.
SUITE_REPO the OpenID Foundation’s repository Where ./dev conformance clones the suite from.
SUITE_PORT 8443 The port the conformance suite listens on.
OWNER owner The nickname of the account the conformance plans sign in as.
OWNER_PASSWORD password That account’s password.