62 lines
3 KiB
Markdown
62 lines
3 KiB
Markdown
|
|
# agent-team Slack app
|
||
|
|
|
||
|
|
Dedicated Slack app backing the Plane-2 clarifier human-gate (Socket Mode).
|
||
|
|
Kept separate from the webhook-only **Tech Notifications** app (`A0ARYQZU3KJ`)
|
||
|
|
that the nightly secrev sweep uses, to isolate the two-way bot's trust surface.
|
||
|
|
|
||
|
|
| Field | Value |
|
||
|
|
|---|---|
|
||
|
|
| App name | Sea Haven agent-team |
|
||
|
|
| App ID | `A0BC7AT8NUD` |
|
||
|
|
| Org / team | seahaven (`E0A524V806L`) |
|
||
|
|
| Manifest | [`agent-team-manifest.json`](./agent-team-manifest.json) (source of truth) |
|
||
|
|
| Settings | https://api.slack.com/apps/A0BC7AT8NUD |
|
||
|
|
|
||
|
|
## Why these scopes (verified against the code)
|
||
|
|
|
||
|
|
`agent_team/transport/slack_listener.py` subscribes over Socket Mode to
|
||
|
|
`message`, `app_mention`, and Block Kit `block_actions`; `slack_live.py` posts
|
||
|
|
questions via `chat.postMessage`.
|
||
|
|
|
||
|
|
| Capability | Scope / setting | Why |
|
||
|
|
|---|---|---|
|
||
|
|
| Post clarifier questions | `chat:write` | `slack_live.py` `chat.postMessage` |
|
||
|
|
| Hear thread replies in the channel | `channels:history` / `groups:history` + `message.channels`/`message.groups` events | `@app.event("message")` |
|
||
|
|
| Hear DM replies | `im:history` + `message.im` event | DM answer path |
|
||
|
|
| Hear @mentions | `app_mentions:read` + `app_mention` event | `@app.event("app_mention")` |
|
||
|
|
| Block Kit button/select answers | `interactivity.is_enabled` | `@app.action({})` |
|
||
|
|
| Inbound WebSocket | `socket_mode_enabled` + an app-level token w/ `connections:write` | VPN-only box, no public HTTPS endpoint |
|
||
|
|
|
||
|
|
Inbound auth is NOT scope-based: AUTHZ-01 (`AGENT_TEAM_SLACK_OWNER_IDS`) gates
|
||
|
|
the *sender* and fails closed. Socket membership alone is never authorization.
|
||
|
|
|
||
|
|
## Remaining manual token mints (operator, in browser)
|
||
|
|
|
||
|
|
Both produce secrets — paste them straight into `~/secrev.env` on the box
|
||
|
|
(mode 600), never into shell history.
|
||
|
|
|
||
|
|
1. **Bot token (`xoxb-`)** — https://api.slack.com/apps/A0BC7AT8NUD/oauth →
|
||
|
|
*Install to Workspace* → approve → copy the **Bot User OAuth Token** →
|
||
|
|
`SLACK_BOT_TOKEN`.
|
||
|
|
2. **App-level token (`xapp-`)** — https://api.slack.com/apps/A0BC7AT8NUD/general
|
||
|
|
→ *App-Level Tokens* → *Generate Token and Scopes* → add scope
|
||
|
|
`connections:write` → copy → `SLACK_APP_TOKEN`.
|
||
|
|
3. **Channel** — create/choose the clarifier channel, `/invite @agent-team`,
|
||
|
|
copy its `C0...` id → `SLACK_CHANNEL_ID`.
|
||
|
|
4. **Owner allowlist** — `AGENT_TEAM_SLACK_OWNER_IDS` = Adam's Slack user id
|
||
|
|
(`U0A3SC48T47`), comma-separated if more than one. Gate fails closed if empty.
|
||
|
|
|
||
|
|
## Reproduce / update the app from the manifest
|
||
|
|
|
||
|
|
```bash
|
||
|
|
TOKEN=$(python3 -c "import json;print(json.load(open(os.path.expanduser('~/.slack/credentials.json')))['E0A524V806L']['token'])")
|
||
|
|
# validate
|
||
|
|
curl -s -X POST https://slack.com/api/apps.manifest.validate \
|
||
|
|
-H "Authorization: Bearer $TOKEN" --data-urlencode "manifest=$(cat agent-team-manifest.json)"
|
||
|
|
# update existing app
|
||
|
|
curl -s -X POST https://slack.com/api/apps.manifest.update \
|
||
|
|
-H "Authorization: Bearer $TOKEN" \
|
||
|
|
--data-urlencode "app_id=A0BC7AT8NUD" \
|
||
|
|
--data-urlencode "manifest=$(cat agent-team-manifest.json)"
|
||
|
|
```
|