Skip to content

Server Configuration

eric edited this page Jul 18, 2026 · 4 revisions

Server Configuration

Configuration comes from three layers, lowest to highest friction:

  1. Constructor optionsnew Server(options); also accepts any socket.io server options (cors, pingInterval, maxHttpBufferSize, …).
  2. A hot-reloadable JSON settings fileserver.loadSettings(path).
  3. Signed management commands at runtime — see Management Commands.

Constructor options

new Server({
    port: 17352,                       // also settable via start(port)

    auth: { enabled: true, /* ... */ },        // [[Authentication and Realms]]
    auth_store: { filename: 'tyo-mq.auth.sqlite' },  // opt-in SQLite auth store (see below)
    storage: 'memory' | 'sqlite' | 'redis' | require('./my-store'),
    storage_options: { /* backend specific */ },
    ack_timeout: '30s',                // default ACK timeout for subscriptions

    http_api: { enabled: true },       // [[Observability HTTP API]]
    cluster: { enabled: true },        // [[Clustering]]
    remote: { ticket_ttl_ms: 60000 },  // [[Remote Namespace]]
    namespaces: { '/collab': { module: './collab.js' } },  // [[Custom Namespaces]]
})

The settings file

server.loadSettings('/etc/tyo-mq/settings.json');

The file is watched (fs.watch + debounce) and deep-merged into live settings on every change — auth rules, tokens, and realm requirements take effect immediately for new connections, no restart needed. SIGHUP forces a reload. Example:

{
  "auth": {
    "enabled": true,
    "realms": {
      "acme":   { "required": true, "key": "consumer-psk", "require_acceptance": true },
      "public": { "required": false }
    },
    "tokens": [
      { "token": "secret", "realm": "acme", "role": "both" }
    ]
  },
  "storage": "redis",
  "storage_options": { "url": "redis://10.0.0.5:6379/0" },
  "cluster": { "enabled": true },
  "http_api": { "enabled": true }
}

When a settings file is loaded, runtime changes made through management commands (added realms, approved tokens, revocations) are persisted back to it, so they survive restarts.

Note: reloads are merge-based — deleting a key directly in the file does not remove it from the running server. Use the management commands (e.g. revoke_token) for removals.

The SQLite auth store (0.14.1, opt-in)

By default realms/tokens persist by rewriting the whole settings JSON on every change. Once that data grows (especially with programmatically created Ephemeral Realms), enable the SQLite auth store:

{ "auth_store": { "filename": "tyo-mq.auth.sqlite" } }

(or --auth-store [file] / TYO_MQ_AUTH_STORE=true|<file>; Node 22+.)

Realms, tokens, and management tokens then persist row-level in SQLite (WAL mode) — every change is a small transaction, and a crash mid-write can never truncate or corrupt the data. Migration is automatic: on first boot, existing realms/tokens from the file or options are imported; the JSON file then keeps only static config (hand-added entries still import on reload). Removals — including expired ephemeral realms — are durable across restarts. Online backup: server.backupAuthStore(dest) (a consistent VACUUM INTO snapshot, safe while running). Strictly opt-in: without the setting, behavior is exactly as before.

The stock server.js entry point

npm start runs a ready-made server wired to environment variables:

Variable Effect
TYO_MQ_AUTH_ENABLED=true enables auth
TYO_MQ_ENV_FILE path of the .env file to load (default .env)
TYO_MQ_ADMIN_TOKEN the admin token (auto-generated into .env if absent)
TYO_MQ_AUTO_ADMIN_TOKEN=false disables admin-token auto-generation
TYO_MQ_SETTINGS_FILE loads + watches this settings file
TYO_MQ_AUTH_STORE=true or <path> enables the SQLite auth store (also: --auth-store [file])

It also configures CORS (*), permessage-deflate compression, and socket.io v3 compatibility (allowEIO3).

Defaults that matter

  • Default port: 17352
  • Default realm (when none is given): default
  • Max HTTP buffer: 50 MB (override with maxHttpBufferSize)
  • Auth, persistence, HTTP API, clustering: all disabled by default — a bare new Server() behaves like the original simple broker.

Clone this wiki locally