Connect providers

Slack

Read channels, post messages, manage users — every call gated by Cedar + audit.

Slack OAuth is workspace-scoped. One Connection = one workspace.

Before you start

  • A Slack workspace where you can install apps (or workspace admin can approve).

Connect

  1. 1
    Open the picker

    ConnectionsConnectSlack.

  2. 2
    Pick workspace + approve

    Slack asks which workspace, then shows the scope list: channels:read, channels:history, chat:write, users:read, team:read.

    Slack OAuth consent screen
    Slack scopes Nomos asks for. Narrow further with Cedar.
  3. 3
    Verify

    Back in Connections: workspace name shows, refresh token marked yes. Slack refreshes every 12h on Nomos's hourly sweep.

Commands

  • /slack/channel/list, /slack/channel/info, /slack/channel/history
  • /slack/message/post, /slack/message/reply, /slack/message/update, /slack/message/delete (step-up)
  • /slack/user/list, /slack/user/get_by_email, /slack/user/info
  • /slack/dm/open, /slack/reaction/add, /slack/reaction/remove
  • /slack/pin/add, /slack/topic/set
  • /slack/search/messages (admin-only)

Starter policies

  • slack:read-only — channel listing, history, user lookup. No posts.
  • slack:safe-default — read + post to channels matching a pattern. Delete requires step-up. Useful for a notification bot.
  • slack:channel-pinned — narrow to one channel. Sandbox-friendly.

Cedar fragment

cedar
permit (
  principal,
  action in [Action::"/slack/channel/history", Action::"/slack/message/post"],
  resource
) when {
  resource.channel == "C0123ABC"  // only #ops
};

forbid (
  principal,
  action == Action::"/slack/message/delete",
  resource
) when { !context.cosigner };

Operational gotchas

  • Bot can't post in a channel+
    Slack requires the bot to be invited to private channels. `/invite @nomos-bot` in the channel.
  • Rate limits hit+
    Slack tier-2 rate limits (50 RPM per workspace). Nomos respects retry-after; your agent should not retry-on-deny.
  • Want to attribute posts to a real user, not a bot+
    Use a user-token connection instead of bot-token. Re-connect via Slack with `user_scope=…` when prompted.