Aileron ControlPlane

Slack

The Slack suite gives your agent three capabilities against the user’s Slack workspace: list channels, search the user’s accessible message history, and post a new message. Post is gated by per-call user approval. Reads run without prompting.

The suite is published from the aileron-connector-slack repo. The connector runs as a sandboxed WASM module talking only to slack.com:443. The user’s OAuth token lives in the Aileron vault and is injected host-side at the network boundary, so the connector code never sees the token bytes.

Posts are made with the authorizing user’s token, not as a bot. Messages appear in Slack authored by the user. This is the right shape for the Monday catch-up demo and for power-user automation generally; it is the wrong shape if you want a dedicated app identity for posts.

Prerequisite

The Aileron Slack app must be installed into your workspace. aileron action add (and add-suite) handles this for you: the CLI opens Slack’s consent screen in your browser, and clicking Allow installs the app and authorizes your user in one step.

If your workspace requires admin approval for third-party apps, Slack shows a “Request to install” button instead of Allow. A workspace admin approves the request, then you re-run the same aileron action add command.

The Aileron Slack app is not on the Slack Marketplace, so there’s no App Directory page to install from. The CLI is the install path.

Install

aileron action add-suite github://ALRubinger/aileron-connector-slack/suite.toml@latest

Individual actions

Pick whichever actions you actually want exposed to the agent.

aileron action add github://ALRubinger/aileron-connector-slack/actions/list-channels@latest
aileron action add github://ALRubinger/aileron-connector-slack/actions/search-messages@latest
aileron action add github://ALRubinger/aileron-connector-slack/actions/send-message@latest

Actions

list-channels

Lists the Slack channels the user has access to in the workspace. Each entry carries id, name, topic, purpose, is_member, and the rest of Slack’s conversations.list envelope. By default only public channels are returned.

InputRequiredDescription
limitnoPage size. Default 100, max 1000.
typesnoComma-separated channel types: public_channel, private_channel, mpim, im. Default public_channel.
cursornoPagination cursor returned from a previous call’s response_metadata.next_cursor.
exclude_archivednoSkip archived channels. Default true.

Idempotent. Read-only. No approval prompt.

search-messages

Searches the user’s Slack message history using Slack’s native search syntax. Returns Slack’s search.messages envelope; the messages.matches array carries the hits with text, user, username, channel, ts, and permalink.

InputRequiredDescription
queryyesSlack search query. Supports from:@user, in:#channel, before:YYYY-MM-DD, after:YYYY-MM-DD, has:link, has:reaction. Example: from:@alice deploy in:#release-notes after:2026-04-01.
countnoResults per page. Default 20, max 100.
sortnoscore (relevance, default) or timestamp (newest first).
pageno1-based page index. Default 1.

Idempotent. Read-only. No approval prompt.

search.messages is one of Slack’s user-token-only APIs. The connector’s OAuth dance requests the search:read user scope so this action works; if the user denies that scope at consent, the action returns a missing_scope error.

send-message

Posts a message to a Slack channel as the authorizing user. The user is prompted to approve the channel and exact message text before Slack receives the post.

InputRequiredDescription
channelyesChannel id (e.g. C0123456) or channel name (e.g. #general). DM ids (Dxxxx) and group ids (Gxxxx) are also accepted by Slack.
textyesThe message body to post. The user will be asked to approve this exact text before Slack receives it.
thread_tsnoParent message timestamp (e.g. 1683750000.000100) to reply in thread. Omit to post a top-level message.

Approval-gated (ADR-0009). The runtime asks the user via the launch-comms channel before Slack is contacted; on denial nothing is sent. Not idempotent: invoking twice posts two messages. Slack has a brief edit window but no retraction, and notifications and thread bumps fire on the original post, so approve-before-dispatch is the only safe model.

Slack’s OAuth consent screen names the connector publisher (ALRubinger) and the requested user-token scope set:

ScopeUsed by
chat:writesend-message. Posts appear as the authorizing user.
channels:readlist-channels.
users:readsearch-messages. Resolves user ids to display names in hits.
search:readsearch-messages. User-token-only; this is what forces the OAuth dance to issue a user token rather than a bot token.

Per ADR-0002’s OAuth section, the consent screen is a contract between the user and the entity identified on it. The user is granting these scopes to the connector publisher’s Slack app, not to Aileron itself.

Error classes

The connector emits structured errors per ADR-0010. Slack returns most logical failures as HTTP 200 with {"ok": false, "error": "<code>"}, so the connector translates both transport-level and in-body errors into the same class set.

ClassWhenWhat the user sees
unauthorizedHTTP 401, or ok=false with error not_authed, invalid_auth, token_revoked, account_inactive, or token_expired.The bound OAuth token is rejected. Re-run aileron binding setup github://ALRubinger/aileron-connector-slack to re-consent.
missing_scopeok=false with error missing_scope or not_allowed_token_type.The OAuth grant is missing a scope the action requires. Re-run aileron binding setup to grant the missing scope.
rate_limitedHTTP 429 or ok=false with error ratelimited.Slack is throttling. The runtime’s retry layer will respect Slack’s Retry-After header for transient backoff.
external_api_errorAny other Slack-reported error.Body is included for the agent and the audit log.
connector_runtime_errorMalformed input, unparseable response, or a missing required arg.Generic; check the action’s required inputs.

See also