Arcwayv0.3.0

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.

OptionDefaultDescription
host'0.0.0.0'Bind address
port3000Listen port
shutdownTimeoutMs10000Time to drain active requests on shutdown
maxBodySize26214400Maximum request body size in bytes (25 MB)
trustProxyfalseWhen true, reads client IP from X-Forwarded-For / X-Real-IP headers. Enable behind a reverse proxy.
corsCORS 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.

OptionDefaultDescription
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.credentialsfalseWhether to allow cookies / auth headers
cors.maxAge86400Preflight cache duration in seconds

API

Controls the API route system.

OptionDefaultDescription
enabledtrueSet false to disable all API routes
pathPrefix'/api'URL prefix for all API routes

Pages

Controls server-side rendered React pages (pages/ directory).

OptionDefaultDescription
enabledtrueSet false to disable all page rendering
fonts[]Custom font declarations (see below)
vite.enabledfalseSet 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:

FieldDescription
familyCSS font-family name
srcPath 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
preloadArray 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

OptionDefaultDescription
clientRequired. 'better-sqlite3', 'postgres', or 'mysql'
connectionRequired. 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.

OptionDefaultDescription
dir'seeds'Directory for seed files (relative to project root or absolute)

Run seeds with arcway seed.


Session

Iron-sealed, cookie-based sessions.

OptionDefaultDescription
cookieName'arcway.session'Name of the session cookie
ttl1209600Session lifetime in seconds (14 days)
cookie.httpOnlytruePrevents client-side JS from reading the cookie
cookie.securetrue in productionSends 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.

OptionDefaultDescription
driver'knex''knex' (database-backed) or 'redis'
tableName'arcway_queue'Database table name (knex driver only)
lockCooldownMs300000Time a message stays locked after being popped (5 min)
redis.urlRequired when driver is 'redis'

Cache

OptionDefaultDescription
driver'memory''memory' or 'redis'
defaultTtlMsOptional default TTL for cache entries
redis.urlRequired when driver is 'redis'

Events

Pub/sub event system for listeners in listeners/.

OptionDefaultDescription
enabledtrueSet 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.urlRequired when driver is 'redis'

Files

File storage for uploads and generated assets.

OptionDefaultDescription
driver'local''local' (filesystem) or 's3' (S3-compatible)
storageDir'.build/storage'Root directory for local storage
s3.bucketS3 bucket name
s3.regionAWS region
s3.endpointCustom endpoint URL (for MinIO or other S3-compatible services)
s3.forcePathStylefalseUse path-style URLs (required for MinIO)

Jobs

Background job runner for files in jobs/.

OptionDefaultDescription
enabledtrueSet false to disable all background jobs
dir'jobs'Directory for job files (relative to project root or absolute)
pollIntervalMs60000How often to poll for due jobs (1 min)
backoffMs1000Exponential backoff base on job failure
cooldownMs1000Global minimum delay between successive runs of the same job. Individual jobs can override with their own cooldownMs.
staleTimeoutMs300000Global timeout before a lease is considered stale and re-queued (5 min). Individual jobs can override with their own staleTimeout.

Mail

OptionDefaultDescription
enabledtrueSet false to disable mail entirely
driver'console''smtp' (send real email) or 'console' (log to terminal, useful in dev)
fromDefault sender address
hostSMTP host (smtp driver)
portSMTP port (smtp driver)
securefalseUse TLS from the start (port 465). For STARTTLS, set false with port 587.
auth.userSMTP username
auth.passSMTP password
throughput.maxPerSecondOptional outbound rate limit
throughput.maxPerMinuteOptional outbound rate limit

Inbound mail (optional) — polls an IMAP mailbox:

OptionDefaultDescription
inbound.driver'imap'
inbound.imap.hostIMAP server host
inbound.imap.portIMAP server port
inbound.imap.tlsUse TLS
inbound.imap.auth.userIMAP username
inbound.imap.auth.passIMAP password
inbound.imap.mailbox'INBOX'Mailbox folder to watch
inbound.imap.pollIntervalSeconds60How often to check for new messages
inbound.retentionDaysDays to retain fetched messages; null to keep forever

WebSocket

OptionDefaultDescription
enabledtrueSet false to disable WebSocket support
path'/ws'WebSocket endpoint path
pingIntervalMs30000How 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.

OptionDefaultDescription
enabledtrueSet false to disable the MCP debug server
secretOptional. When set, debug API requires a Bearer <secret> header or ?token=<secret> query param
logBufferSize2000Max number of log entries kept in memory

Logger

OptionDefaultDescription
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'.

OptionDefaultDescription
urlRedis 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):

  1. .env — shared defaults
  2. .env.local — local overrides (gitignored)
  3. .env.{mode} — mode-specific (.env.development, .env.production)
  4. .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.

On this page