Skip to content

Approve from Slack and Teams

llms.txtlists every page for an agent
Optional: hand this page to your coding agentThe steps work by hand too.
Show the whole prompt
Read https://intentius.io/terragucci/guides/approve-from-chat/.
Add `relay: terragucci` under `notify:` in terragucci.yml, and `apply.resume` if it is missing, run `npx terragucci config check` and `npx terragucci init`, and open a pull request.
Tell me which chat ids each approver needs on their line, and which token, secrets and Slack or Teams settings I must create; do not edit the signers file yourself, and do not create a token, a secret, a Slack app or a Teams webhook.
Never apply, approve (a pull request review or `terragucci approve`), override a policy denial (`terragucci override`), use `--mode apply`, or merge; never touch `.chant/allowed_signers` or `chant/lifecycle`.

A waiting wave’s Slack message has an Approve and a Decline button. A Teams card gives the approving reply, @terragucci approve wave-2 <digest>, since Teams sends card buttons only to a registered bot. Clicks and replies go to terragucci relay, which you run in your own cloud; terragucci hosts nothing.

A click by Under approval: ledger or pr-review Under approval: sealed
a chat user the signers file lists beside their principal the relay records an approval of the digest the message showed, as that principal and marked relayed, and says so in the thread; the resume job applies the wave refused: only the approver’s own key seals an approval, so they run terragucci approve --sign
a chat user no line lists refused in the thread; nothing is recorded the same
anyone, after the plans moved refused, naming the digest waiting now; nothing is recorded the same

Decline records nothing; the relay names who declined in the thread, and the wave waits until someone approves its plans.

You need Why
Chat notices for the channel the buttons go on those messages
apply.resume the relay starts no job, so the resume job applies the wave once the approval stands
A signers file, .chant/allowed_signers, on the default branch the relay maps a chat user to a principal there
A place to run a container, or a function the relay runs in your cloud
  1. Add each approver’s chat ids beside the principal on their line of .chant/allowed_signers, then merge the change. The relay reads the file on the default branch for every click.

    alice@acme.com,slack:T0123ABCD/U0456EFGH,teams:6f1c2d0e-1a2b-4c3d-8e9f-0a1b2c3d4e5f ssh-ed25519 AAAAC3Nz...
    Chat The id Where to find it
    Slack slack:<team id>/<user id> the user’s profile, Copy member ID; the workspace’s team id is in its settings
    Teams teams:<Entra object id> the user’s Object ID in Microsoft Entra

    The approval records the line’s first principal that is not a chat id. A chat id listed on two people’s lines maps to neither.

  2. Make the relay’s token. It is approve-only: the relay refuses to start with a token that can do more than read the repo and push chant/lifecycle.

    A fine-grained token for this repo alone, with Contents: Read and write and nothing else, from an account that does not administer the repo. The relay refuses a classic token, and the default branch must be protected.

  3. Set up the chat side.

    Turn on Interactivity in the Slack app that holds the channel’s incoming webhook, and set its Request URL to <relay address>/slack. Copy the app’s Signing Secret.

  4. Run the relay. It reads its settings from its environment.

    Any container platform, such as ECS, Cloud Run, Azure Container Apps or Kubernetes, behind HTTPS:

    Terminal window
    docker run -p 8080:8080 \
    -e TERRAGUCCI_RELAY_REPO=https://github.com/acme/infra.git \
    -e TERRAGUCCI_RELAY_TOKEN -e SLACK_SIGNING_SECRET -e TEAMS_WEBHOOK_SECRET \
    ghcr.io/intentius/terragucci-tofu:<the tag init names> terragucci relay

    The startup log names the repo and default branch, then the user the relay pushes as. A token that can do more than approve stops the relay there, and the log gives the reason.

  5. Set notify.relay in terragucci.yml and regenerate the pipeline, then merge:

    apply:
    resume: 5
    notify:
    slack: SLACK_WEBHOOK_URL
    relay: terragucci
    Terminal window
    npx terragucci init

    relay is the outgoing webhook’s name in Teams, not a secret. The next waiting wave’s message carries the buttons.

Check Refused when
The request’s signature: Slack’s signing secret over the timestamp and body, or the Teams outgoing webhook’s HMAC over the body it does not verify, or a Slack request is more than five minutes old (401)
The chat user no line of the signers file on the default branch lists them
The mode the default branch’s approval is sealed
The digest no wave waits for the digest the button or reply names

Slack waits three seconds for the relay to answer a click. If fetching the repo and pushing the approval take longer, Slack shows a timeout, but the thread reply still says what was recorded.

The approval line carries relayedBy and via: slack or via: teams, and the audit trail shows who relayed it. The threat model says what the relay is trusted with.

terragucci

These docs count page views and clicks with PostHog. They set no cookies, store nothing in your browser, and send nothing when your browser asks not to be tracked.