Configuration
Configure arcway.config.js, import aliases, and environment variables
arcway.config.js
Complete reference config with all available options:
export default {
server: {
host: '0.0.0.0', // Default: '0.0.0.0'
port: 3000, // Default: 3000
shutdownTimeoutMs: 10_000, // Default: 10s
maxBodySize: 26_214_400, // Default: 25 MB
trustProxy: false, // Default: false. When true, reads client IP from X-Forwarded-For / X-Real-IP headers.
cors: {
// Or true (permissive) / false (disabled)
origin: ['https://app.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
exposedHeaders: ['X-Total-Count'],
credentials: true,
maxAge: 86400,
},
},
api: {
enabled: true, // Default: true. Set false to disable API routes.
pathPrefix: '/api', // Default: '/api'. All API routes are prefixed.
},
pages: {
enabled: true, // Default: true. Set false to disable pages/ rendering.
fonts: [
{
family: 'Inter',
src: 'fonts/inter',
preload: [400, 700],
},
],
vite: {
enabled: false, // Default: false. Set true to use Vite for dev HMR.
},
},
database: {
client: 'better-sqlite3', // 'better-sqlite3', 'postgres', or 'mysql'
connection: process.env.DATABASE_URL,
dir: 'migrations', // Default: 'migrations'
sqlite: {
useNullAsDefault: true, // SQLite only. Default: true
foreignKeys: false, // SQLite only. Enables PRAGMA foreign_keys = ON
},
},
seeds: {
dir: 'seeds', // Default: 'seeds'
},
session: {
// No password here — the session signing key is derived from vault.masterSecret.
cookieName: 'arcway.session', // Default
ttl: 1_209_600, // 14 days in seconds (default)
cookie: {
httpOnly: true, // Default: true
secure: true, // Default: true in production, false in dev
sameSite: 'lax', // Default: 'lax'
path: '/', // Default: '/'
},
},
queue: {
driver: 'knex', // 'knex' (default) or 'redis'
tableName: 'arcway_queue', // Default for knex driver
lockCooldownMs: 300_000, // Default: 5 min
redis: { url: '...' }, // Required when driver is 'redis'
},
cache: {
driver: 'memory', // 'memory' (default) or 'redis'
defaultTtlMs: '60s', // Optional default TTL, or 60000
redis: { url: '...' }, // Required when driver is 'redis'
},
events: {
enabled: true, // Default: true. Set false to disable event system.
driver: 'memory', // 'memory' (default) or 'redis'
redis: { url: '...' }, // Required when driver is 'redis'
},
files: {
driver: 'local', // 'local' (default) or 's3'
storageDir: '.build/storage', // Default: '.build/storage'
s3: {
bucket: 'my-bucket',
region: 'us-east-1',
endpoint: 'http://localhost:9000', // For MinIO
forcePathStyle: true,
},
},
jobs: {
enabled: true, // Default: true. Set false to disable background jobs.
pollIntervalMs: 60_000, // Default: 1 min
backoffMs: 1_000, // Default: exponential base
cooldownMs: 1_000, // Default: 1s. Global fallback; per-job cooldownMs overrides.
staleTimeoutMs: 300_000, // Default: 5 min. Global fallback; per-job staleTimeout overrides.
workerPoolSize: 3, // Default: availableParallelism() - 1. Set 0 to run jobs inline on the main thread.
},
mail: {
enabled: true, // Default: true. Set false to disable mail.
driver: 'smtp', // 'smtp' or 'console'
from: 'noreply@example.com',
host: 'smtp.example.com',
port: 587,
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
throughput: {
maxPerSecond: 10,
maxPerMinute: 300,
},
inbound: {
driver: 'imap',
imap: {
host: 'imap.example.com',
port: 993,
tls: true,
auth: {
user: process.env.IMAP_USER,
pass: process.env.IMAP_PASS,
},
mailbox: 'INBOX',
pollIntervalSeconds: 60,
},
retentionDays: 30, // null to keep forever
},
},
websocket: {
enabled: true, // Default: true. Set false to disable WebSocket support.
path: '/ws', // Default: '/ws'. WebSocket endpoint path.
pingIntervalMs: 30_000, // Default: 30s. How often to ping connected clients.
driver: 'memory', // Default: 'memory'. 'redis' for cluster-safe broadcasts.
},
mcp: {
enabled: true, // Default: true. Set false to disable MCP debug tools in dev.
secret: 'my-debug-token', // Optional. Requires Bearer token or ?token= for debug API access.
logBufferSize: 2000, // Default: 2000. Max log entries in memory.
},
logger: {
level: 'info', // 'debug', 'info', 'warn', 'error'
},
redis: {
url: 'redis://localhost:6379', // Shared Redis connection
keyPrefix: 'arcway:', // Default: 'arcway:'. Prefix for all Redis keys.
},
plugins: {
enabled: false, // Default: false. Turn on the native plugin loader.
dirs: ['plugins'], // Default: ['plugins']. Directories scanned for plugins.
enablements: { // Optional. Per-plugin on/off; overrides the plugin's own `enabled` flag.
'web-search': true,
},
},
vault: {
// Root master secret (v1:<base64-32-bytes>). Required whenever the vault is enabled.
// Generate with `npx arcway vault generate-key`. Wire it to any env var you choose.
masterSecret: process.env.ARCWAY_MASTER_SECRET,
// Or pass an array as a rotation keyring (ordered oldest → newest, last = active):
// masterSecret: [process.env.OLD_MASTER, process.env.NEW_MASTER],
},
};Module Reference
Server
Controls the HTTP server.
| Option | Default | Description |
|---|---|---|
host | '0.0.0.0' | Bind address |
port | 3000 | Listen port |
shutdownTimeoutMs | 10000 | Time to drain active requests on shutdown |
maxBodySize | 26214400 | Maximum request body size in bytes (25 MB) |
trustProxy | false | When true, reads client IP from X-Forwarded-For / X-Real-IP headers. Enable behind a reverse proxy. |
cors | — | CORS policy (see below) |
CORS can be set to true (allow all), false (disable), or a config object. In development mode, CORS defaults to permissive (all origins). In production, CORS is disabled unless explicitly configured.
| Option | Default | Description |
|---|---|---|
cors.origin | '*' | Allowed origins — string, array, or regex |
cors.methods | ['GET','POST','PUT','PATCH','DELETE','OPTIONS'] | Allowed HTTP methods |
cors.allowedHeaders | ['Content-Type','Authorization'] | Allowed request headers |
cors.exposedHeaders | [] | Headers exposed to the browser |
cors.credentials | false | Whether to allow cookies / auth headers |
cors.maxAge | 86400 | Preflight cache duration in seconds |
API
Controls the API route system.
| Option | Default | Description |
|---|---|---|
enabled | true | Set false to disable all API routes |
pathPrefix | '/api' | URL prefix for all API routes |
Pages
Controls server-side rendered React pages (pages/ directory).
| Option | Default | Description |
|---|---|---|
enabled | true | Set false to disable all page rendering |
fonts | [] | Custom font declarations (see below) |
vite.enabled | false | Set true to use Vite as the dev server (enables component-level HMR). Requires vite, @vitejs/plugin-react, and @tailwindcss/vite in your project. Esbuild-based dev is the default. |
Fonts — each entry in the fonts array:
| Field | Description |
|---|---|
family | CSS font-family name |
src | Path to a font directory under public/; Arcway scans files in that directory and derives weights from filenames like 300.woff2, regular.woff2, bold.woff2, or family-prefixed names such as chapmanregular.woff2 |
preload | Array of weights to inject as <link rel="preload"> |
pages: {
fonts: [
{ family: 'Inter', src: 'fonts/inter', preload: [400, 600, 700] },
{ family: 'Fira Code', src: 'fonts/fira-code' },
],
},Database
| Option | Default | Description |
|---|---|---|
client | — | Required. 'better-sqlite3', 'postgres', or 'mysql' |
connection | — | Required. Connection string or knex connection object |
dir | 'migrations' | Directory for migration files (relative to project root or absolute) |
Migrations run automatically at startup. To manage migrations manually, see arcway migrate.
When client is 'better-sqlite3', Arcway automatically applies WAL mode, a 5-second busy timeout, and synchronous=NORMAL after connection. These optimizations are built-in and cannot be overridden via config. See database docs for details.
Seeds
Database seed files.
| Option | Default | Description |
|---|---|---|
dir | 'seeds' | Directory for seed files (relative to project root or absolute) |
Run seeds with arcway seed.
Session
Iron-sealed, cookie-based sessions.
| Option | Default | Description |
|---|---|---|
cookieName | 'arcway.session' | Name of the session cookie |
ttl | 1209600 | Session lifetime in seconds (14 days) |
cookie.httpOnly | true | Prevents client-side JS from reading the cookie |
cookie.secure | true in production | Sends cookie over HTTPS only |
cookie.sameSite | 'lax' | SameSite policy ('strict', 'lax', or 'none') |
cookie.path | '/' | Cookie path scope |
The session signing/encryption key is derived from vault.masterSecret — there is no password field. See Vault.
Queue
Persistent task queue for background processing.
| Option | Default | Description |
|---|---|---|
driver | 'knex' | 'knex' (database-backed) or 'redis' |
tableName | 'arcway_queue' | Database table name (knex driver only) |
lockCooldownMs | 300000 | Time a message stays locked after being popped (5 min) |
redis.url | — | Required when driver is 'redis' |
Cache
| Option | Default | Description |
|---|---|---|
driver | 'memory' | 'memory' or 'redis' |
defaultTtlMs | — | Optional default TTL for cache entries |
redis.url | — | Required when driver is 'redis' |
Events
Pub/sub event system for listeners in listeners/.
| Option | Default | Description |
|---|---|---|
enabled | true | Set false to disable the event system |
driver | 'memory' | 'memory' (in-process) or 'redis' (cross-process) |
dir | 'listeners' | Directory for listener files (relative to project root or absolute) |
redis.url | — | Required when driver is 'redis' |
Files
File storage for uploads and generated assets.
| Option | Default | Description |
|---|---|---|
driver | 'local' | 'local' (filesystem) or 's3' (S3-compatible) |
storageDir | '.build/storage' | Root directory for local storage |
s3.bucket | — | S3 bucket name |
s3.region | — | AWS region |
s3.endpoint | — | Custom endpoint URL (for MinIO or other S3-compatible services) |
s3.forcePathStyle | false | Use path-style URLs (required for MinIO) |
Jobs
Background job runner for files in jobs/.
| Option | Default | Description |
|---|---|---|
enabled | true | Set false to disable all background jobs |
dir | 'jobs' | Directory for job files (relative to project root or absolute) |
pollIntervalMs | 60000 | How often to poll for due jobs (1 min) |
backoffMs | 1000 | Exponential backoff base on job failure |
cooldownMs | 1000 | Global minimum delay between successive runs of the same job. Individual jobs can override with their own cooldownMs. |
staleTimeoutMs | 300000 | Global timeout before a lease is considered stale and re-queued (5 min). Individual jobs can override with their own staleTimeout. |
| Option | Default | Description |
|---|---|---|
enabled | true | Set false to disable mail entirely |
driver | 'console' | 'smtp' (send real email) or 'console' (log to terminal, useful in dev) |
from | — | Default sender address |
host | — | SMTP host (smtp driver) |
port | — | SMTP port (smtp driver) |
secure | false | Use TLS from the start (port 465). For STARTTLS, set false with port 587. |
auth.user | — | SMTP username |
auth.pass | — | SMTP password |
throughput.maxPerSecond | — | Optional outbound rate limit |
throughput.maxPerMinute | — | Optional outbound rate limit |
Inbound mail (optional) — polls an IMAP mailbox:
| Option | Default | Description |
|---|---|---|
inbound.driver | — | 'imap' |
inbound.imap.host | — | IMAP server host |
inbound.imap.port | — | IMAP server port |
inbound.imap.tls | — | Use TLS |
inbound.imap.auth.user | — | IMAP username |
inbound.imap.auth.pass | — | IMAP password |
inbound.imap.mailbox | 'INBOX' | Mailbox folder to watch |
inbound.imap.pollIntervalSeconds | 60 | How often to check for new messages |
inbound.retentionDays | — | Days to retain fetched messages; null to keep forever |
WebSocket
| Option | Default | Description |
|---|---|---|
enabled | true | Set false to disable WebSocket support |
path | '/ws' | WebSocket endpoint path |
pingIntervalMs | 30000 | How often to ping connected clients (30s) |
driver | 'memory' | 'memory' (in-process only) or 'redis' (cross-worker broadcasts via pub/sub). Requires redis.url. See Cluster mode. |
MCP
MCP debug tools, available in development mode.
| Option | Default | Description |
|---|---|---|
enabled | true | Set false to disable the MCP debug server |
secret | — | Optional. When set, debug API requires a Bearer <secret> header or ?token=<secret> query param |
logBufferSize | 2000 | Max number of log entries kept in memory |
Logger
| Option | Default | Description |
|---|---|---|
level | 'info' | Minimum log level: 'debug', 'info', 'warn', or 'error' |
Redis
Shared Redis connection used by queue, cache, and events when their driver is set to 'redis'.
| Option | Default | Description |
|---|---|---|
url | — | Redis connection URL (e.g. redis://localhost:6379) |
keyPrefix | 'arcway:' | Prefix applied to all Redis keys |
Individual modules (queue, cache, events) can each specify their own redis.url to use a different Redis instance. If they don't, the top-level redis.url is used.
Subsystem Toggle Flags
The api, events, jobs, pages, mail, websocket, and mcp sections all support an enabled flag. When set to false, the corresponding subsystem is completely disabled at startup. This is useful for running stripped-down instances — e.g. a worker process that only runs jobs, with api and pages disabled.
Environment Variables
Arcway loads .env files on startup (before arcway.config.js is read):
Load priority (later overrides earlier):
.env— shared defaults.env.local— local overrides (gitignored).env.{mode}— mode-specific (.env.development,.env.production).env.{mode}.local— mode-specific local overrides
# .env
DATABASE_URL=postgres://localhost:5432/myapp
ARCWAY_MASTER_SECRET=v1:…
REDIS_URL=redis://localhost:6379// arcway.config.js -- process.env is populated before this runs
export default {
database: {
client: 'postgres',
connection: process.env.DATABASE_URL,
},
vault: {
masterSecret: process.env.ARCWAY_MASTER_SECRET,
},
};Note on Seeds
The seeds directory defaults to seeds/ at the project root. Override it with seeds.dir in your config. Run seeds with arcway seed.