Primate LogoPrimate

Configuration

Primate works out of the box with zero configuration. In some cases, you may wish to change the defaults. The most common use case is activating additional modules.

Configuration files are located in config. Anything you configure is merged into the defaults.

app.ts

TypeScript JavaScriptconfig/app.tsconfig/app.js
import config from "primate/config";

export default config({
  /* options */
});

config/app.ts exports the application facade for your app. For using that value at runtime — including app.config(), app.env(), app.view(), and app.root — see the Application page.

App options

Option Default Description
client.groups undefined isolated client bundle groups
client.routes undefined route-prefix to bundle-group mapping
db.migrations undefined database migration configuration
entrypoints {} named client entrypoints
http.csp {} content security policy
http.headers {} default HTTP response headers
http.host "localhost" server host
http.port 6161 server port
http.ssl.cert undefined path to SSL certificate
http.ssl.key undefined path to SSL private key
http.static.root "/" web path of static assets
loaders {} esbuild file loaders by extension
env.schema undefined schema for typed environment variables
modules [] extension modules
i18n.defaultLocale undefined default locale
i18n.locales [] list of locales
i18n.currency "USD" active currency
i18n.persist "cookie" locale persistence mode

db.migrations

Configuration for Primate's opt-in migration system.

import config from "primate/config";
import db from "@/config/db";

export default config({
  db: {
    migrations: {
      table: "migration",
      db,
    },
  },
});

Apply migrations explicitly with primate migrate:apply during development or build/migrate.js apply before starting a production server. The legacy autoapply option is deprecated in 0.41 and will be removed in 0.42. See the Stores page for the complete migration workflow.

client.groups

Client groups partition browser code and assets by URL route prefix. Each group is an independent build branch, compiled into its own output file tree. Shared dependencies are duplicated rather than emitted as chunks shared across an access boundary.

import config from "primate/config";

export default config({
  client: {
    groups: {
      public: "/",
      admin: "/admin/app/",
    },
    routes: {
      "/": "public",
      "/admin": "admin",
    },
  },
});

client.groups maps group names to asset URL prefixes. client.routes maps URL path prefixes to those names; values that are not keys in client.groups are rejected. The longest matching route prefix wins, and prefixes match whole path segments: /admin includes /admin/users but not /administrator. A / assignment is required as the default and is checked during startup. Mappings to unknown groups, duplicate normalized mappings, and duplicate asset prefixes are also rejected during startup.

Each group receives its own bootstrap, route-page registry, CSS, dynamic imports, and file-loader assets beneath its configured asset prefix. Top-level files in views are shared deliberately and are duplicated into every group; put group-private pages and layouts beside their handlers in routes.

Imported fonts, images, and other file-loader assets are likewise emitted under each group's prefix, even when their contents are identical. Distinct URLs are part of the isolation guarantee. Assets that are deliberately public and shared across every authorization boundary can instead live in static.

Navigation within a group keeps normal client routing. Navigation across groups stays in the same document: Primate first loads the destination assets and inactive styles and waits for its frontend to become ready. It then disposes the active frontend instance, replaces its group styles, and mounts the destination. Links, form redirects, and browser history all use this transition. If a destination asset cannot be loaded or is rejected by the proxy, the active app, URL, and styles remain in place. Static module code may remain in the browser's module cache after disposal.

This allows a reverse proxy to apply the same authentication rule to both the HTML route and its complete client file tree. For example, protect /admin and /admin/app/, with the more specific asset rule evaluated before a catch-all public rule. Production filenames are content-hashed and can be cached as immutable assets; HTML should not be cached with the same policy.

Client groups provide asset isolation, not application authorization. Continue to authorize every protected request in route hooks or handlers. Source maps are not emitted by Primate's client production build.

entrypoints

Named client entrypoints bundled from the client directory and injected into templates/app.html as placeholders.

import config from "primate/config";

export default config({
  entrypoints: {
    css: "master.css",
    colorscheme: "colorscheme.ts",
  },
});

Each key becomes a placeholder in your HTML template. For a key css, add %css% wherever you want the asset injected:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    %css% %head%
  </head>
  <body>
    %body%
  </body>
</html>

Any file type supported by your active frontend modules works as an entrypoint — if you have @primate/svelte configured, you can point an entrypoint at a .svelte file and it will be bundled and injected like any other asset.

The names app, head, and body are reserved and cannot be used as entrypoint keys.

http.csp

The Content Security Policy (CSP) to use.

Example of a restrictive policy.

{
// all content must come from own origin, excluding subdomains
"default-src": ["'self'"],
// styles must come from own origin, excluding subdomains
"style-src": ["'self'"],
// disallow <object>, <embed> and <applet> elements
"object-src": ["'none'"],
// disallow embedding
"frame-ancestors": ["'none'"],
// all form submissions must be to own origin
"form-action": ["'self'"],
// allow only own origin in <base>
"base-uri": ["'self'"],
}

http.headers

HTTP headers to use when generating requests using the view handler.

http.host

The HTTP host to use. This value is directly passed to the runtime.

http.port

The HTTP port to use. This value is directly passed to the runtime.

http.ssl.cert

Path to SSL certificate. If this property and http.ssl.key are set and point to a valid key/certificate pair, Primate uses https instead of http.

http.ssl.key

Path to SSL key. If http.ssl.cert and this property are set and point to a valid key/certificate pair, Primate uses https instead of http.

http.static.root

The path at which to serve static assets (those located in the static directory). Static assets take precedence over routes. This option allows you to have all static assets served at a subpath, like /public.

loaders

A map of file extensions to esbuild loaders. Currently only "file" is supported, which copies the asset into the build output and replaces the import with its URL — useful for fonts and other binary assets referenced from CSS.

import config from "primate/config";

export default config({
  loaders: {
    ".woff2": "file",
  },
});

env.schema

A Pema object schema used to validate and type environment variables exposed through the application facade.

import config from "primate/config";
import p from "pema";

export default config({
  env: {
    schema: p({
      API_TOKEN: p.string,
      PORT: p.u16,
    }),
  },
});

With env.schema configured, app.env(key) validates values when the app starts serving and becomes type-aware. See the Application page for usage.

modules

Additional modules to load at runtime.

i18n.defaultLocale

The default locale to use, must be one from the locales list.

i18n.locales

List of locales to use, must have at least one locale.

i18n.currency

Currency to use in localization.

i18n.persist

Locale persistence mode.

Reference

import type { Module } from "@primate/core";
import type { FileRef } from "@rcompat/fs";

interface Config {
  http?: {
    csp?: Record<string, string>;
    headers?: Record<string, string>;
    host?: string; // "localhost"
    port?: number; // 6161
    ssl?: {
      cert?: FileRef | string;
      key?: FileRef | string;
    };
    static?: {
      root?: string; // "/"
    };
  };
  modules?: Module[];
}

session.ts

TypeScript JavaScriptconfig/session.tsconfig/session.js
import session from "primate/session";

export default session/*<SessionShape>*/({
  /* options */
});

Session options

Option Default Description
cookie.httpOnly true mark cookie as HttpOnly
cookie.maxAge none persist cookie for this many seconds
cookie.name "session_id" name of the session cookie
cookie.path "/" path for which the cookie is valid
cookie.sameSite "Lax" SameSite cookie policy
cookie.secure app TLS setting mark cookie as Secure
store default in-memory store store managing sessions

Whether the session cookie should be marked HttpOnly and hidden from client-side JavaScript.

The number of seconds for which the browser should retain the session cookie. When omitted, it is removed when the browser session ends.

The name of the session cookie.

The session cookie path (paths on which the cookie is loaded).

The level of security to use in sending the session cookies when browsing between websites.

Whether the session cookie should be marked Secure. By default, this follows the app's TLS setting. Set it explicitly when TLS terminates at a trusted reverse proxy.

store

The store used to validate and persist sessions. By default, Primate uses an in-memory store that resets when the app restarts.

db/*.ts

TypeScript JavaScriptconfig/db.tsconfig/db.js
import sqlite from "@primate/sqlite";

export default sqlite();