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.
Server
Section titled “Server”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. |
Required in production
Section titled “Required in production”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.
Tenants
Section titled “Tenants”| 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.
Sign-in policy
Section titled “Sign-in policy”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. |
Rate limits
Section titled “Rate limits”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. |
Lifetimes
Section titled “Lifetimes”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. |
Database
Section titled “Database”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. |
Processes
Section titled “Processes”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. |
Outbound calls
Section titled “Outbound calls”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. |
Themes
Section titled “Themes”| 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. |
Client engine
Section titled “Client engine”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.
Client
Section titled “Client”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.
Development
Section titled “Development”Read by ./dev, the compose files, and the test suites. A deployed server ignores them.
The dev stack
Section titled “The dev stack”| 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. |