- Add `client-` prefix to sandbox-processes.md naming table for external-surface clients bridging an in-sandbox agent. - Tighten the `agent-` example off interactive CLIs; document that claude/codex/opencode are foreground tools, not tmux sessions. - entrypoint.sh: restore client-slack on container start when SLACK_APP_TOKEN + SLACK_BOT_TOKEN are present and `pi` is installed. - Propagate rename through wizard code, READMEs, and docs. - Fold in half-finished harness-pi rename from #302. Closes #344 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.8 KiB
sidebar_position, title
| sidebar_position | title |
|---|---|
| 1 | Slack |
Slack UI labels accurate as of 2026-05-12.
Slack
The Slack integration ships as a Pi extension at .pi/extensions/slack/. Once
your sandbox is up and Slack tokens are in env, DM the bot or mention it in a
channel to start a conversation. The extension opens a Socket Mode WebSocket on
startup and bridges inbound Slack events into the Pi agent, then posts the
agent's response back to Slack.
1. Prerequisites
- Sandbox is running (
make psshows theopenharnesscontainer). pi --versionworks inside the sandbox (make shellto verify).- A Slack workspace where you can create apps (workspace admin or equivalent). If you are on a company Slack that restricts app creation, create a free personal workspace at slack.com/get-started and use it for testing.
2. Find Your Slack IDs
You need these before filling in the allowlist env vars.
User ID (U…)
- Open Slack and go to your profile (or the profile of the user to allow).
- Click the ... (More) menu in the top-right of the profile panel.
- Click Copy member ID. The value starts with
U.
Channel ID (C…)
- Open the channel in Slack.
- Click the channel name at the top to open the About panel.
- Scroll to the bottom of the panel — the channel ID starts with
Cand is listed there.
Collect all user and channel IDs you want to allow before moving to env setup.
3. Create the Slack App
- Go to api.slack.com/apps and click Create New App.
- Choose From an app manifest.
- Select your workspace and paste the contents of
.pi/install/slack-manifest.jsonfrom this repo. - Click through the confirmation screens and then Install to Workspace.
- Approve the requested OAuth scopes.
4. Capture Tokens
After installation, collect two tokens from the Slack app settings. They are not interchangeable — the wrong token in the wrong variable causes a silent auth failure.
| Token | Prefix | Where to find it |
|---|---|---|
| App-Level Token | xapp- |
Basic Information page → App-Level Tokens section → generate one with connections:write scope |
| Bot User OAuth Token | xoxb- |
OAuth & Permissions page → Bot User OAuth Token |
Keep both values ready for the next step.
5. Run the wizard — oh config slack
Inside the sandbox, run the wizard. It prompts for the two tokens, asks
which allowlist mode to use (users / channels / both), validates IDs,
writes .devcontainer/.env atomically, and offers to restart the
client-slack tmux session.
make shell # enter the sandbox
oh config slack # interactive wizard
The wizard:
- Validates token prefixes (
xapp-/xoxb-) before writing — swapped tokens are caught at the prompt, not after launch. - Enforces the deny-default allowlist rule (at least one of
SLACK_ALLOW_USERS/SLACK_ALLOW_CHANNELSis required). - Writes only the Slack keys; unrelated entries (
GH_TOKEN,TZ, etc.) are preserved. - Starts the Slack bridge for you — at the end, it kills any
existing
client-slacktmux session and launches a fresh one runningpiwith the new env sourced.piloads the Slack extension on boot and opens the Socket Mode connection. The wizard polls for the"connected and listening"log line and prints✓ Slack bridge is liveon success (up to 15s).
If you decline the start step, the wizard prints the exact commands to launch the bridge manually later.
Manual fallback
If oh isn't available (older sandbox image, or you'd rather edit by
hand), the manual procedure still works:
Hand-edit .devcontainer/.env + relaunch client-slack
.devcontainer/.env uses Docker Compose KEY=value format — no export
prefix. This file is gitignored, so your tokens will not be committed.
SLACK_APP_TOKEN=xapp-...
SLACK_BOT_TOKEN=xoxb-...
# At least one of the two allowlist vars must be set (deny-default if both absent).
# When both are set: message passes only if channel AND user both match.
SLACK_ALLOW_USERS=U01ABCD2345,U02EFGH6789
# SLACK_ALLOW_CHANNELS=C01ABCD2345,C02EFGH6789
# Optional vars:
# SLACK_BASE_DIR=/custom/path # default: ~/.pi/cache/slack
# SLACK_BACKFILL=C01ABCD2345 # comma-separated channel IDs to backfill on startup (slow)
Then launch Pi in tmux. .devcontainer/.env is Compose-formatted (no
export), so set -a is required to auto-export vars as they are
sourced. set -a is shell-local — the tmux new-session command must
run in the same shell that ran set -a, or pi will not inherit the
vars. This is the most common manual-mode failure.
set -a; source /home/sandbox/harness/.devcontainer/.env; set +a
tmux new-session -d -s client-slack 'pi 2>&1 | tee /tmp/client-slack.log'
tmux attach -t client-slack
Detach with Ctrl-b d. The session name client-slack follows the client-
prefix convention in
context/rules/sandbox-processes.md.
6. Smoke Test
Run these checks in order. The first two run in the shell where you sourced the env (before attaching to tmux).
-
Vars present in the current shell:
env | grep SLACKExpected:
SLACK_APP_TOKEN,SLACK_BOT_TOKEN, and your allowlist var are all listed. If any are missing,set -adid not run in this shell — repeat step 6 from the beginning. -
Socket Mode connected (the real connectivity check):
tmux capture-pane -t client-slack -p | grep -i 'socket\|connected\|listening'Look for lines indicating the Socket Mode client connected or is listening. Note:
curl https://slack.com/api/auth.testonly validates the bot token (xoxb-), not the Socket Mode app token (xapp-). An invalidSLACK_APP_TOKENcan passauth.testand still fail to open a Socket Mode connection. Use the tmux log check above as the authoritative test. -
Round-trip test: From an allow-listed user account, DM the bot or
@mentionit in an allow-listed channel. Watchtmux attach -t client-slack— you should see the inbound event logged and the agent's reply posted back to Slack.
7. Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Bot doesn't respond; allowlist is set | pi did not inherit the vars — set -a ran in a different shell than tmux new-session |
Kill the session (tmux kill-session -t client-slack), run set -a; source …; set +a, then relaunch tmux new-session in the same shell |
| Bot doesn't respond; no allowlist set | Deny-default: both SLACK_ALLOW_CHANNELS and SLACK_ALLOW_USERS are unset |
Add at least one allowlist var to .devcontainer/.env and relaunch |
invalid_auth or not_authed in log |
xapp- and xoxb- tokens are swapped |
SLACK_APP_TOKEN must be the xapp- token; SLACK_BOT_TOKEN must be the xoxb- token — correct in .devcontainer/.env and relaunch |
| Socket Mode crashes / reconnects in a loop | Network interruption or token revoked | tmux kill-session -t client-slack, verify tokens in .devcontainer/.env, then relaunch |
| Bot is in allow-list but channel messages ignored | Bot is not a member of the channel | In Slack, type /invite @OpenHarness in the target channel |
8. Architecture Pointer
The extension is self-contained under .pi/extensions/slack/. For the full
file inventory, env var reference, and build/test instructions, see
.pi/extensions/slack/README.md.
For upstream lineage, sync model, and divergence history, see
.pi/UPSTREAM.md.