Company knowledge — right in your Slack chat
An employee messages the bot in a DM — Query Engine answers under their own permissions, with links to sources. Everything the web chat can do, without leaving the messenger.
ack <3s
answer as a post in the thread
Slack is one more thin window into the same conversation: Query Engine does the thinking and searching under the employee's identity, Slack merely carries the message in and the answer out. All that's new lives at the edges: connection, identity bridge, deferred delivery. One conversation model across all surfaces →
Admin installs the Achilles app into the workspace once — the bot token
and signing secret land in slack_settings, like SMTP in Email. The screen is
platform settings, a single toggle turns the bot on.
The install model is internal-app: the customer registers the app in their own workspace. That's exactly how it should be for self-hosted.
The sender is resolved to an account via
identity_mapping
(source='slack') — the sign-in identity, distinct from the
content identity of the Slack source in Knowledge Store. If the workspace's
provisioned email matches, the link is automatic; if not, a
link code:
whoever signed in on the web gets a code and returns it to the bot in a DM (linking the
Slack that returned it to the account that issued it). Only an admin creates the account
(by invitation) —
Slack membership grants no access.
From there on it's only user_id → role → ACL.
A thread is a conversation: replies within it continue the conversation, a new message opens a new one. Context is truncated by tokens — the engine's shared mechanism, not its own. "Summarize" is an ordinary question to the bot, not a command.
Channels · @mention · slash command — v2.
Slack expects an acknowledgment within 3 seconds, but the model takes longer: intake
acknowledges immediately, processing goes to the
interactive
queue, the answer comes back as a post. It survives restarts and retries.
A repeated Slack event is dropped by event_id; the public endpoint is
rate-limited.
What's the status of release 4.2 and who owns the ajax bug?
Release 4.2 is in regression testing, ship date planned for Friday. The ajax bug is owned by Dina Safina1.
The answer is scoped to Maxim's permissions: anything he can't see won't appear in it. The follow-up stays in the thread of the same conversation.
First contact — linking the account with a code:
Hi! What's the status of release 4.2?
To continue, confirm your account: sign in at achilles.local/link and send me the code. After that I'll answer under your permissions.
K7P2-9XQ4
Done — linked to maxim@company.com. Ask away
The code is visible only in the signed-in browser and comes back through Slack — a forwarded link can't hijack the account. Linking screen →
source='slack' → sign-in identity bridge
(team, channel, thread_ts)
| id | BigInteger | PKCHECK | always 1 · singleton |
| team | Text | NULL |
workspace id (T…) · NULL until connected
|
| team_name | Text | NULL | workspace name for the UI (company.slack.com) · from auth.test |
| bot_token_enc | Text | NULL |
OAuth bot token (xoxb-…), AES-256-GCM ciphertext · → encryption
|
| signing_secret_enc | Text | NULL | secret for signing inbound requests, ciphertext |
| bot_user_id | Text | NULL |
bot id (U…) · filters out own messages and mentions
|
| enabled | Boolean | NOT NULLDEFAULT | master switch · DEFAULT false · off → the bot stays silent |
| last_test_ok | Boolean | NULL | result of the last "Test connection" |
| last_test_at | DateTime(tz) | NULL | when it was tested · feeds the status chip on the screen |
| created_at | DateTime(tz) | DEFAULT | server_default=now() |
| updated_at | DateTime(tz) | DEFAULT |
server_default=now() · trigger set_updated_at()
|
is_available() — a model property, not a column:
enabled and team · bot_token_enc ·
signing_secret_enc all filled in. Mirrors SMTP in Email.
Checks: Slack signatureack < 3 sidentity resolutionretry dedupthread = conversationACL pass-throughunlinked usersettings singleton
The webhook is anonymous by design: the lock is the Slack signature, the rate-limit window is fail-closed.
identity_mapping → user_id bridge.
→ Auth / Security
/api/v1/slack/* paths on the shared surface map.
→ HTTP API