Skip to content
Get started
Get started
TCP Protocol Reference: Binary MessagePack Commands
View Markdown
api reference · tcp

The wire protocol, documented.

A high-performance binary protocol on port 6789 by default. All messages use MessagePack encoding with length-prefixed framing, and pipelining lets the server process commands concurrently.

Every message (request and response) is wrapped in a length-prefixed frame:

Frame layoutrequest and response
payload length 4 bytes, big-endian unsigned 32-bit
MessagePack payload N bytes

The framing protocol works as follows:

  1. The first 4 bytes are a big-endian unsigned 32-bit integer indicating the length of the MessagePack payload.
  2. The next N bytes are the MessagePack-encoded command or response object.
  3. Maximum frame size is 64 MB. Frames exceeding this limit cause the connection to be terminated.
import { pack, unpack } from 'msgpackr';
// Encode a command into a framed message
function frameCommand(cmd: object): Uint8Array {
const payload = pack(cmd);
const frame = new Uint8Array(4 + payload.length);
// Write length prefix (big-endian u32)
frame[0] = (payload.length >> 24) & 0xff;
frame[1] = (payload.length >> 16) & 0xff;
frame[2] = (payload.length >> 8) & 0xff;
frame[3] = payload.length & 0xff;
frame.set(payload, 4);
return frame;
}
// Decode a framed response (skip the 4-byte length prefix)
function decodeFrame(frame: Uint8Array): object {
return unpack(frame.subarray(4));
}
import { pack, unpack } from 'msgpackr';
const socket = await Bun.connect({
hostname: 'localhost',
port: 6789,
socket: {
data(socket, data) {
// Parse frames from data, then unpack each frame with msgpackr
},
},
});
// Send a command
const cmd = pack({ cmd: 'Ping' });
const frame = new Uint8Array(4 + cmd.length);
frame[0] = (cmd.length >> 24) & 0xff;
frame[1] = (cmd.length >> 16) & 0xff;
frame[2] = (cmd.length >> 8) & 0xff;
frame[3] = cmd.length & 0xff;
frame.set(cmd, 4);
socket.write(frame);

Clients should send a Hello command after connecting to report their protocol revision and discover server capabilities.

Request:

{ cmd: 'Hello', protocolVersion: 3, capabilities: ['pipelining', 'separate-job-name'] }

Response:

{
ok: true,
protocolVersion: 3,
capabilities: ['pipelining', 'separate-job-name'],
server: 'bunqueue',
version: 'x.y.z' // Installed server package version
}

The current protocol version is 3. It supports pipelining and separate-job-name. Revision 3 places job metadata in top-level job.name and preserves job.data exactly as supplied. The server still accepts legacy inputs with no top-level name: at that inbound boundary only, a string data.name is decoded as the old embedded-name envelope.

The server supports pipelining: clients can send multiple commands without waiting for each response. The server processes frames in parallel with a concurrency limit of 50 commands per connection, controlled by a semaphore.

To correlate responses with requests when pipelining, include a reqId field in each command. The server echoes reqId back in the corresponding response.

// Send two commands simultaneously
socket.write(frameCommand({ cmd: 'PUSH', queue: 'emails', data: { to: 'a@b.com' }, reqId: '1' }));
socket.write(frameCommand({ cmd: 'PUSH', queue: 'emails', data: { to: 'c@d.com' }, reqId: '2' }));
// Responses may arrive in any order - match by reqId
// { ok: true, id: 'abc-123', reqId: '1' }
// { ok: true, id: 'def-456', reqId: '2' }

When the server is configured with AUTH_TOKENS, all connections must authenticate before sending other commands. The Auth command is always permitted regardless of authentication state.

Request:

{ cmd: 'Auth', token: 'your-secret-token' }

Response (success):

{
ok: true;
}

Response (failure):

{ ok: false, error: 'Invalid token' }

If auth tokens are configured and a client sends any command before authenticating, the server responds with:

{ ok: false, error: 'Not authenticated' }

All responses include an ok boolean field. On success ok is true with command-specific data. On failure ok is false with an error string.

// Success
{ ok: true, ...data, reqId?: string }
// Error
{ ok: false, error: 'Error message', reqId?: string }

SubscribeEvents selects one queue for the current connection; UnsubscribeEvents clears it without closing the socket. Both commands require normal authentication and return a regular reqId-correlated response.

{ cmd: 'SubscribeEvents', queue: 'tasks', reqId: 'events-1' }
{ ok: true, reqId: 'events-1' }
// Later, independently of command responses:
{ type: 'event', event: { eventType: 'completed', queue: 'tasks', jobId: '...', timestamp: 0, data: { ok: true } } }
{ cmd: 'UnsubscribeEvents', reqId: 'events-2' }

The unsolicited event envelope has no reqId. Pipelined clients must recognize type: 'event' before correlating command responses. A new subscription on the same connection replaces the previous queue. Slow subscribers are subject to the normal per-connection write-buffer limit.

An optional eventTypes array (1 to 32 event type names of up to 64 characters; null means no filter) limits the stream to those types, which saves the broker the encoding and the client the decoding of everything else. The bunqueue Worker subscribes this way for stalled only:

{ cmd: 'SubscribeEvents', queue: 'tasks', eventTypes: ['stalled'], reqId: 'events-3' }

A broker older than the filter ignores eventTypes and sends every type, so check event.eventType on the client side as well.

When a TCP connection closes, the server automatically releases all jobs that were being processed by that client back to their queues. This uses retry logic with exponential backoff (up to 3 attempts) to ensure jobs are not left in an inconsistent state.

Each connection is subject to server-side rate limiting. If exceeded, the server responds with:

{ ok: false, error: 'Rate limit exceeded' }

Every command object must include a cmd field. An optional reqId field can be included for request-response correlation (required for pipelining).

The commands are documented by family on the pages below, and the command summary links every command to its section.

PageCommand familiesCovers
JobsCore, Log, Lock, More JobAdd, claim, acknowledge and fail jobs; job logs; lock extension; more job transitions
QueriesQueryRead jobs, states, results, progress, child values and counts
Control & LimitsControl, Rate Limiting, Queue ConfigJob and queue control, rate and concurrency limits, job groups, deduplication keys, stall and DLQ config
Dead Letter QueueDLQInspect, retry, purge and remove dead-letter entries; re-queue completed jobs
CronCronCreate, update, list, read and delete cron schedules
FlowsFlow DependencyParent/child dependency commands used by FlowProducer
MonitoringMonitoring, DashboardHealth, protocol negotiation, stats, metrics, heartbeats and dashboard snapshots
Workers & WebhooksWorker, WebhookWorker registration and webhooks

Queue names must satisfy the following constraints:

  • Not empty and at most 256 characters
  • Only alphanumeric characters, underscores, dashes, dots, and colons: [a-zA-Z0-9_\-.:]+

Job data payloads are limited to 10 MB when serialized.

CategoryCommandDescription
CorePUSHAdd a job to a queue
PUSHBBatch push multiple jobs
PUSHFAtomically commit a flow graph
PULLPull next job (supports long poll and locks)
PULLBBatch pull jobs
ACKAcknowledge job completion
ACKBBatch acknowledge
FAILMark job as failed
QueryGetJobGet job by ID
GetStateGet job state
GetResultGet job result
GetJobsList jobs with filtering
GetJobCountsCount jobs by state
GetCountsPerPriorityCount jobs by priority
GetJobByCustomIdLook up job by custom ID
CountQueued job count for a queue
GetProgressGet job progress
GetChildrenValuesGet child job return values
GetQueueLimitsRead live queue rate/concurrency status
GetDeduplicationJobIdResolve a queue-scoped deduplication key
ControlCancelCancel a job
ProgressUpdate job progress
UpdateUpdate job data
ChangePriorityChange job priority
PromoteMove delayed job to waiting
MoveToDelayedMove active job to delayed
MoveToWaitingChildrenPark an active job for children
ChangeDelayChange a delayed job’s delay
MoveToWaitMove a job back to waiting
PromoteJobsPromote all delayed jobs in a queue
DiscardMove job to DLQ
WaitJobWait for job completion
ExtendLockExtend a job lock
ExtendLocksExtend job locks (batch)
RemoveDeduplicationKeyRelease a queue-scoped deduplication key
RemoveJobDeduplicationKeyRelease only a job-owned key
PausePause a queue
ResumeResume a queue
IsPausedCheck if queue is paused
DrainRemove waiting/prioritized/delayed jobs
ObliterateRemove all queue data
CleanRemove old jobs
ListQueuesList all queues
DLQDlqGet DLQ entries
GetDlqStatsGet aggregate DLQ statistics
RetryDlqRetry DLQ jobs
PurgeDlqClear DLQ
RemoveDlqJobPermanently delete one DLQ job
RetryCompletedRe-queue completed jobs
CronCronCreate/update cron schedule
CronDeleteDelete cron schedule
CronListList cron schedules
CronGetGet cron schedule by name
MonitoringPingHealth check
HelloProtocol negotiation
StatsServer statistics
MetricsDetailed metrics
TrimEventsTrim one queue’s lifecycle journal
PrometheusPrometheus-format metrics
StorageStatusGet storage/disk health status
HeartbeatWorker heartbeat
JobHeartbeatJob heartbeat (stall prevention)
JobHeartbeatBBatch job heartbeat
WorkersRegisterWorkerRegister a worker
UnregisterWorkerUnregister a worker
ListWorkersList workers
WebhooksAddWebhookRegister a webhook
RemoveWebhookRemove a webhook
ListWebhooksList webhooks
SetWebhookEnabledEnable/disable a webhook
RateRateLimitSet queue rate limit
RateLimitClearClear queue rate limit
SetConcurrencySet queue concurrency limit
ClearConcurrencyClear concurrency limit
Job groupsGetGroupJobsCount / GetGroupsJobsCountRead grouped backlog
GetGroupActiveCountRead active jobs in one group
SetGroupRateLimit / GetGroupRateLimit / RemoveGroupRateLimitManage one group’s rate override
GetGroupRateLimitTtlRead one group’s fixed-window TTL
SetGroupConcurrency / GetGroupConcurrency / RemoveGroupConcurrencyManage one group’s concurrency override
PauseGroup / ResumeGroup / IsGroupPausedControl and read one group’s pause state
RateLimitGroupInstall an immediate manual group deadline
ConfigSetStallConfigSet per-queue stall config
GetStallConfigGet per-queue stall config
SetDlqConfigSet per-queue DLQ config
GetDlqConfigGet per-queue DLQ config
LogsAddLogAdd job log entry
GetLogsGet job logs
ClearLogsClear job logs
FlowUpdateParentUpdate a child’s parent reference
GetFailedChildrenValuesFailed children values
GetIgnoredChildrenFailuresIgnored children failures
RemoveChildDependencyRemove a child’s parent dependency
RemoveUnprocessedChildrenRemove unprocessed children
DashboardDashboardOverviewAggregated dashboard snapshot
DashboardQueuesAll queues with stats
DashboardQueueSingle queue detail
SystemCompactMemoryTrigger memory compaction
EventsSubscribeEventsStream one queue’s events on this connection
UnsubscribeEventsStop this connection’s event stream
AuthAuthAuthenticate connection