[data-reveal]{opacity:1!important;transform:none!important}
Building

Configuration

Every option the DMS module accepts in antelope.config.ts — URLs, auth token lifetimes, HTML rendering, realtime driver, and dashboard presentation.

The DMS module reads one config object — the config block you give @antelopejs-private/cms in your antelope.config.ts (Project setup). The reference below covers every key and its default. All keys are optional; a bare config: {} runs with the defaults.

This is a reference page. On a first read, skim the example below and move on to Pages & components — come back when you need a specific key.
antelope.config.ts
"@antelopejs-private/cms": {
  source: { /* … */ },
  config: {
    apiBaseUrl: "http://localhost:5010",
    clientBaseUrl: "http://localhost:3001",
    homepage: "/form/form-simple",
    meta: { title: "Acme Admin", description: "Internal operations dashboard" },
    auth: {
      jwtSecret: "", // the DMS's session-token signing secret — set a strong value in production
      mustValidateEmail: true,
    },
    realtime: { driver: "auto" },
    htmlRender: { renderEndpoint: "http://localhost:3001/api/html/render" },
  },
},

Top-level options

KeyTypeDefaultPurpose
apiBaseUrlstringhttp://127.0.0.1:5010Where the backend is reachable. The frontend's authenticated fetch is bound to it.
clientBaseUrlstringhttp://localhost:3001Where the dashboard frontend is served. Used in generated links (for example the signup link in invite emails).
homepagestring/The route the dashboard opens on. Read by the frontend via useHomepage(). A user whose role cannot access it is redirected to the first page they can access.
metaobject{ title?, description? } — the dashboard's document title and description, served through /api/system-state and applied by the SEO plugin.
authobjectsee belowThe authentication tuning block.
htmlRenderobjectsee belowThe HTML/email rendering block.
realtimeobject{ driver: "auto" }The realtime transport block.
nuxtobjectsee belowThe layer-distribution block: who may fetch the frontend layers and their private options.

auth — tokens, validation, sanitization

These settings drive the Built-in authentication flow. Lifetimes are in milliseconds.

KeyTypeDefaultPurpose
jwtSecretstring""The DMS's own signing secret for session tokens, mixed into each user's per-user token secret. It is independent of the @antelopejs/auth-jwt module's secret. Set a strong value in production.
accessTokenLifetimenumber1 hourAccess-token validity. Short by design — the frontend refreshes transparently.
refreshTokenLifetimenumber30 daysRefresh-token validity. Tokens older than 15 days are rotated on refresh.
emailValidationTokenLifetimenumber1 dayHow long an email-validation token stays usable.
passwordRecoverTokenLifetimenumber1 dayHow long a password-reset token stays usable.
mustValidateEmailbooleanfalseWhen true, users must validate their email before @AuthUser-guarded routes accept them.
userSensitiveKeysstring[][]Extra User fields to strip from API responses, on top of the built-in set (tokens, 2FA secrets, …). Dotted paths supported.
oauthobjectLogin providers. See below.

auth.oauth — login providers

Precoded providers (github, google) turn into a "Continue with …" button on the login and signup screens. A provider stays disabled — and invisible — until the instance supplies both credentials, so this block is entirely opt-in.

antelope.config.ts
auth: {
  jwtSecret: "",
  oauth: {
    providers: {
      github: { clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET },
    },
    allowAccountCreation: true,
  },
},
KeyTypeDefaultPurpose
providersobject{}{ github?, google? }, each { clientId, clientSecret, scopes? }. Credentials belong in environment variables, never in a committed config.
allowAccountCreationbooleanfalseWhether an unknown provider identity may create an account without an invitation. Off by default: turning it on makes every provider account a sign-up entry.
linkByVerifiedEmailbooleantrueWhether a provider identity binds to the existing account holding the same e-mail. Turn off to require linking from an authenticated session.
callbackBaseUrlstringclientBaseUrlPublic origin the provider redirects back to. Set it when the browser-facing origin differs from the configured one.
trustProxybooleanfalseDeclare that a trusted reverse proxy sits immediately in front of the frontend servers and appends to x-forwarded-for. Drives the OAuth rate-limit client key.

Register <callbackBaseUrl>/auth/oauth/<provider>/callback as the authorization callback URL in the provider's application settings.

The browser-facing OAuth routes are rate-limited to 10 flows per minute per client, enforced by the frontend server (each route carries its own 10-hit budget, so a full start + callback flow spends one of each). What identifies a client: by default, the socket address only — x-forwarded-for is never trusted, since any caller can write it; behind a shared proxy this means clients share one budget until you opt in. Deployments with a trusted reverse proxy directly in front set oauth.trustProxy: true, and the limit then keys on the forwarded entry that proxy itself appended (the last one). Relevant mostly with allowAccountCreation on, where the flow would otherwise be an unthrottled account factory.

Account linking rules. A provider account already bound to a user always resolves to that user. Otherwise, an account holding the same e-mail is only adopted when the provider marks that e-mail verified and the local account is validated — control of the mailbox is the single prerequisite, the same bar password recovery sets. Anything else is refused rather than linked silently. Two-factor authentication is unaffected: an OAuth login on a 2FA-enabled account still runs the challenge before a session is issued.

Signing in with a provider from an invitation link consumes the invitation, provided the provider e-mail matches the invited one — and creates the account even with allowAccountCreation off, since the invitation is itself the permission to join.

htmlRender — HTML & email rendering

The backend renders HTML templates (transactional emails, static HTML) by calling the frontend's render endpoint with a short-lived service token — see Backend services.

KeyTypeDefaultPurpose
renderEndpointstringhttp://localhost:3001/api/html/renderThe frontend route that renders a named template to HTML.
serviceSecretstring"dev"Shared secret signing the service token. Change it in production.
serviceTokenLifetimenumber5 minutesValidity of each render-service token.

realtime — transport driver

The realtime layer (SSE topics, presence, notification streams — see Backend services) picks its broker with realtime.driver:

ValueBehavior
"auto"(default) Uses Redis when a @antelopejs/redis instance is available, otherwise falls back to memory.
"redis"Always use Redis pub/sub — required for multi-instance deployments.
"memory"In-process only.
The memory driver is single-instance: events, presence, and notification streams don't cross process boundaries. Run Redis (and driver: "auto" or "redis") as soon as you deploy more than one backend instance.

nuxt — layer distribution

The backend serves the frontend's Nuxt layers over two routes that acms consumes: GET /cms/nuxt (the manifest) and GET /cms/modules (a ZIP of every registered layer's source). They carry things no browser and no stranger should see — each layer's privateOptions (oauth.relaySecret, htmlRender.serviceSecret), and the full source of every layer any module registered with AddNuxtLayer, including its Nitro server/ routes.

bootstrapSecret is the credential acms presents to reach them.

KeyTypeDefaultPurpose
bootstrapSecretstringShared secret between this instance and the machine that builds its frontend. Set a strong value in production.
requireBootstrap"warn" | "enforce""warn""warn" serves an unauthenticated caller a manifest stripped of every private option, and logs once. "enforce" answers 401.
config: {
  nuxt: {
    bootstrapSecret: process.env.CMS_BOOTSTRAP_SECRET,
  },
},

Set the same value as CMS_BOOTSTRAP_SECRET in the environment that runs acms build. It is deliberately not derived from auth.jwtSecret: it lives on a build host, so it must not be a function of the secret that signs session tokens, and rotating it must not log every user out.

A frontend built without the credential comes up, but its runtimeConfig.cms is missing oauth.relaySecret and htmlRender.serviceSecret — so provider login and server-side HTML rendering fail at runtime. The backend logs exactly this on the first unauthenticated request.

In development nothing needs configuring: the instance generates an ephemeral per-boot credential and publishes it to the project's .antelope/cms-dev.json (owner-readable only, stamped with its pid), where acms dev and acms prepare find it by walking up from the working directory. The file outlives the instance that wrote it — deleting it on shutdown could race a replacement backend's freshly published credential — but a leftover copy is ignored once its pid is gone.