- Docs
- Resources
- 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.
Reporting vulnerabilities
Section titled “Reporting vulnerabilities”Do not open a public issue for security vulnerabilities.
Report privately through either channel:
- Email: security@bunqueue.dev
- GitHub private vulnerability reporting: open the Security tab and select “Report a vulnerability”
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.
Security model
Section titled “Security model”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
Section titled “Authentication”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.
AUTH_TOKENS=$(openssl rand -hex 32) bunqueue startOr through the configuration file:
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 },});import { Queue } from 'bunqueue-client';
const queue = new Queue('emails', { host: 'q.internal', token: process.env.BUNQUEUE_TOKEN });queue = Queue("emails", host="q.internal", token=os.environ["BUNQUEUE_TOKEN"])$queue = new Queue('emails', [ 'host' => 'q.internal', 'token' => getenv('BUNQUEUE_TOKEN'),]);queue := bunqueue.NewQueue("emails", bunqueue.Options{ Host: "q.internal", Token: os.Getenv("BUNQUEUE_TOKEN"),})use bunqueue_client::{ConnectionOptions, Queue};
let queue = Queue::new("emails", ConnectionOptions { host: "q.internal".into(), token: std::env::var("BUNQUEUE_TOKEN").ok(), ..Default::default()});queue = Bunqueue.queue("emails", host: "q.internal", token: System.fetch_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.
Transport security
Section titled “Transport security”Three options, in order of preference for typical deployments:
-
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 startClients 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. -
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_PATHis 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 -
A reverse proxy (nginx, Caddy) terminating TLS in front of the HTTP API, with the server bound to localhost.
Network exposure and defaults
Section titled “Network exposure and defaults”Defaults favor a working local setup. Review this table before exposing an instance beyond a trusted network:
| Setting | Default | Production recommendation |
|---|---|---|
AUTH_TOKENS | unset, no authentication | Always set; rotate with multiple tokens |
HOST | 0.0.0.0, all interfaces | Bind to 127.0.0.1 or a private interface unless remote clients need direct access |
| TLS | disabled | Enable native TLS or terminate at a proxy |
CORS_ALLOW_ORIGIN | unset, no cross origin access is granted | Set explicitly, and only to your dashboard origins, when a browser client needs the HTTP API |
METRICS_AUTH | false, /prometheus is public | Set true if metrics may leak operational detail; with no AUTH_TOKENS, /prometheus fails closed with 503 |
| Protocol rate limit | 10,000 requests per 60 s per client | Tune with RATE_LIMIT_MAX_REQUESTS / RATE_LIMIT_WINDOW_MS |
Abuse protection
Section titled “Abuse protection”- 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 atAddWebhooktime. - 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.
Data protection
Section titled “Data protection”-
At rest, SQLite. Restrict the database to the service user (
chmod 600) and place it on an encrypted volume. The-waland-shmsidecars 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:
// Avoidawait queue.add('task', { apiKey: 'secret123' });// Preferawait 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=falsefor metadata-only telemetry andBUNQUEUE_CLOUD_REMOTE_COMMANDS=falsefor a read-only connection. Specific top-level fields can be redacted withBUNQUEUE_CLOUD_REDACT_FIELDS, and outgoing events can be signed withBUNQUEUE_CLOUD_SIGNING_SECRET.
Hardening checklist
Section titled “Hardening checklist”SQLite-backed server example:
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- Set
AUTH_TOKENS; never run an exposed instance unauthenticated. - Enable TLS, natively or at a proxy; use Unix sockets when everything is on one host.
- Bind
HOSTto the narrowest interface that still reaches your clients. - Leave
CORS_ALLOW_ORIGINunset unless a browser client needs the HTTP API; when it does, list the exact origins. - If Cloud is enabled, explicitly disable job payload collection and remote commands unless the deployment requires them.
- Gate
/prometheuswithMETRICS_AUTH=truewhere metrics are sensitive. - Run as an unprivileged user. For SQLite,
chmod 600the data file and encrypt its persistent volume; enable S3 backups with server-side encryption where required. - For PostgreSQL, inject
BUNQUEUE_POSTGRES_URLfrom a secret manager, require verified TLS, use a least-privilege database role, assign a uniqueBUNQUEUE_BROKER_IDto every broker, and rely on database-native HA, backups, and point-in-time recovery instead of the SQLite S3 snapshot flow. - Monitor
/health, watch authentication failures in the logs, and alert on unusual job patterns.
Supported versions
Section titled “Supported versions”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.