Slack setup for a Murmurent centre (mayor guide)¶
Murmurent treats messaging as a pluggable layer. This deployment uses Slack because the lab and centre already communicate there; a different messaging system could fill the same role with an equivalent integration. Slack is the current choice because it is already in routine use.
Murmurent uses Slack as its communication fabric in this deployment: a
private mayor↔CC channel (#murmurent-ops) where the code posts events,
a private channel per lab/core (#lab-<group>, members-only),
and #general for broadcasts. This guide is the one-time setup a
mayor does so those channels get created and populated automatically.
Looking for a lab/core's own Slack workspace instead (the PI-level setup)? See
docs/group_slack_setup.mdandmurmurent group-slack-setup <group>. This page is centre-wide only.
Slack terms used in this guide¶
Two Slack concepts recur throughout this guide and are defined here, since Slack experience varies across readers:
- Workspace: an organization's whole Slack instance, with its own
members, apps, and billing. Murmurent involves two kinds of workspace: a
centre-level workspace that the mayor runs (the subject of this
guide), and each lab/core's own, separate workspace (see
docs/group_slack_setup.md). - Channel: a single conversation stream inside a workspace. Murmurent
creates channels inside whichever workspace is relevant: generally one
per lab/core, one per project, plus shared channels like
#generaland#murmurent-ops.
Slack workspaces are created through the Slack UI rather than an API, so a few of the following steps are manual. They're marked [manual].
1. Create the workspace [manual]¶
In Slack, create a workspace named murmurent-<unique_name> (e.g.
murmurent-bioconvergence). Then:
- Create a channel named exactly
#general. Newer Slack workspaces start with a different default channel, such as#socialor a welcome channel, in place of#general, so plan on creating it yourself: + → Create a channel → name itgeneral→ Create. Murmurent broadcasts to everyone through this channel, andcentre-slack-setuplooks it up by that exact name; if it's missing,centre-slack-setupreports#general not found; create it in Slack, and broadcasts toeveryonehave nowhere to land until the channel exists. - Obtain the workspace invite link: Invite people → Copy invite link. Give this to new members during onboarding.
2. Create a bot token [manual]¶
Murmurent connects to Slack through a bot user on a Slack app you own. Create it once:
- Go to https://api.slack.com/apps and sign in as the account that owns your
murmurent-<name>workspace. - Create New App → From scratch. Name it
mayor, pick yourmurmurent-<name>workspace, Create App. Then open App Home (left sidebar) and set the bot's Display Name and Default username tomayor. This name is what PIs + members see as the sender of every DM Murmurent sends (onboarding steps, approvals), so messages consistently read as coming from the mayor. (Already have an app? Rename it here.) - In the app's left sidebar, open OAuth & Permissions.
- Scroll to Scopes → Bot Token Scopes (the Bot section; the separate "User Token Scopes" section stays empty here). Click Add an OAuth Scope and add each of these:
| Scope | Why |
|---|---|
groups:write |
create private channels (lab/core + #murmurent-ops) |
channels:manage |
create public channels (if you use any) |
chat:write |
post events + broadcasts |
im:write |
opens a real DM to a member (onboarding + decision DMs) so it lands in the member's Direct Messages rather than the bot's App messages tab |
files:write |
attaches signed bundles (e.g. bundle.json) to DMs as downloadable files rather than plain inline text |
im:history |
reads back the bot's own DM threads so Murmurent can verify a delivery actually landed |
users:read.email |
resolve a member's email → their Slack account |
groups:read, channels:read |
look up channel ids by name (e.g. #general) |
channels:join |
lets Murmurent auto-join a public channel it needs to post to (e.g. #claude-test) rather than requiring a manual /invite of the bot. Applies to public channels; private channels still use a one-time manual invite. |
- Scroll back up to OAuth Tokens for Your Workspace → Install to Workspace → Allow.
- Copy the Bot User OAuth Token: it starts with
xoxb-.
Adding scopes later? They do nothing until you Reinstall to Workspace; the
xoxb-token string usually stays the same, so verify what's actually live via thex-oauth-scopesresponse header (curl -sI -X POST https://slack.com/api/auth.test -H "Authorization: Bearer $TOKEN" | grep -i x-oauth-scopes) rather than trusting the app config page. Missing scopes degrade quietly: e.g. lackingchannels:joinsurfaces only asnot_in_channelon public channels the bot has yet to join. See docs/group_slack_setup.md → "Upgrading an existing bot" for the symptom table. 7. Back in Slack, invite the bot to#generalso it can post there: in the#generalchannel, type/invite @mayor(the bot's name). The bot is auto-added to any private channel it creates, but must be invited to pre-existing channels like#general.
3. Give Murmurent the token¶
Either export it, or store it in the token file (mode 0600):
export MURMURENT_SLACK_TOKEN=xoxb-...
# ...or, to persist it for the dashboard/server:
umask 077; printf '%s\n' 'xoxb-...' > ~/.config/murmurent/slack-token
MURMURENT_SLACK_TOKEN and the legacy SLACK_BOT_TOKEN both work.
4. Point the centre at the workspace¶
Set the workspace id + invite link on the centre (via murmurent centre-init
--slack-workspace T… --slack-invite-url https://join…, or the /registrar
profile editor for an existing centre: slack_workspace and slack_invite_url
in centre.md).
5. Verify + provision the centre channels¶
murmurent centre-slack-smoke # confirms the bot token can create a channel
murmurent centre-slack-setup # creates #murmurent-ops, wires #general + admin broadcasts
centre-slack-setup creates the private mayor↔CC channel (#murmurent-ops,
stored as mayor_channel_id) and seeds the broadcast map (admin → the mayor
channel, everyone → #general). Re-running is safe (idempotent).
What happens automatically after this¶
- New lab/core (created via a join approval or the registrar's create
button): a private channel
#lab-<group>is created, the group's members are invited, and the channel id is stored on the group, as long as the token is set (a join approval also needsslack_workspace). If the name is already taken, the existing channel is used. If Slack is not set up, the group is still created and the dashboard says that no channel was made. - Members join and leave with the group: adding a core member or issuing a lab member's card adds them to the channel; removing a core member, or deactivating a lab member, removes them; reactivating adds them back.
- Broadcasts:
murmurent broadcast --audience everyone …lands in#general;--audience adminreaches the mayor channel. - Join requests: the mayor is notified in the
admin(mayor) channel; the requester is DM'd (or the source GitHub issue is commented) on a decision.
New members¶
Give a new member the workspace invite link (step 1). Once they've joined the
workspace and their email is on their member file (email: frontmatter), the
next provision/reconcile of their lab adds them to that lab's channel, and only
that lab's channel. Members with an email recorded on their member file resolve
to a Slack account automatically; anyone missing that field is reported as
unresolved until it's added.
Notes¶
- Free/Pro Slack limits invites to the invite-link step above (joining a channel is automatic once someone is already in the workspace). Full workspace-invite automation needs a paid admin token.
- Channel names are
lab-plus the group's name, normalized to Slack rules (lowercase,[a-z0-9_-], ≤80):mh→#lab-mh. A name that already starts withlab_is not repeated:lab_mh→#lab-mh. Groups created before this naming keep the channel they already have. - Adding a member to the channel needs their email on their member file,
to find their Slack account. Adding a core member accepts an optional
emailfor this (POST /api/registrar/core/<core>/members; the dashboard has no form for it yet).