Skip to content

jeremylongshore/claude-code-slack-channel

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

305 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

claude-code-slack-channel v0.11.0

The Slack-native governance substrate for Claude Code — the kernel other governance tooling builds on. Humans, Claude Code sessions, and peer agents converse safely in shared channels: every tool call passes through a declarative, tier-aware policy engine; every decision lands in a hash-chained, Ed25519-signed audit journal you can verify offline. Per-thread session isolation, identity-aware permission gates, operator admin commands with cross-channel approval, peer-bot loop control, and defense-in-depth against prompt injection. The policy/journal/relay kernel here is the substrate that the companion agent-governance-plane (AGP) vendors for cross-runtime governance.

CI License: Apache 2.0 OpenSSF Scorecard

Links: Roadmap & Scope · Gist One-Pager · GitHub Pages · Release Notes

Research Preview — Channels require Claude Code v2.1.80+ and claude.ai login.

How It Works

Slack workspace (cloud)
    ↕ WebSocket (Socket Mode — outbound only, no public URL)
server.ts (local MCP server, spawned by Claude Code)
    ↕ stdio (MCP transport)
Claude Code session

Socket Mode means no public URL needed — works behind firewalls, NAT, anywhere.

Quick Start

For AI-assisted setup: run /slack-channel:install and your AI assistant will walk you through every step below. The install skill also supports doctor (health check), verify (round-trip test), repair (auto-fix), manifest (one-click Slack-app import), reset, tour, and uninstall modes — see skills/install/SKILL.md.

Prerequisites

Before step 1, confirm you have:

  • Bun ≥ 1.0 — install with curl -fsSL https://bun.sh/install | bash. Node.js / Docker fallbacks are documented in Option B / Option C below.
  • Claude Code ≥ v2.1.80 — see https://docs.claude.com/claude-code/install for upgrade.
  • claude.ai login — this is a Research Preview constraint. API-key-only auth (ANTHROPIC_API_KEY set with no claude.ai session) does NOT work for Channels. Run claude login to complete the browser flow.

1. Create a Slack App

  1. Go to api.slack.com/appsCreate New App → From scratch
  2. Socket Mode: Settings → Socket Mode → Enable → Generate App-Level Token (xapp-...) with connections:write scope
  3. Event Subscriptions: Enable → Subscribe to bot events:
    • message.im — DMs
    • message.channels — public channels
    • message.groups — private channels
    • app_mention — @ mentions
  4. Bot Token Scopes (OAuth & Permissions):
    • chat:write — send messages
    • channels:history — read public channels
    • groups:history — read private channels
    • im:history — read DMs
    • reactions:write — add reactions
    • files:read — download shared files
    • files:write — upload files
    • users:read — resolve display names
  5. Install to Workspace → Copy Bot Token (xoxb-...)

2. Configure Tokens

/slack-channel:configure xoxb-your-bot-token xapp-your-app-token

3. Run

Pick your runtime:

Option A: Bun (recommended)

bun install
# Current (claude-code-plugins marketplace):
claude --channels plugin:slack-channel@claude-code-plugins
# Future (after upstream approval):
# claude --channels plugin:slack-channel@claude-plugins-official

Option B: Node.js / npx

npm install
# In .mcp.json, change command to: "npx", args: ["tsx", "server.ts"]
claude --channels plugin:slack-channel@claude-code-plugins

Option C: Docker

docker build -t claude-slack-channel .
# In .mcp.json, change command to: "docker", args: ["run", "--rm", "-i", "-v", "~/.claude/channels/slack:/state", "claude-slack-channel"]
claude --channels plugin:slack-channel@claude-code-plugins

3.5. Add the bot to your Slack channel

This step is the most common silent failure. Slack installs apps to the workspace without joining any channels. The pairing DM in step 4 works because DMs auto-route, but a channel test message in step 5 will hit silence — the bot literally isn't in the channel to see the event.

  1. Open Slack → navigate to the channel where you want the bot to live
  2. Click the channel name at the top → Integrations tab
  3. Click Add an App → select your bot
  4. Confirm: the bot now appears in the channel's member list

Private channels need explicit invitation. Repeat per channel if the bot needs to be in more than one.

4. Pair Your Account

  1. DM the bot in Slack — you'll get a 6-character pairing code
  2. In your terminal: /slack-channel:access pair <code>
  3. You're connected. Chat away.

5. Verify it's working

In the channel where you added the bot in step 3.5, send:

@<bot-name> hello

The bot should reply within 10 seconds. If you get silence, run /slack-channel:install doctor for a structured diagnosis, then /slack-channel:install repair to auto-fix what's fixable. See Troubleshooting for the top failure modes.

Troubleshooting

The five silent-failure modes that cover ~95% of fresh-install issues:

  1. Bot is not in the channel — see step 3.5 above. Channel test messages hit silence because the bot can't see the event.
  2. Claude Code version too old — run claude --version; need ≥ v2.1.80.
  3. claude.ai login missingANTHROPIC_API_KEY alone is not accepted (Research Preview constraint). Run claude login.
  4. Bun not installedbun: command not found. Install with curl -fsSL https://bun.sh/install | bash or use the Node.js fallback in Option B.
  5. Wrong file permissions on .env — must be 0600. Run chmod 0600 ~/.claude/channels/slack/.env, or run /slack-channel:install repair.

Full troubleshooting matrix (10 failure modes): skills/install/references/troubleshooting.md.

Policy Engine (v0.6.0+)

Author rules in access.json.policy to automate permission decisions for Claude Code tool calls. Three rule effects, evaluated strictest-tier-first then first-applicable within a tier (detailed below):

{
  "policy": [
    {
      "id": "safe-reads-in-ops",
      "effect": "auto_approve",
      "match": { "tool": "Read", "channel": "C_OPS_DOCS" }
    },
    {
      "id": "no-shell",
      "effect": "deny",
      "match": { "tool": "Bash" },
      "reason": "Shell execution is not permitted."
    },
    {
      "id": "dangerous-writes",
      "effect": "require_approval",
      "match": { "tool": "Write", "channel": "C_DEPLOY" },
      "approvers": 2,
      "ttlMs": 300000
    }
  ]
}
  • auto_approve — skip the Block Kit prompt; the tool call runs immediately and a policy.allow event is journaled.
  • deny — the reason is posted back into the originating thread and the call is rejected. policy.deny is journaled.
  • require_approval — route through human approver(s). approvers: 2 requires two distinct Slack user_ids (NIST two-person integrity; the same user cannot double-satisfy quorum by clicking twice). A single deny from any allowlisted user rejects the request immediately regardless of quorum count.
  • Successful approvals grant a TTL window scoped to (rule, channel, thread) so a chain of similar calls doesn't re-prompt.
  • Parse errors in access.json.policy are fatal at boot — policy is safety-critical, silent degradation is not offered. Missing or empty policy is fine (first-install path).

Tier-aware evaluation: rules carry a tier (adminuserworkspacedefault). Evaluation is strictest-tier-wins — a higher tier's deny cannot be overridden by a lower tier's auto_approve, so a workspace-level guardrail can't be relaxed by a per-user rule. Within a tier, first-applicable ordering holds.

When a call is denied, the result returned to Claude is context-stripped to a bare { behavior: 'deny' } — the rule id and reason are journaled and posted to the thread, but not handed back to the model, so a denied tool call can't be reverse-engineered into a rephrase-and-retry loop.

Full schema reference: ACCESS.md. Decision procedure: 000-docs/policy-evaluation-flow.md. Release scope and what was deliberately deferred: 000-docs/v0.6.0-release-plan.md.

Access Control

See ACCESS.md for the full schema.

/slack-channel:access policy allowlist       # Only pre-approved users
/slack-channel:access add U12345678          # Add a user
/slack-channel:access remove U12345678       # Remove a user
/slack-channel:access channel C12345678      # Opt in a channel
/slack-channel:access channel C12345678 --mention  # Require @mention
/slack-channel:access status                 # Show current config

Multi-agent coordination

Channels can opt in to cross-bot message delivery by listing trusted bot user IDs in allowBotIds. Useful when multiple Claude Code instances (or other bots you operate) need to coordinate in a shared channel — e.g., an ops-monitor agent and an engineering agent in #incidents. Default is no cross-bot delivery: every bot message is dropped at the gate. See ACCESS.md for the full schema and security tradeoffs.

Example access.json entry:

{
  "channels": {
    "C_INCIDENTS": {
      "requireMention": false,
      "allowFrom": ["U_OPS_BOT", "U_ENG_BOT", "U_HUMAN"],
      "allowBotIds": ["U_OPS_BOT", "U_ENG_BOT"]
    }
  }
}

Self-echoes from this bot are always filtered regardless of allowBotIds. Peer bots cannot approve permission prompts — the permission relay gates on the top-level allowFrom, not the channel policy.

For the full multi-agent recipe — registering a second bot, configuring allowBotIds mutually, mention-driven addressing, loop-prevention rate limit, !mute/!unmute operator verbs, common failure modes, what you DON'T get — see 000-docs/multi-agent-channels.md.

This is a prompt-injection vector by design. The security model is documented threat-model-first in SECURITY.md: (1) the adversary and the four principals, (2) the mitigations below — each with its own stated limit, and (3) an explicit list of what this does NOT protect against (same-UID host compromise, in-process token exposure, supply-chain, Slack/Anthropic platform bugs). Start there for the full picture; the defense-in-depth layers are:

  • Sender gating: Every inbound message hits a gate. Ungated messages are silently dropped before reaching Claude.
  • Outbound gate: Replies only work to channels that passed the inbound gate.
  • File exfiltration guard: Cannot send .env, access.json, audit.log, or other state files through the reply tool.
  • Prompt-injection defense: System instructions explicitly tell Claude to refuse pairing/access requests from Slack messages — peer-bot messages carry the same risk as human messages.
  • Bot filtering: bot_id messages are dropped by default. Channels that host multiple cooperating agents can opt in to specific peers via allowBotIds; self-echoes are always filtered via bot_id / bot_profile.app_id / user triple-check.
  • Link unfurling disabled: All outbound messages set unfurl_links: false, unfurl_media: false.
  • Token security: .env is chmod 0o600, never logged, never in tool results.
  • Static mode: Set SLACK_ACCESS_MODE=static to freeze access at boot (no runtime mutation).
  • Signed audit journal: Every tool-call decision is written to a hash-chained, Ed25519-signed journal (RFC 8785 JCS canonical form). The chain is verifiable offline against a published public key — see Audit Signing.
  • Admin-command hardening: Operator verbs (!clear, !restart) route through gate → policy → journal → execute and require a server-minted HMAC nonce confirmed from a second channel. Claude cannot self-invoke them — no MCP tool name begins with admin.. This closes the EchoLeak / operator-coercion class (CVE-2025-32711, threat T11): a prompt injected into one channel cannot drive a privileged action, because confirmation must come from a channel the attacker doesn't control.
  • Peer-bot loop control: A per-(channel, bot_id) sliding-window rate limit (default 10 msg/60s) breaks A→B→A runaway loops; operators can !mute <@bot> / !unmute <@bot> a misbehaving peer.

Each layer's limit, and an explicit "what this does NOT protect against" section, are in SECURITY.md. Trust boundaries, per-primitive attack surface, and threats T1–T11 are documented in 000-docs/THREAT-MODEL.md.

Audit Signing

Every decision the gate makes is journaled to ~/.claude/channels/slack/audit.log — hash-chained and, as of v0.10, Ed25519-signed — so a third party can verify the log against a public key without trusting the host.

# Verify a journal end-to-end (hash chain + signatures):
bun server.ts --verify-audit-log ~/.claude/channels/slack/audit.log

# Operator key lifecycle (Ed25519 over RFC 8785 JCS):
bun audit-key-cli.ts init       # generate keypair, encrypt private half to .env, print public key
bun audit-key-cli.ts rotate     # fresh keypair, re-sign chain head, archive the old public key
bun audit-key-cli.ts verify     # verify the log against active + archived public keys
bun audit-key-cli.ts show       # print active public key + key id

The key is loaded at boot from SOPS+age-encrypted .env. Run with --no-audit-signing to fall back to hash-chain-only. Design + rotation lifecycle: 000-docs/audit-journal-architecture.md and 000-docs/key-management.md.

Companion tooling

agent-governance-plane (AGP) is a companion governance tool that builds on this substrate. AGP vendors a pinned copy of CCSC's governance kernel — the policy engine (policy.ts), the hash-chained signed journal (journal.ts), the Slack relay, and nonce-bound HITL — to govern agent runtimes beyond a single Slack bridge. The relationship is one-way by design: CCSC is the substrate and has zero knowledge of AGP; AGP is the downstream consumer. If you need governance across multiple agent runtimes (not just Claude Code in Slack), AGP is the layer to look at; if you need a governed Slack channel for Claude Code, CCSC is all you need. The substrate-extraction contract is documented in AGP's 000-docs/009-AT-ADR-ccsc-substrate-extraction-strategy.md, and the patterns shared between the two are recorded in 000-docs/ADR-002.

Testing & Quality

Security-critical code earns a hard gate. ci.yml runs nine checks in sequence on every PR — a red on any one blocks merge:

Gate What it enforces
bun run typecheck TypeScript strict, no any escape hatches
Biome lint curated rule set
bun test 986 tests / 6,017 assertions across unit + property + Gherkin suites
coverage floor ≥ 95% line + function (scripts/coverage-floor.sh)
dependency-cruiser architecture invariants (e.g. policy.ts may never import manifest.ts)
Gherkin lint acceptance-feature style, --strict
harness-hash verify SHA-256 tamper check on pinned .feature + arch-rule files
bun audit dependency CVEs at --audit-level=high
crap-score cyclomatic-complexity ceiling (30)

Out of band: CodeQL, gitleaks secret scanning, OpenSSF Scorecard, and manual Stryker mutation testing (bunx stryker run, baselines in 000-docs/MUTATION_REPORT.md). The five Wall-1 acceptance primitives live in features/*.feature and run against the real code via a hand-rolled Gherkin runner.

bun test                  # full suite
bun run typecheck         # strict type check
bunx @biomejs/biome check .

Development

# Dev mode (bypasses plugin allowlist):
claude --dangerously-load-development-channels server:slack

One-Pager & System Analysis

Full project one-pager and operator-grade system analysis

Changelog

Every user-visible change is recorded in CHANGELOG.md (Keep a Changelog format). Release notes: GitHub Releases.

Contributing

See CONTRIBUTING.md for branching, commits, PR workflow, and the quality-gate checklist. AI assistants helping with contributions should read AGENTS.md first.

Contributors

Support

Sponsor on GitHub Buy Me a Coffee

This is open-source software maintained by Jeremy Longshore. If it saves you time, consider sponsoring on GitHub or buying a coffee — it funds continued development and keeps the project independent.

License

Apache License 2.0 — see LICENSE.

About

Slack-native governance substrate for Claude Code and peer agents: every tool call passes a tiered policy engine and lands in a hash-chained, Ed25519-signed audit journal you can verify offline. The governance kernel the companion agent-governance-plane (AGP) builds on.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

  •  

Packages

 
 
 

Contributors