Moving to a gateway
One gateway holds every inbound transport for a fleet of instances — the GitHub and Jira webhooks, one Slack Socket Mode connection per app, the pollers — and each instance polls its own queue (the capability, issue-496). This page is the move: an instance that today runs its own poller or Slack listener, put behind a gateway. Nothing migrates by itself, because the gateway is opt-in: a cli-config.yaml without a gateway block behaves exactly as before, and no key was renamed or removed.
When you want one
- Two or more instances read one Slack app. Slack splits an app's envelopes across its open connections, so two listeners each hear half; the gateway holds the one connection.
- A GitHub webhook should reach several instances, and only one URL can be registered.
- No inbound webhook is possible at all (an enterprise network), so one process must hold the long-lived connections and poll on the fleet's behalf.
One instance on one machine gains nothing from a gateway; leave it as it is.
The move
- Write
gateway-config.yamlbeside the CLI config of the machine that will run the gateway./the-loop:init-gatewaywalks you through it and scaffolds the file; by hand, start fromtemplates/gateway-config.yamlin the plugin. (/the-loop:init-cliasks "will this instance read its events from a gateway?" for each instance's owngatewayblock, and points you at/the-loop:init-gatewaywhen you say the gateway runs here.) Every secret is an environment-variable name (*Env) read fromenv.file: the GitHub poll token (read access only), the Slack app-level token and bot token. They move to the gateway, which spawns nothing and holds no GitHub or Jira posting token; with the bot token it holds it also posts to Slack for the instances (slack.post.enabled, on by default — issue-519), so an instance behind it needs no Slack token of its own. List the repositories (repositories). Slack collaboration channels need no entry: each instance publishes the rooms its work items declare (the-loop add-channel), and keeps them current (issue-505).slack.roomsis only for a channel that no instance declares. The gateway's own GitHub and Jira pollers are off unless you turn them on (github.poll.enabled,github.poll.sources): your instances keep polling as they do today. - Start it:
the-loop gateway start. It validates the file, refuses to start on a violation naming each key, and listens on127.0.0.1:8790by default. A non-loopbacklisten.hostneedslisten.exposed: true— there is no instance authentication in this release, so the network is the boundary (decision-146). - Point each instance at it. Add the
gatewayblock to the instance'scli-config.yaml—url, andactorsfor the users this instance works for — give the instance a distinctinstance.name(a second peer under a live name is refused with409), setchannels.slack.read.mode: gatewayif the instance uses Slack, andthe-loop restart. Its webhook receiver and its poller keep running exactly aswebhooksandpollingsay; only its Slack listener (or poll reader) stops, andthe-loop statusreadsoff · gateway configuredfor it. Withread.mode: gatewaythe Approve, Execute and Start buttons, the kickoff picker and the slash command all keep working through the gateway, and the instance never opens a Socket Mode connection itself. A 20.3.0 config withmode: sockethere still works, andthe-loop migrate-configrewrites it togateway(issue-503). To leave the gateway, remove the block and setread.modeback tosocketorpoll. Agatewaymode without a block reads nothing, on purpose, so that leaving cannot silently open a second socket on the gateway's app. - Re-point a GitHub webhook, if you had one, at the gateway's
/gh-webhookwith the same secret: the contract is GitHub's verbatim. - Slack reads over Socket Mode only. Behind a gateway there is no poll reader, so a reply in a bound thread is input only when it mentions the bot (the issue-389 rule), and the app must hold
app_mentions:readand subscribe toapp_mention. An app installed before issue-389 lacks both:/the-loop:upgrade-the-loopregenerates the kept manifest and says when to re-apply it and reinstall, or runthe-loop channels manifest --writeyourself;the-loop channels status --probelists the granted scopes. - Check:
the-loop gateway status(ingresses, the Socket Mode state, live instances),the-loop gateway instances(each instance's lease, last fetch and backlog) and, on an instance, the Gateway card of the dashboard orGET /api/v1/gateway.
What does not change
- The instance still spawns, posts, applies control commands and judges authorization; a forged event through the gateway is judged at the instance as it always was.
- Delivery ids are the ones the instance poller produced (
poll-comment-<id>,poll-presence-<ref>-<uuid>), so the instance's dedup caches keep working. - A running work item keeps running: your instance's poller still reads its ticket. If you do turn the gateway's poller on, it baselines an item's existing thread at first sight, as the instance poller does, so a history is never replayed as prompts; the one edge is a
the-loop startposted before the gateway first listed the item, which is baselined too (the gateway runs no control grammar), so post it again. - The gateway being unreachable stops nothing on the instance: it keeps running, retries with back-off and never falls back to its own ingresses.
Who polls
Each instance keeps its own GitHub and Jira pollers behind a gateway; that is the P0 of the first landing, so a fleet never funnels every work item's polling through one process. An instance that polls a repository itself and receives that repository's webhooks through the gateway sees a comment by both paths, exactly as an instance running its own receiver beside its own poller does today, so pick one per repository. Slack is the gateway's alone: one app has one Socket Mode connection.