stakpak-gateway
Messaging gateway runtime for stakpak.
It bridges chat platforms (Telegram / Slack / Discord) to the Stakpak server API (/v1/sessions/...), manages routing/session mapping, and exposes a small Gateway API for outbound messages and autopilot notifications.
What it does
- Receives inbound messages from channels
- Routes each conversation to a stable Stakpak session
- Sends user messages to the autopilot server runtime
- Streams run events and returns assistant replies back to channel
- Handles tool decisions using configured approval policy
- Stores routing/session mappings in SQLite
- Supports autopilot notifications via
POST /v1/gateway/send
Architecture & data flow
Channel adapter (telegram/slack/discord)
└─ emits InboundMessage
│
▼
Dispatcher
├─ resolve routing key (dm/group/thread)
├─ load/create session mapping (GatewayStore)
├─ send user message to autopilot server
├─ subscribe to SSE events for the run
├─ auto-resolve tool approvals (gateway policy)
└─ deliver assistant reply back to channel
│
▼
Channel adapter send(...)
Core components
runtime.rs- boots channels + dispatcher + prune loop
- mounts gateway API state
dispatcher.rs- main orchestration loop for inbound messages and run events
- queues follow-up messages while a run is active for a session
- keeps per-session SSE cursor to resume safely
router.rs- computes stable routing keys for direct/group/thread conversations
- supports bindings + DM scope behavior
store.rs- SQLite persistence for routing key → session mapping
- stores one-shot
delivery_contextfor autopilot notification replies
client.rs- HTTP + SSE client to autopilot server
- sends messages, receives run events, resolves tool decisions
api.rs- gateway HTTP surface:
GET /statusGET /channelsGET /sessionsPOST /send
- gateway HTTP surface:
Session model
- Each chat target resolves to a routing key.
- Routing key maps to one persistent Stakpak session_id.
- For thread-aware channels, each thread can map to a separate session.
- Delivery metadata is refreshed on inbound messages so replies go to the right target.
Tool approval model
- Gateway receives
tool_calls_proposedfrom SSE. - It builds decisions using configured policy (
allow_all,deny_all,allowlist). - In autopilot mode, approval policy is derived from the profile's auto-approve settings.
How to run
The gateway is managed through the autopilot system. There are no standalone stakpak gateway commands.
Setup channels
# Add channels via autopilot CLI
# Verify channels
Start autopilot (includes gateway)
This starts the full autopilot runtime which includes:
- Scheduler — cron-based schedule execution
- Server — HTTP API on
127.0.0.1:4096 - Gateway — channel adapters + routing
Gateway routes are available at http://127.0.0.1:4096/v1/gateway/*.
Channel management
Configuration is stored in ~/.stakpak/autopilot.toml.
Gateway API quick test
Status
Send outbound message
Channel target formats:
- Telegram:
{ "chat_id": "...", "thread_id": "..." } - Discord:
{ "channel_id": "...", "thread_id": "...", "message_id": "..." } - Slack:
{ "channel": "...", "thread_ts": "..." }
Slack setup
Behavior
- DMs always work
- In channels: bot responds when mentioned
- Thread sessions are supported
- Receipt reaction (
:eyes:) is added on accepted inbound messages
Required Bot Token Scopes
The Slack bot needs specific OAuth scopes. Without the read/history scopes, the bot can send notifications but cannot receive inbound messages.
| Scope | Purpose | Required for |
|---|---|---|
chat:write |
Send messages to channels | Outbound (notifications) |
reactions:read |
Read emoji reactions | Inbound |
reactions:write |
Add emoji reactions (:eyes: receipt) |
Outbound |
channels:read |
See public channels the bot is in | Inbound |
groups:read |
See private channels the bot is in | Inbound |
im:read |
See DM conversations | Inbound |
mpim:read |
See group DM conversations | Inbound |
channels:history |
Read messages in public channels | Inbound |
groups:history |
Read messages in private channels | Inbound |
im:history |
Read DM messages | Inbound |
mpim:history |
Read group DM messages | Inbound |
app_mentions:read |
Receive @mention events | Inbound |
Socket Mode also requires an App-Level Token (xapp-*) with the connections:write scope.
Slack App Configuration
- Go to api.slack.com/apps → select the app
- OAuth & Permissions → add all Bot Token Scopes listed above
- Socket Mode → enable (requires App-Level Token /
xapp-*) - Event Subscriptions → enable and subscribe to bot events:
message.channels— messages in public channelsmessage.groups— messages in private channelsmessage.im— direct messagesapp_mention— @mentions
- Reinstall the app to the workspace (scope changes require reinstall)
- Update
autopilot.tomlwith the newxoxb-*bot token (or re-runstakpak autopilot channel add slack ...)
Troubleshooting
If stakpak autopilot channel test passes (✓) but the bot never responds to messages:
# Check if bot has read scopes
|
# "missing_scope" → bot only has chat:write, needs read scopes above
Library usage (Rust)
use ;
use CancellationToken;
# async
Source layout
src/runtime.rs– Gateway runtime boot + channel wiringsrc/dispatcher.rs– inbound -> server run -> outbound reply loopsrc/client.rs– Stakpak HTTP/SSE clientsrc/store.rs– SQLite mapping/context storesrc/router.rs– routing key and scope resolutionsrc/targeting.rs– outbound target parsing + keyingsrc/channels/*– Telegram/Slack/Discord implementationssrc/api.rs–/v1/gateway/{status,channels,sessions,send}