The substrate for running background jobs: how traffic is split by load and which processes chew through it. The queue payload lives in redis-durable; a job's path inside the queue (heartbeat, retries, cleanup) is on the job lifecycle.
Grouped by the nature of the job: we separate what contends with each other — fast from heavy, bulk mailings from transactional mail (so a campaign doesn't delay an invite or a password reset), and agents (which hit the LLM provider's rate limit) apart from live traffic.
agent_max_concurrency separate from chat
The agents lane keeps its own cap on concurrent LLM calls, so
background work doesn't eat the provider's rate limit out from under live
chat.
command (saq configured for its lane).
Three worker services with different concurrency; the API is a
separate service.
agent_max_concurrency| Queue | Lane | Consumer | Nature of jobs |
|---|---|---|---|
| Harvester sync | background | Harvester | full · incremental · partial-resync · dlq-retry · reconcile · health |
| Email · transactional | interactive | invite · reset (test email — inline, bypasses the queue) | |
| Email · bulk mailing | background | bulk campaigns — by volume, not weight; separated so they don't crowd out transactional mail | |
| KS background runs | background | Knowledge Store | ingest · re-embedding · graph curation · backup — bounded concurrency (online search calls the runtime directly, bypassing the queue) |
| Agent runs | agents | Agent Engine | runs under the cap on concurrent LLM calls |
| Notifications · external delivery | interactive | Notifications | email + webhook POST (in-app is written synchronously to Postgres, bypassing the broker) |
| Slack bot · inbound | interactive | Slack | DM event → Query Engine → post back to the thread (ack<3s on receipt, dedup by event_id) |
| Telegram bot · inbound | interactive | Telegram | DM update → Query Engine → reply as a message (received by webhook, dedup by update_id) |
| Mattermost bot · inbound | interactive | Mattermost | DM post → Query Engine → reply in the thread (no webhook — enqueued by the scheduler's WebSocket listener, dedup by post id) |
job_id —
a repeat call spawns no duplicate; the mechanics are on
job uniqueness.
pub/sub — the push-notification signal:
a new feed row fans out across the api replicas, and the notification
bell updates instantly. The channel is ephemeral
(redis-cache);
the source of truth is Postgres, and polling stays a background top-up.