Both capabilities are Enterprise entitlements. In the plan configuration,
feature.sso and feature.self_host are true only on the Enterprise plan and false on Free, Pro, and Max. They are surfaced as account feature flags; they are not toggles you set yourself. Talk to the team to enable them — see contact sales.How SSO works in ModuleX
ModuleX has one identity provider: Clerk. The backend only ever verifies Clerk-issued JWTs — there are no first-party/auth/login, /register, /logout, or /refresh routes. The auth provider is read from modulex.toml as [auth].provider, which defaults to "clerk" and is the only configuration key that file consumes:
modulex.toml
What the backend verifies on every request
Whether a user arrives via SSO or ordinary Clerk sign-in, the token they carry is verified the same way. The verifier requires bothCLERK_JWKS_URL and CLERK_ISSUER to be set; if either is missing, verification returns no claims and the request is rejected.
string
required
JWKS endpoint used to verify the JWT signature. JWKS responses are cached in-process with a 300-second TTL. Missing value disables JWT verification entirely.
string
required
Expected
iss claim. The token is decoded with issuer=CLERK_ISSUER.sub (the Clerk user id), email, azp, and sid. When the configured provider is clerk, the effective organization role is sourced from the JWT’s metadata.role. See the authentication model page for the full header-by-header breakdown, including the Authorization: Bearer forms and the Socket.io handshake.
Just-in-time user provisioning
ModuleX provisions accounts just in time (JIT) on the first valid JWT for an unknown user. This is what makes SSO usable without manual seat setup: a user who signs in through your identity provider for the first time is created automatically.1
First authenticated request arrives
A Clerk JWT for a user with no existing ModuleX record reaches the backend.
2
The user record is created
A
User is created with the user-level role USER. (User-level roles are USER and SUPER_ADMIN; these are distinct from organization roles.)3
A default organization and owner membership are seeded
A personal default organization is created, and the new user is added as its
owner. The flow is idempotent and concurrent-safe, guarded by a row lock so a webhook-plus-JIT race cannot double-provision.Organization roles after SSO sign-in
Once provisioned, a user acts inside an organization under an organization role. The live roles areowner and admin only.
owner is assignable only at organization creation. Invitations and role updates are constrained to admin at the API edge. Organization scope itself is carried by the X-Organization-ID header on REST/SDK calls and by the Socket.io handshake auth.organizationId field on the realtime surface — see org context & X-Organization-ID.
Deployment options
ModuleX runs in two shapes. The default is ModuleX-managed (cloud); self-hosted is an Enterprise-only entitlement.ModuleX-managed (cloud)
The default. ModuleX operates the backend, realtime server, database, and secret storage. Available on every plan. Authentication, secrets, and the production security gate are managed for you.
Self-hosted
Enterprise only (
feature.self_host = true). You operate the ModuleX backend in your own environment and supply the required configuration and secrets described below. Talk to the team via contact sales.Self-hosting is gated by the
self_host entitlement. In the plan configuration, feature.self_host is true only on Enterprise. There is no public self-service installer documented in source. The packaging, container images, and orchestration topology for a customer self-host are TBD — confirm the supported deployment artifacts with the ModuleX team.Self-host requirements
A self-hosted backend sources its configuration through a single settings layer, resolving each secret environment variable first, then Azure Key Vault, then default. The following are the configuration values a production self-host depends on. Types and defaults are as defined in the backend configuration.string
default:"development"
required
Canonical environment name; drives all production-only enforcement. Aliases are normalized:
prod becomes production, stage becomes staging. Set to production for a real deployment. The values production and staging are the two that trigger the hard security gate below.string (≥32 chars)
required
Symmetric key used to encrypt integration credentials. Required in
production/staging (minimum length 32 = 256-bit). In development it defaults to an empty string and the app logs an insecure-key warning. Generate with python3 -c "import secrets; print(secrets.token_urlsafe(32))".string (≥32 chars)
required
A server-side secret mixed into API-key hashing to harden the one-way fingerprint. Required in
production/staging (minimum length 32). Generate with python3 -c "import secrets; print(secrets.token_hex(32))".string
default:"(empty)"
required
PostgreSQL DSN. Required in
production/staging (empty value is an error in those environments, a warning otherwise). Use the async driver form, for example postgresql+asyncpg://user:pass@db:5432/modulex.string
default:"redis://localhost:6379"
Redis connection string. Used for caching, rate limiting, and realtime coordination.
string
default:"(empty)"
required
JWKS endpoint for verifying Clerk JWTs. Required for any human sign-in (including SSO). Missing value disables JWT verification.
string
default:"(empty)"
required
Expected JWT
iss claim; must match the issuer your Clerk instance mints.comma-separated list
default:"[]"
Gate for the platform
/admin/* cross-organization monitoring routes. Comma-separated to allow zero-downtime rotation. An empty list makes the admin gate fail closed (no access). Generate with openssl rand -hex 32.string
default:"(unset)"
When set, secrets are sourced from Azure Key Vault after the environment variable lookup. Authentication uses the Container App system-assigned managed identity with the “Key Vault Secrets User” role. When unset (local/dev), only environment variables are read.
Casing on the wire. Environment variables are
UPPER_SNAKE_CASE. When sourced from Azure Key Vault, secret names are UPPER-KEBAB-CASE (underscores become hyphens) — for example ENCRYPTION_KEY is stored as ENCRYPTION-KEY and API_KEY_PEPPER as API-KEY-PEPPER. The conversion is automatic.The production startup security gate
On startup, the backend validates the security-critical secrets. The behavior depends onENVIRONMENT:
In
production/staging, any validation error prints ❌ SECURITY VALIDATION FAILED and the process exits with code 1. In development the process prints an insecure-settings warning and continues. On success it logs that security settings were validated.
Minimal production environment
Operational details that are not pinned in source — TBD. The process/worker runtime (host, port, worker count), container images and orchestration topology, backup/restore procedures, and any Celery or telemetry configuration are not documented in the configuration source. The backend exposes no
PORT, WORKERS, HOST, or SENTRY_* environment variables, which suggests those are supplied by the process manager or compose stack rather than by application config. Confirm the supported runtime with the ModuleX team.Provisioning and seats
User provisioning is JIT, as described above — new SSO users are created on first sign-in with an owner default organization. Directory-driven provisioning and de-provisioning (SCIM) is not implemented in source and is TBD. Seat limits come from plan entitlements, not from a deployment setting:
A
seats quota of null means the plan is per-seat licensed (billed, not capped at a fixed number). Enterprise sets feature.self_host and feature.sso to true; all other plans set both to false. For the full plan comparison and the credit and rate-limit allowances, see plans & pricing.
Requirements at a glance
To use SSO
To use SSO
- An Enterprise plan with
feature.sso = true. - A Clerk enterprise connection configured against your identity provider (SAML/OIDC). Connection setup lives in the Clerk dashboard and is TBD in this documentation.
CLERK_JWKS_URLandCLERK_ISSUERconfigured on the backend (managed for you on cloud).- Users are provisioned JIT on first sign-in as organization
ownerof a default org; live roles areowner/admin.
To self-host
To self-host
- An Enterprise plan with
feature.self_host = true. ENVIRONMENT=production(orstaging).ENCRYPTION_KEYandAPI_KEY_PEPPER, each at least 32 characters — both production-required. SetAPI_KEY_PEPPERexplicitly when you self-host.- A PostgreSQL
DATABASE_URL(async driver) and aREDIS_URL. CLERK_JWKS_URLandCLERK_ISSUERfor sign-in.ADMIN_DASHBOARD_API_KEYSif you use the platform admin dashboard (the gate fails closed when empty).- Optionally,
AZURE_KEY_VAULT_URLto source secrets from Azure Key Vault. - The startup security gate exits the process with code
1if any required secret is missing or too short inproduction/staging. - Container images, orchestration, and runtime topology are TBD — confirm with the ModuleX team.
Related
Authentication model
Clerk JWT verification,
mx_live_* API keys, and the header forms every caller uses.Data security & encryption
How credentials and secrets are encrypted, and the production security checks.
Roles & permissions
The live
owner/admin role model and what each can do.Contact sales
Enable SSO and self-hosting on an Enterprise plan.