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
import config from "primate/config";
export default config({
/* options */
});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 |
|---|---|---|
| build.external | [] |
packages excluded from the server bundle |
| client.partitions | undefined |
isolated client bundle partitions |
| client.routes | undefined |
route-prefix to bundle-partition 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.timeout | undefined |
in-flight HTTP idle timeout |
| 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 |
build.external
Packages and import paths to exclude from the production server bundle. At
runtime they are loaded from node_modules instead. Use this for dependencies
that load adjacent WebAssembly modules, native addons, or data files that would
otherwise be separated from the package by bundling.
import config from "primate/config";
export default config({
build: {
external: ["satori", "@resvg/resvg-js"],
},
});External dependencies must be installed wherever the production server runs. Development already keeps packages external by default.
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. Automatic startup
application is not supported. See the Stores page for the complete migration
workflow and 0.41 upgrade guidance.
client.partitions
Client partitions isolate browser code and assets by URL route prefix. Each partition 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: {
partitions: {
public: "/",
admin: "/admin/app/",
},
routes: {
"/": "public",
"/admin": "admin",
},
},
});client.partitions maps partition names to asset URL prefixes. client.routes
maps URL path prefixes to those names; values that are not keys in
client.partitions 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 partitions, duplicate normalized mappings,
and duplicate asset prefixes are also rejected during startup.
In 0.42, client.partitions replaces the 0.41 client.groups key. Rename the
key when upgrading; the old key is rejected with a migration diagnostic rather
than treated as a virtual route group. client.routes is unchanged.
Each partition 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
partition; put partition-private pages and layouts beside their handlers in
routes.
Imported fonts, images, and other file-loader assets are likewise emitted under
each partition'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 partition keeps normal client routing. Navigation across partitions 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 partition 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 partitions 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>Entrypoint keys follow the placeholder naming rules: they start with a letter or underscore and may contain letters, numbers, underscores, and hyphens.
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.timeout
The maximum time, in whole milliseconds, that an in-flight request or response
may remain inactive in production. Accepted values range from 0 through
255000; 0 disables the timeout. If omitted, the HTTP runtime's default is
preserved. This is an inactivity timeout, not an overall request deadline or a
completed connection's keep-alive timeout.
Development always disables this timeout so debugging and live reload are not interrupted, regardless of the configured value. Bun rounds positive values up to whole seconds. Deno does not expose an HTTP idle-timeout setting, so the option is validated but Deno's native default is preserved.
For server-sent events and other long-lived responses, either disable the timeout or send heartbeats more frequently than it. Reverse proxies and load balancers have independent idle timeouts; configure heartbeat cadence below the shortest timeout in the complete request path.
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
import session from "primate/session";
export default session/*<SessionShape>*/({
/* options */
});import session from "primate/session";
export default session({
/* 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 |
cookie.httpOnly
Whether the session cookie should be marked HttpOnly and hidden from
client-side JavaScript.
cookie.maxAge
The number of seconds for which the browser should retain the session cookie. When omitted, it is removed when the browser session ends.
cookie.name
The name of the session cookie.
cookie.path
The session cookie path (paths on which the cookie is loaded).
cookie.sameSite
The level of security to use in sending the session cookies when browsing between websites.
cookie.secure
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 accepts JSON-compatible object data and resets when the app restarts. A custom store validates data against its declared fields.
db/*.ts
import sqlite from "@primate/sqlite";
export default sqlite();import sqlite from "@primate/sqlite";
export default sqlite();