| name | geode-serve |
| description | Slack Gateway operations guide. Socket Mode credentials, config.toml bindings, serve restart, receiver debugging, reaction behavior. Triggers on "serve", "gateway", "slack", "바인딩", "binding", "소켓", "socket", "폴러", "poller", "config.toml". |
geode serve — Slack Gateway Operations Guide
Source: Distilled from Gateway debugging session (2026-03-26)
Primary failure modes: missing binding, missing xapp- token, or bot not invited to a bound channel
Diagnosing a connection does not authorize credential changes, channel messages,
or process restarts. Apply only the requested operation and keep credential
values, fragments, and private message content out of reports.
Architecture
geode serve
→ core/wiring/adapters.py: Merge ~/.geode/config.toml + project overlay
→ SlackPoller: SLACK_APP_TOKEN present → SlackSocketModeClient.run()
→ bounded-queue admit → ACK Events API envelope → filter exact bound channel → route_message()
→ _send_response(): SlackTransport.post_message (direct Web API, thread reply)
No `SLACK_APP_TOKEN` selects an explicit degraded polling fallback. It is a
migration path, not the operational target.
Prerequisites
| Item | How to verify |
|---|
SLACK_BOT_TOKEN | test -n "${SLACK_BOT_TOKEN:-}" — exit status only, no value output |
SLACK_APP_TOKEN | test -n "${SLACK_APP_TOKEN:-}" — exit status only; app requires connections:write |
| App settings | Socket Mode on; bot events app_mention, message.channels |
| Gateway config | Inspect only gateway.bindings.rules in the global config and project overlay; do not dump the entire config |
| Slack health | When live diagnostics are authorized, geode doctor slack is OPERATIONAL and every binding says bot_member=True |
The shell presence checks cover inherited environment variables only. An unset
variable does not prove the runtime lacks a credential: the resolver also uses
the global .env. Do not print or copy that file to investigate.
geode doctor slack makes live Slack calls (auth.test, apps.connections.open,
and channel membership checks). Its credential rows use partial masking, not
full redaction; workspace/bot identifiers also appear. Keep raw diagnostics
local and report only check names, status, and redacted findings. The temporary
Socket Mode URL is omitted from the successful diagnostic report.
config.toml Setup
Use .geode/config.toml.example to create a project config only when none exists.
If it already exists, edit the required binding fields in place and preserve
unrelated settings. Never overwrite an existing global config or project overlay
with the template.
[gateway.bindings]
[[gateway.bindings.rules]]
channel = "slack"
channel_id = "C0XXXXXXXXX"
auto_respond = true
require_mention = true
time_budget_s = 90
config.toml is in .gitignore — not deleted by git pull
config.toml.example is committed — reference for clean clones
- Adding channels: Repeat
[[gateway.bindings.rules]] blocks
Start/Restart
Follow Rebuild & Restart only when
startup or restart is authorized. Confirm the installation, GEODE home/socket,
PID, and session owner before stopping anything. The lifecycle implementation
in core/cli/commands/lifecycle.py currently discovers the first matching serve
PID; neither that match nor the stop command proves ownership. If several
sessions match or ownership is unclear, stop and request direction.
Restart the confirmed installation through its existing launcher and retain
startup diagnostics. Do not replace a managed service with an unrelated
background process or discard its logs into /dev/null. Verify the requested
process and socket before claiming it restarted.
Debugging Checklist
Symptom: Bot does not respond to messages
grep "binding" ~/.geode/logs/serve.log
grep -i "gateway config sources" ~/.geode/logs/serve.log
grep -E "Slack inbound mode|Slack Socket Mode connected" ~/.geode/logs/serve.log
geode doctor slack
grep "Slack message from" ~/.geode/logs/serve.log
Symptoms and Causes
| Symptom | Cause | Resolution |
|---|
| "Loaded 0 gateway bindings" | Binding absent or not loaded from the merged config | Check global/project sources; add only the missing binding without replacing existing config |
polling fallback | SLACK_APP_TOKEN missing | If configuration changes are authorized, set an app token with connections:write in the global credential store; restart only through the owned-process procedure |
not_in_channel / bot_member=False | Bot was not invited | Run /invite @geode in the linked channel |
| Repeated disconnects | App token invalid or Socket Mode disabled | Run geode doctor slack, then verify app-level token and Socket Mode settings |
| New top-level/unengaged message receives no response | require_mention=true but no @mention | Mention @botname once or set require_mention=false |
| Engaged thread stops after daemon restart | No resumable ACTIVE/PAUSED checkpoint, or receiver is still polling | Confirm Socket Mode logs; re-mention once if the prior machine is terminal |
| Bot re-responds to its own messages | bot_message filter bypassed | Check bot_id field — if normal, check Slack App settings |
Reaction Behavior
require_mention = true + first <@BOT_ID> mention:
- :eyes: reaction (acknowledge receipt)
- Normalize the root
ts as thread_id and remember the engaged thread
ChannelManager.aroute_message() → AgenticLoop execution
- :white_check_mark: reaction (complete)
- Send response in thread
Later human reply in that engaged thread (no repeated mention):
- Match channel-scoped engaged state or the durable ACTIVE/PAUSED gateway checkpoint
- Reuse the same session/lane/checkpoint key and restore checkpoint messages after restart
- Run the same :eyes: → AgenticLoop → :white_check_mark: → thread-response lifecycle
Unengaged regular message (no mention, require_mention = false):
- Process without reaction + thread response
Related Files
| File | Role |
|---|
core/messaging/slack_socket_mode.py | Socket URL, WebSocket ACK/reconnect loop |
core/server/supervised/slack_poller.py | Socket event normalization + compatibility fallback |
core/messaging/binding.py | Binding management + message routing |
core/messaging/slack_transport.py | Bot-token Web API outbound + channel diagnostics |
core/wiring/adapters.py | Gateway config merge and receiver registration |
.geode/config.toml | Channel bindings (local, untracked) |
.geode/config.toml.example | Binding template (committed) |