Skip to content
Get started
Get started
Security: Authentication, TLS & Hardening
View Markdown
reference · security

Security, hardened by default.

The bunqueue security model: the defaults you get out of the box, the controls available to harden a deployment, and how to report vulnerabilities. Every statement on this page reflects the current codebase.

Do not open a public issue for security vulnerabilities.

Report privately through either channel:

You will receive an acknowledgement within 48 hours. Fixes ship as patch releases and are announced through GitHub Security Advisories and the npm advisory database.

A bunqueue server exposes two listeners: the TCP protocol on port 6789, used by every client SDK, and the HTTP API on port 6790, used for health, metrics, dashboards and the REST surface. Both listeners share the same token and TLS configuration, with explicit public health and metrics exceptions described below. Each broker is one process, and a shared SQLite server or PostgreSQL namespace is one trust domain: any authenticated client can operate on any queue. Multi tenant isolation, when required, is achieved by running one instance per tenant, or by namespacing queues with prefixKey where the boundary is organizational rather than adversarial.

Authentication is token based and disabled until you configure it. When AUTH_TOKENS is set, every TCP connection must authenticate as its first command. HTTP API and debug requests require the bearer token, including /gc and /heapstats. The orchestrator probes /health, /healthz, /live, and /ready intentionally remain public. /prometheus is public by default and requires the same bearer token only when METRICS_AUTH=true; that setting with an empty token set fails closed.

Terminal window
AUTH_TOKENS=$(openssl rand -hex 32) bunqueue start

Or through the configuration file:

bunqueue.config.ts
import { defineConfig } from 'bunqueue';
export default defineConfig({
auth: { tokens: [process.env.AUTH_TOKEN!] },
});

Clients pass the token in their connection options, identically across languages:

import { Queue } from 'bunqueue/client';
const queue = new Queue('emails', {
connection: { host: 'q.internal', token: process.env.BUNQUEUE_TOKEN },
});

Multiple tokens are supported (AUTH_TOKENS=token1,token2), which enables zero downtime rotation: add the new token, roll clients over, remove the old one. /prometheus can additionally be gated with METRICS_AUTH=true.

Three options, in order of preference for typical deployments:

  1. Native TLS on both listeners. Provide a certificate and key and both the TCP protocol and the HTTP API serve TLS directly, no proxy required. Partial configuration (one variable without the other) is a startup error, not a silent downgrade.

    Terminal window
    TLS_CERT_FILE=./cert.pem TLS_KEY_FILE=./key.pem bunqueue start

    Clients verify against system certificate authorities by default and accept a custom CA bundle (tls: { caFile: './ca.pem' }). Disabling verification is possible for development only. See the TLS guide.

  2. A Unix domain socket for the HTTP API on same host deployments, where access control reduces to filesystem permissions. The TCP protocol has no Unix-socket support today (TCP_SOCKET_PATH is reserved but not applied), so bind it to loopback:

    Terminal window
    HTTP_SOCKET_PATH=/run/bunqueue/http.sock HOST=127.0.0.1 bunqueue start
  3. A reverse proxy (nginx, Caddy) terminating TLS in front of the HTTP API, with the server bound to localhost.

Defaults favor a working local setup. Review this table before exposing an instance beyond a trusted network:

SettingDefaultProduction recommendation
AUTH_TOKENSunset, no authenticationAlways set; rotate with multiple tokens
HOST0.0.0.0, all interfacesBind to 127.0.0.1 or a private interface unless remote clients need direct access
TLSdisabledEnable native TLS or terminate at a proxy
CORS_ALLOW_ORIGINunset, no cross origin access is grantedSet explicitly, and only to your dashboard origins, when a browser client needs the HTTP API
METRICS_AUTHfalse, /prometheus is publicSet true if metrics may leak operational detail; with no AUTH_TOKENS, /prometheus fails closed with 503
Protocol rate limit10,000 requests per 60 s per clientTune with RATE_LIMIT_MAX_REQUESTS / RATE_LIMIT_WINDOW_MS
  • Protocol rate limiting. A sliding window limiter caps requests per client on the wire, 10,000 per 60 seconds by default, configurable via environment variables.
  • Frame size cap. TCP frames are limited to 64 MB; oversized frames are rejected before allocation, preventing memory exhaustion.
  • Per queue controls. Rate limits and global concurrency caps can be set per queue at runtime (RateLimit, SetConcurrency).
  • Webhook SSRF protection. Webhook URLs are validated before registration: only http/https, no localhost or loopback, no private IPv4 ranges, no IPv6 unique local, link local or IPv4 mapped bypasses, and no cloud metadata endpoints. Invalid targets are rejected at AddWebhook time.
  • Input validation. Queue names are restricted to a safe character set, job payloads are capped at 10 MB, and numeric options are bounds checked server side.
  • Error redaction. TCP/HTTP command failures preserve intended domain messages, but PostgreSQL SQLSTATE, constraint, driver, host, SQLite, and network diagnostics are replaced with a generic internal-server error. The same rule applies to non-throwing storage status in health/readiness, dashboards, MCP, and Cloud telemetry. SQLite disk-full keeps its actionable message so operators can distinguish and remediate exhausted local storage.
  • At rest, SQLite. Restrict the database to the service user (chmod 600) and place it on an encrypted volume. The -wal and -shm sidecars live in the same directory and require the same handling.

  • At rest, PostgreSQL. Use provider or volume encryption, require TLS with certificate verification, keep the connection URL in a secret manager, and grant the bunqueue role only the target database/schema privileges it needs.

  • Backups. SQLite S3 backups support server-side encryption; scope their IAM credentials to one bucket. PostgreSQL mode does not use this snapshot flow: configure and test normal database backups and point-in-time recovery.

  • Job payloads. Do not place secrets in job data. Store a reference and resolve it inside the worker:

    // Avoid
    await queue.add('task', { apiKey: 'secret123' });
    // Prefer
    await queue.add('task', { secretRef: 'vault:api-key' });
  • Cloud telemetry. When the optional bunqueue.io integration is enabled, job payloads and remote commands are both enabled by default. Set BUNQUEUE_CLOUD_INCLUDE_JOB_DATA=false for metadata-only telemetry and BUNQUEUE_CLOUD_REMOTE_COMMANDS=false for a read-only connection. Specific top-level fields can be redacted with BUNQUEUE_CLOUD_REDACT_FIELDS, and outgoing events can be signed with BUNQUEUE_CLOUD_SIGNING_SECRET.

SQLite-backed server example:

Terminal window
AUTH_TOKENS=$(openssl rand -hex 32) \
TLS_CERT_FILE=/etc/bunqueue/cert.pem \
TLS_KEY_FILE=/etc/bunqueue/key.pem \
HOST=10.0.0.5 \
CORS_ALLOW_ORIGIN=https://dashboard.example.com \
METRICS_AUTH=true \
BUNQUEUE_DATA_PATH=/data/bunq.db \
BUNQUEUE_CLOUD_INCLUDE_JOB_DATA=false \
BUNQUEUE_CLOUD_REMOTE_COMMANDS=false \
bunqueue start
  1. Set AUTH_TOKENS; never run an exposed instance unauthenticated.
  2. Enable TLS, natively or at a proxy; use Unix sockets when everything is on one host.
  3. Bind HOST to the narrowest interface that still reaches your clients.
  4. Leave CORS_ALLOW_ORIGIN unset unless a browser client needs the HTTP API; when it does, list the exact origins.
  5. If Cloud is enabled, explicitly disable job payload collection and remote commands unless the deployment requires them.
  6. Gate /prometheus with METRICS_AUTH=true where metrics are sensitive.
  7. Run as an unprivileged user. For SQLite, chmod 600 the data file and encrypt its persistent volume; enable S3 backups with server-side encryption where required.
  8. For PostgreSQL, inject BUNQUEUE_POSTGRES_URL from a secret manager, require verified TLS, use a least-privilege database role, assign a unique BUNQUEUE_BROKER_ID to every broker, and rely on database-native HA, backups, and point-in-time recovery instead of the SQLite S3 snapshot flow.
  9. Monitor /health, watch authentication failures in the logs, and alert on unusual job patterns.

Security fixes are released as patch versions on the current 2.x line. There are no long term support branches: keep the server and the client SDKs (bunqueue, bunqueue-client) on the latest release. Updates are announced through GitHub Security Advisories and npm advisories.