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 behavior
- DMs always work
- In channels: bot responds when mentioned
- Thread sessions are supported
- Receipt reaction (
:eyes:) is added on accepted inbound messages
Make sure Slack bot scopes include at least:
chat:writereactions:writechannels:history,groups:history,im:history,mpim:history- Socket Mode app token (
connections:write)
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}