Configuration
All delivery queue settings live under thedeliveryQueue key in your YAML
config file. Every field has a sensible default, so an empty section enables
the queue with production-ready settings.
Fields
Enable or disable the delivery queue. When disabled, a no-op adapter is used
and messages are sent without persistence. Useful for development or when
persistence overhead is not wanted.
Maximum number of entries allowed in the queue. Enqueue is rejected when this
limit is reached, preventing unbounded growth. In practice this limit should
rarely be hit — it exists as a safety valve.
Maximum number of delivery attempts before an entry is marked as permanently
failed. Each attempt uses the backoff schedule (5s, 25s, 2m, 10m, 10m cap)
to space retries.
Time-to-live for queue entries in milliseconds (default: 1 hour). Entries
older than this are pruned automatically. This limits the duplicate delivery
window after a crash — stale messages are dropped rather than re-sent.
Whether to re-deliver pending entries when the daemon starts. This is the
core crash-recovery mechanism: entries that were enqueued but not yet acked
are retried on the next startup.
Wall-clock budget for the startup drain cycle in milliseconds (default: 60
seconds). If draining takes longer than this, remaining entries are left
for future drain cycles. Prevents blocking daemon startup.
Interval for periodic cleanup of expired entries in milliseconds (default: 5
minutes). The prune timer runs on an unref’d interval so it does not prevent
process exit.
Delivery semantics
The delivery queue provides at-least-once delivery. Every message is persisted before the send attempt and only removed after platform confirmation. This means:- If the daemon crashes between enqueue and ack, the message is re-delivered on restart (no message loss).
- If the daemon crashes after the platform accepted the message but before
acking the queue entry, the message may be delivered twice. The
defaultExpireMsTTL limits the window for duplicates. - Platform-level deduplication (where available) further reduces duplicate impact.
Monitoring
Queue lifecycle events are emitted on the typed event bus and logged with structured fields. Key log lines to monitor:| Event | Level | What it means |
|---|---|---|
Message enqueued for delivery | INFO | Message entered the queue |
Message delivered and acked | INFO | Platform confirmed delivery |
Message delivery failed, scheduled for retry | WARN | Transient failure, will retry |
Message delivery permanently failed | WARN | Non-retriable error (chat not found, bot blocked) |
Delivery queue startup drain complete | INFO | Startup drain finished |
Delivery cancelled by before_delivery hook | INFO | A before_delivery hook returned cancel; the message was not sent |
Delivery aborted | INFO | Mid-delivery cancellation via abort signal (partial chunks may have shipped — see chunksDelivered / totalChunks) |
delivery:enqueued, delivery:acked, delivery:nacked, delivery:failed,
delivery:queue_drained, delivery:hook_cancelled, delivery:aborted.
Key fields to watch in your log aggregator:
durationMson acked events: delivery latency per message.entriesAttempted/entriesDeliveredon drain events: crash recovery volume.errorKind:transient(retry scheduled) vspermanent(message dropped).
Disabling the queue
To revert to fire-and-forget delivery (no persistence), setenabled: false:
