Integrations options
Options under integrations — how the-loop's own calls reach external services.
Control plane only
This governs what the daemon does: posting a reaction, announcing a session, notifying a channel, reading an item's state. What the agent does from inside its session is unconstrained — CLI, MCP, API, whatever the harness has. Nothing here narrows the agent.
Introduced by issue-109 to replace three copies of one setting: ghBinary used to be declared separately under routing.control, routing.reactions and routing.announce. It is now declared once here and fanned out internally. A config still carrying the old key is refused, naming the replacement — see the-loop migrate-config.
integrations:
github:
transport: auto
cli: { binary: gh }
api: { tokenEnv: [GITHUB_TOKEN], baseUrl: "" }
slack:
transport: sdk
urlEnv: THE_LOOP_SLACK_WEBHOOK_URL
# url: https://hooks.slack.com/services/… # takes precedence; commits the credentialChoosing a transport
Transport is a choice, not a mandate:
autoresolves token → binary, and fails closed naming both remedies if neither is available. It never guesses silently.- An explicit transport is honoured verbatim and fails rather than degrading. If you asked for
apiand the token is missing, you get an error — not a quiet fallback to a CLI that might be authenticated as somebody else.
GitHub
github.transport
- Type:
'auto' | 'api' | 'cli' - Default:
auto
How GitHub calls are made:
| Value | Means |
|---|---|
api | stdlib HTTP with a token from github.api.tokenEnv |
cli | the operator's authenticated gh, inheriting enterprise and SSO settings |
auto | token first, then binary; fails closed naming both remedies |
github.api.tokenEnv
- Type:
string[] - Default: none
Environment variables holding a token, tried in order.
Variable names, never tokens
This is a list of variable names. Putting a token in this file commits it.
github.api.baseUrl
- Type:
string - Default: none (github.com)
API base URL — set it for a GitHub Enterprise host.
github.cli.binary
- Type:
string - Default:
gh
Path or name of the gh CLI. One declaration, used by every feature that shells out to GitHub: control-command paper-trail comments, dispatch reactions, session announcements, and the GitHub poll provider.
This is the key that replaced the three ghBinary declarations.
Slack
slack.transport
Type:
'auto' | 'sdk' | 'webhook'Default:
sdksdk— the officialslack-sdk, a required dependency since the extras were removed (owner decision, PR #162), so it is always present.webhook— a raw POST to an incoming-webhook URL, using nothing but the standard library.
slack.urlEnv
- Type:
string - Default:
THE_LOOP_SLACK_WEBHOOK_URL
Environment variable holding the incoming-webhook URL. The default source, and the one to keep where this file is shared or public.
It is a credential
A Slack incoming-webhook URL is the credential — anyone holding it can post to your channel. Keeping it in the environment keeps it out of git.
slack.url
- Type:
string - Default: none —
urlEnvis read instead
The incoming-webhook URL itself, for an operator who has judged it non-secret (a personal channel in a private workspace, say). Takes precedence over urlEnv, so the effective configuration never depends on ambient environment: reading this file tells you where a notification goes.
integrations:
slack:
transport: sdk
url: https://hooks.slack.com/services/XXX/YYY/ZZZSetting this commits the credential
A webhook URL is a bearer credential — for one channel, with no read access and no workspace scope, but a credential. Put it here and it lands in git history, where deleting the line does not remove it; disclosure is fixed by deleting the webhook in Slack, not by editing the file. Prefer urlEnv unless you have decided otherwise on purpose.
The reason the key exists is the failure mode it removes. With env-only configuration, the variable has to be present in every process that might deliver a notification — the poll daemon (which inherits it from whichever shell ran poll start), the harness sessions that daemon spawns, and every fresh machine's provisioning. Restart the daemon from cron, systemd or a new SSH session without the export and notifications stop with nothing louder than a log line, because notify is best-effort by contract. An empty url: is treated as absent and falls back to urlEnv, so a blank key cannot silently disable a working setup.
When neither source resolves, the error names both:
slack has no webhook url — set integrations.slack.url in the CLI config,
or export THE_LOOP_SLACK_WEBHOOK_URLJira
jira.transport
- Type:
'auto' | 'api' | 'cli' - Default:
api
How Jira calls are made. Same semantics as GitHub's.
jira.api.baseUrl
- Type:
string - Default: none
Jira API base URL, e.g. https://your-org.atlassian.net.
jira.api.tokenEnv
- Type:
string - Default: none
Environment variable holding the Jira API token. A name, not a token.
jira.cli.binary
- Type:
string - Default:
jira
Path or name of the Jira CLI.
Next
- Observability options — the event log and who gets notified.
- Routing options — the features that use these transports.