Connectors

A connector is a provider definition that OutcomeCI ships: the API's base URL, the kinds of credential it accepts, the operations a step can be granted, and the rules no grant can override. A workflow binds a connector to a secret under apis:

secrets:
  github: vault:github
 
apis:
  github: {uses: github, auth: secrets.github}

The connector never holds a secret or makes a request itself. The runtime's broker sends every call, authenticates it with the credential, enforces the step's grants, and records the call in the run's journal. The agent sees an operation name, its inputs, and the projected result, never a secret or a token.

Two connectors are available: slack and github. Each workflow revision records the exact connector definition it compiled against, so a run always uses the operations its revision was built with.

Accepted credentials

auth: secrets.<name> never says how to authenticate: the Vault credential does. Each connector lists the kinds its API accepts and fills in what the provider fixes, such as its token endpoint:

Connector Credential Store it as
github Personal access token, classic or fine-grained A plain value
github GitHub App installation app_installation with --app-id, --installation-id, and the app's private key
slack Bot token (xoxb-) A plain value
slack Slack app with token rotation oauth2 with --client-id, --grant-type refresh_token, and the client_secret and refresh_token

A GitHub App installation signs a short-lived JWT with the app's key and exchanges it for an installation token. A Slack app with token rotation exchanges its refresh token for a 12-hour bot token; Slack issues a new refresh token each time, and the runtime saves it back to the Vault before the run continues. Both tokens are reused until they expire.

A credential the connector does not accept fails before any request, naming the connector, the Vault entry, and the kinds it accepts.

Request a connector

New connectors arrive through GitHub issues on outcomeci/connectors. Open an issue that names the API, the operations your workflow needs, and how the API authenticates. The pull request that answers it follows the connector authoring guide, which people and coding agents both use: it defines the operations, the grants and refusals, the credential kinds the API accepts, and the tests a connector needs before it ships.

GitHub

uses: github calls https://api.github.com with a personal access token or a GitHub App installation token. A step can send up to 100 GitHub requests.

Operation What it does Grant arguments
read Sends a GET to any path under the GitHub REST API, such as /repos/{owner}/{repo}/contents/{path} repo
write Reads, creates branches, commits files, and opens pull requests with GET, POST, PUT, and PATCH repo

A repo grant limits every request to paths under /repos/{owner}/{name}. Write it as a literal owner/name or as a reference to an {owner, name} object, such as trigger.repo.

write always refuses, whatever the grant:

  • changes outside a repository;
  • repository settings and administration, such as collaborators, hooks, keys, Actions, environments, and rulesets;
  • branch protection;
  • merging a pull request or branch; and
  • moving an existing branch.

Slack

uses: slack calls the Slack Web API with a bot token, or with the token a Slack app with token rotation issues. A step can send up to 50 Slack requests.

Operation What it does Grant arguments
post Posts one message to a channel, or a thread reply with thread_ts. Returns the message's channel, ts, and thread_ts channel, thread_ts
thread Reads a message's thread: the root message and every reply channel
reactions Reads the reactions on one message channel
file Opens one file shared in a conversation, such as a screenshot channel

file downloads the file with the same credential, only from files.slack.com, and saves it with the run's artifacts. It returns the file's name, mimetype, size, and a local file.path the agent can open. Files larger than 2 MiB are refused. A channel grant on file is checked against the response: the file must be shared in that channel.

Grant operations to a step

A step's can: lists the operations it may call. A bare entry grants the operation with no argument rules:

can:
  - github.read

A mapping fixes grant arguments on every call:

can:
  - slack.post: {channel: trigger.channel, thread_ts: trigger.ts}
  - github.write: {repo: target.repo}

An argument is a literal or a reference to data the step can read. The broker enforces it on every call, fills it in when the agent leaves it out, and refuses a call that names something else. When a step is granted the same operation twice, name each grant with as so later steps can tell the calls apart:

can:
  - slack.post: {channel: build, as: plan_post}

A later step reads the recorded call as <step>.calls.plan_post, or the last call of an operation as <step>.calls.slack.post.

Add rules a grant cannot express

Grant arguments are exact. For rules about intent, such as "one branch, one pull request, no force-push", add a policy to the step:

- fix:
    reason: fix-and-open-pr.md
    can:
      - github.write: {repo: triage.repo}
    policy: "One new branch from the default branch, one PR, only files the fix needs. No force-push."

A reviewer checks each call that changes something against the policy before the broker sends it. Reads are not reviewed. See Review calls with a policy.

Wait for people

The Slack connector defines two watchers, which await and converse steps use to wait for a person:

Watcher Written as Waits for
reaction await: {slack.reaction: {...}} An emoji reaction on a recorded message, optionally from one person
reply converse: slack.thread(<recorded message>) Human replies in the message's thread; bot messages are ignored

The runtime owns the polling, the timeout, and the record of what it saw. See await steps and converse steps.

Start runs from provider events

The Slack connector also has a receiver, which a webhook trigger uses to verify Slack's signed Events API requests:

trigger:
  webhook: {uses: slack, auth: secrets.slack_signing, events: [mention, dm]}
Event Starts a run for
mention A top-level message that mentions the app, in a channel it is in
dm A top-level direct message to the app

The receiver answers Slack's URL verification handshake, rejects requests whose signature does not match or whose timestamp is more than five minutes old, and deduplicates redeliveries by Slack's event ID. Thread replies do not start runs. See Webhooks.

oci integration slack setup creates a Slack app with only the scopes the operations use and an event subscription to your workflow's webhook URL. See the CLI reference.