Configuration reference
Every runtime setting — profiles and hardened validation, database, required application values, file-based secrets, Canvas/LTI endpoints, SEB options, and secret rotation.
The application reads its configuration from process environment variables (or
mounted secret files). It does not load .env files itself — Compose loads an
env file explicitly, and Cloud Run injects values. Protect any local production
file with chmod 600 and keep it out of version control.
Profiles and validation
The active profile is the first non-empty value among APP_ENV, NODE_ENV, and
the alias SPRING_PROFILES_ACTIVE:
| Resolved value | Result |
|---|---|
prod / production | Production profile and hardened validation. |
test | Test profile; in-memory repositories. |
| Anything else, or unset | Development profile. |
Cloud Run is always hardened
Cloud Run is treated as hardened even for an isolated APP_ENV=dev service.
Hardened validation requires real database, Canvas, LTI, OAuth, secret, URL, and
certificate values, and startup fails before listening if configuration is
unsafe or incomplete. That fail-fast is intentional — a misconfigured exam tool
should not accept traffic.
PostgreSQL
PostgreSQL 17+ is the supported durable store. The app uses ordinary PostgreSQL protocol settings and is not tied to a managed provider.
| Variable | Default | Notes |
|---|---|---|
DATABASE_HOST | 127.0.0.1 | Host, IP, or Unix socket directory. Required when hardened. |
DATABASE_PORT | 5432 | 1–65535. Stays 5432 for Cloud SQL sockets. |
DATABASE_NAME | canvas_seb | Dedicated application database. Required when hardened. |
DATABASE_USER | canvas_seb | Application role. Required when hardened. |
DATABASE_PASSWORD | — | Required when hardened. Prefer DATABASE_PASSWORD_FILE. |
DATABASE_PASSWORD_FILE | — | Readable file with the password. Conflicts with the direct variable. |
DATABASE_SSL_MODE | disable | disable, require, verify-ca, verify-full. Verify over untrusted networks. |
DATABASE_POOL_MAX | 5 | Per-process max, 1–100. Size the DB for pool max × instances/jobs. |
DATABASE_CONNECTION_TIMEOUT_MS | 10000 | 100–120000 ms. |
DATABASE_STATEMENT_TIMEOUT_MS | 30000 | 100–600000 ms. |
DATABASE_CLEANUP_BATCH_SIZE | 500 | 1–10000. Read by the cleanup command, not normal startup. |
Cloud Run connects to Cloud SQL with
DATABASE_HOST=/cloudsql/PROJECT:REGION:INSTANCE and DATABASE_SSL_MODE=disable
(the authenticated socket is local to the sandbox). An external managed PostgreSQL
should normally use verify-full with a trusted chain; for a private CA, mount
the CA file and set NODE_EXTRA_CA_CERTS before startup.
Run npm run db:migrate (or the migration job/service) before a new revision, and
npm run db:cleanup on a schedule. /ready fails until every checked-in
migration is applied.
Required application values
| Variable | Purpose | Hardened requirement |
|---|---|---|
NODE_ENV | Node runtime mode. | production for the deployed image. |
APP_ENV | Application profile. | dev (isolated non-prod) or prod. |
PORT | HTTP port. | Defaults to 8080; platforms may inject it. |
TOOL_URL | Public origin of this deployment. | HTTPS origin only; no path/query/credentials. |
CANVAS_DOMAIN | Connected Canvas origin. | HTTPS origin only. |
LTI_CLIENT_ID | LTI 1.3 Developer Key client ID. | Required. |
LTI_PRIVATE_KEY | RSA private JWK for tool signing. | RSA 2048+ bits, exponent 65537, RS256. |
LTI_DEPLOYMENT_ID_CHECKING_ENABLED | Enforce the deployment-ID allowlist. | Defaults true; false only for controlled self-service. |
LTI_DEPLOYMENT_ID | Installed External App deployment ID. | Required when checking is enabled; comma/newline allowlist. |
CANVAS_API_CLIENT_ID | Canvas API OAuth client ID. | Required and distinct from the LTI key. |
CANVAS_API_CLIENT_SECRET | Canvas API OAuth secret. | Required secret. |
SESSION_SECRET | Express session signing secret. | ≥32 chars, different from state encryption. |
STATE_ENCRYPTION_KEY | AES-GCM material for opaque LTI/OAuth state. | ≥32 chars, different from session signing. |
SEB_CONFIG_ENCRYPTION_CERT_PEM | Public X.509 cert used to encrypt .seb. | Valid end-entity RSA cert whose Key Usage permits encryption. |
SEB_CONFIG_ENCRYPTION_CERT_PATH is the file alternative for the public
certificate. The matching private key is never a server input.
Two keys, never mixed
LTI_CLIENT_ID (LTI 1.3 Developer Key) and CANVAS_API_CLIENT_ID (API OAuth
Developer Key) come from different Canvas registrations. Mixing them breaks
either signed launches or Canvas API authorization. Likewise, SESSION_SECRET and
STATE_ENCRYPTION_KEY must be different values.
Deployment-ID policy
LTI_DEPLOYMENT_ID_CHECKING_ENABLED=true (default) restricts launches to the IDs
in LTI_DEPLOYMENT_ID. Set it to false only when the configured Canvas issuer
and LTI client ID are intentionally trusted to create course-level installs.
Disabled mode still requires Canvas's signed deployment-ID claim and retains token
signature, issuer, audience, nonce, target-link, and browser/state validation — it
removes only the preconfigured allowlist.
File-based secrets
These values accept a mutually exclusive _FILE alternative, read once at startup:
DATABASE_PASSWORD_FILELTI_PRIVATE_KEY_FILECANVAS_API_CLIENT_SECRET_FILESESSION_SECRET_FILESTATE_ENCRYPTION_KEY_FILESEB_QUIT_PASSWORD_FILE
Setting both a direct value and its _FILE form is rejected. Docker/Kubernetes
secret mounts work without provider-specific SDKs. The checked-in
compose.secrets.yaml override wires these file paths into the app, migration, and
cleanup processes and supplies PostgreSQL its own POSTGRES_PASSWORD_FILE.
Canvas and LTI endpoints
Canvas cloud defaults are built in. Override the authorization and JWKS URLs for a
Canvas with different LTI endpoints. LTI_ISSUER must equal the iss in that
Canvas's launch — do not assume it equals CANVAS_DOMAIN.
| Variable | Default | Notes |
|---|---|---|
LTI_ISSUER | https://canvas.instructure.com | HTTPS issuer URL. |
LTI_KEY_SET_URL | https://sso.canvaslms.com/api/lti/security/jwks | Canvas platform JWKS endpoint. |
LTI_AUTH_URL | https://sso.canvaslms.com/api/lti/authorize_redirect | Canvas OIDC authorization endpoint. |
CANVAS_API_BASE_URL | ${CANVAS_DOMAIN}/api/v1 | Must match the configured Canvas origin when hardened. |
CANVAS_REDIRECT_URI | ${TOOL_URL}/api/oauth2callback | If supplied, must equal the exact callback when hardened. |
See Self-hosted Canvas for the override
procedure and the sso.canvaslms.com failure it prevents.
SEB and diagnostics
| Variable | Default | Notes |
|---|---|---|
SEB_QUIT_PASSWORD | — | Optional managed exit-password fallback; must pass password policy. |
SEB_REQUIRED_DOMAINS | empty | Concrete reviewed hostnames; wildcards and IdP hosts rejected. |
SEB_CONFIG_ENCRYPTION_ENABLED | true | Must stay true when hardened. |
SEB_CONFIG_ENCRYPTION_CERT_PATH | — | Public certificate path (used by the Compose secret mount). |
SEB_CONFIG_ENCRYPTION_PUBLIC_KEY_PEM | — | Local-dev fallback; insufficient when hardened. |
SEB_CONFIG_ENCRYPTION_PUBLIC_KEY_PATH | — | File form of the local public-key fallback. |
HOST | 0.0.0.0 | Bind address. |
USE_IN_MEMORY_STORE | false | Local/test only; hardened runtimes reject it. |
APP_DEBUG_ENABLED | false | Hardened runtimes reject true. |
APP_DETECTOR_DIAGNOSTICS_ENABLED | false | Sanitized detector detail; production rejects true. |
APP_ASSET_VERSION | — | Optional client cache version; K_REVISION used when present. |
Secret rotation
Rotate one secret at a time. Create a new immutable version, point the runtime at the numbered version, deploy, smoke test, then disable the old version.
| Rotating… | Consequence |
|---|---|
SESSION_SECRET | Invalidates active sessions. |
STATE_ENCRYPTION_KEY | Invalidates outstanding opaque state. |
| LTI signing material | Requires Canvas/JWKS coordination. |
| SEB certificate | Requires distributing the new private identity and issuing fresh configurations. |
For Cloud Run, always pin numbered Secret Manager versions — never latest. The
exact secret names and bootstrap order are in
Google Cloud. Rotation runbooks are in
Operations.
Data model (for reference)
Runtime data lives in nine tables plus the schema_migrations ledger:
admin_course_connections, admin_tool_presets,
admin_tool_preset_assignments, assessments, courses, canvas_oauth_tokens,
sessions, transient_states, and operation_locks. You do not manage these by
hand — migrations own the schema and cleanup drains expired rows.
Docker / VPS
Run the same container on any Linux host with Docker Compose — host prep, mounted secrets, the migration gate, a TLS reverse proxy, and the two-pass bootstrap.
Certificate management
Create, configure, validate, and rotate the SEB configuration-encryption identity — the certificate that decides which devices can open a configuration.