arizukocomponents › slakd

slakd

What it is

slakd is the Slack channel adapter. It receives Slack workspace events over the HTTP Events API (proxyd forwards /slack/events to slakd) and posts outbound via the Slack Web API. From gated’s point of view it is a normal channel adapter with JID prefix slack: and per-message JIDs of the form slack:<workspace>/<kind>/<id> where kind is channel, dm, or group.

Why it exists

Slack is the canonical team chat surface and the only platform arizuko targets that ships an “Agents & AI Apps” assistant pane — a dedicated sidebar with suggested prompts and a conversation title. Talking to Slack directly (rather than through a SaaS gateway) is what lets slakd implement that pane natively and ride the same trust boundary as every other adapter.

How it fits

Slack workspace
        |  HTTPS Events API webhook
        v
      proxyd  (verifies X-Slack-Signature, forwards /slack/...)
        |
        v
      slakd     (LISTEN_ADDR=:8080)
        |  POST /v1/messages    (signed by CHANNEL_SECRET)
        v
      gated
        |  POST /v1/send         (callback to slakd)
        v
      slakd     (chat.postMessage / files.* / reactions.add)
        |
        v
      Slack Web API

slakd verifies the X-Slack-Signature HMAC of v0:<ts>:<body> on every webhook and rejects skew > 5 minutes. Reactions added on Slack become synthetic like / dislike inbound rows (classified by chanlib.ClassifyEmoji). Channel messages that contain <@bot_user_id> are tagged verb=mention; DMs and unaddressed channel messages ride verb=message.

Wired verbs (see slakd/bot.go, slakd/server.go): send, send_file, edit, delete, like, dislike, post, plus the assistant-pane verbs pane_set_prompts and pane_set_title. forward, quote, repost are 501-with-hint.

Assistant pane

With assistant:write scope and the assistant_thread_started + assistant_thread_context_changed subscriptions, slakd upserts a row in pane_sessions when a user opens the sidebar, sets the title to <ASSISTANT_NAME> — chat, seeds three suggested prompts, and dispatches a synthetic pane_open inbound. Pending prompts and title updates staged via MCP tools are drained on the next chat.postMessage.

Output rendering switches the agent into the slack Claude Code output style (*bold*, _italic_, <url|text>) since v0.40.7 so the bot speaks Slack mrkdwn instead of CommonMark.

Standalone usage

slakd is a plain Go binary. It needs a reachable gated, a Slack app with a bot token, and a public URL that forwards /slack/events to it.

export ROUTER_URL=http://gated:8080
export CHANNEL_SECRET=$(grep ^CHANNEL_SECRET .env | cut -d= -f2)
export LISTEN_ADDR=:8080
export SLACK_BOT_TOKEN=xoxb-...
export SLACK_SIGNING_SECRET=...
export ASSISTANT_NAME=arizuko
export DB_PATH=/srv/app/home/store/messages.db
./slakd

Slack-side setup: create a Slack app, enable Events API with request URL pointing at /slack/events, subscribe to message.channels, message.im, message.groups, reaction_added, and (for the pane) assistant_thread_started + assistant_thread_context_changed. Do not subscribe to app_mention — slakd detects mentions from <@bot_user_id> in the message text, and a parallel app_mention subscription causes duplicate processing. Scopes: chat:write, files:write, reactions:write, im:history, channels:history, groups:history, assistant:write.

GET /health returns 503 when Slack auth fails or the workspace cache cannot warm.

Go deeper