Somebody types "can we loop in finance on this?" in a product channel. Nobody
tags the finance channel. The thread moves on. Three weeks later there is a
contract nobody in finance has seen.
That is not a tooling problem in any obvious sense. Slack worked exactly as
designed. Search would have found the message if anyone had known to look for
it. The failure is that the people who needed to know were never told, and
nothing in the workspace was watching for the difference between mentioning a
team and involving one.
Why keyword matching does not solve this
The instinct is to grep for the word "finance" and alert on it. That produces
a channel nobody reads inside a week, because "finance" appears in sentences
that have nothing to do with governance, and the sentences that do matter
often do not contain the word at all.
What actually carries the signal is structure. A Slack message is not plain
text on the wire. When someone references a channel, it arrives looking like
this:
Can we loop in <#C01ABCDEF|finance> before this goes out?
That is a channel reference, distinct from a mention that notifies the
channel, and it survives in the event payload whether or not anyone was
actually alerted. It means the workspace already knows the difference between
"I said the word finance" and "I pointed at the finance channel and did not
bring anyone in". Nobody was reading it.
So the bot parses references rather than words. It maps channel IDs to what
those channels are for, and it looks for the specific shape of a message that
points at a governance channel from outside it. Context beats keywords, and
in this case the context was already structured and already being thrown away.
Two decisions that mattered more than the detection
It joins every public channel by itself. The obvious build asks an admin
to add the bot wherever it should watch, which means coverage is a function of
somebody remembering. Every channel created after launch is a gap, and nobody
finds out until something is missed in one. Instead it lists the public
channels through the API and joins them, then listens for channel_created
and joins new ones as they appear. Coverage is complete by construction rather
than by diligence, across more than a hundred channels.
It runs on Socket Mode. The usual Slack integration takes an inbound
webhook, which means a public HTTPS endpoint that anyone can reach and that
has to be secured, monitored and kept alive. Socket Mode reverses the
direction: the bot opens an outbound connection and Slack pushes events down
it. For an organisation that would otherwise be exposing a new inbound surface
to catch its own internal messages, that trade is worth taking on its own.
The constraint that shaped the deployment
Socket Mode has a consequence that only shows up on deploy. Because the bot
never receives inbound HTTP, it never binds a port, and the platform hosting
it was watching for exactly that to decide whether the service was alive. A
correctly working bot looked, to the host, like a process that had failed to
start.
The fix is a sidecar: a minimal HTTP server inside the same process whose only
job is to answer a health check.
import express from "express";
const app = express();
app.get("/", (_req, res) => res.send("Bot is running"));
app.listen(process.env.PORT || 3000);
Ten lines that do nothing for the product and without which the product does
not stay running. It is worth saying plainly, because this is the part that
never appears in an architecture diagram and is most of what deployment
actually is: the platform has an opinion about what a healthy service looks
like, and you either meet it or you do not run.
Scopes, deliberately narrow
The bot holds channels:join, channels:history and chat:write. Public
channels only, no user impersonation, no private conversations, no ability to
act as anyone. A tool that reads a workspace to improve governance has to be
governable itself, and the smallest scope set that does the job is the one
that survives the security review it will eventually get.
What it changed
Coverage went from whichever channels someone remembered to whichever channels
exist. Finance mentions that previously depended on a person noticing now
arrive in one place with the surrounding context attached. The work of
watching moved off people and into something that does not get busy or go on
leave.
The wider lesson is about where automation should sit. This one requires no
configuration, adapts to new channels on its own, and nobody in the workspace
has to change how they write. The best version of a system like this is one
the team never thinks about, which is also the version that is hardest to
justify building, because the evidence of it working is the absence of an
expensive surprise.

Top comments (0)