Permissions in chat channels
ChatOps treats every command as if you ran it from the SemiLayer Console. The bot is not a service account — it carries your identity, your role, and your access. This page explains what that means in shared channels, why some commands post privately by default, and how to think about over-share risk.
The mental model — kubectl in a shared terminal
If you've used kubectl get in a shared terminal, you already know
the model:
- The command runs as you. Your RBAC applies.
- Other people in the room can see what you run and what comes back.
- You're responsible for what gets shared.
ChatOps is the same. /semilayer search refund policy runs as
your linked SemiLayer identity. The platform service does the RBAC
check (lens visibility, org membership, environment scope). If
you can see the lens, you can search it from chat — and if
you post the result publicly, everyone in the channel sees what
you saw.
ChatOps is not a per-channel RBAC layer. Other channel members are not consulted before a search runs. The invoker's permissions decide what the bot returns; the invoker decides whether to post that result publicly.
Three layers of identity
| Layer | Required for | Set by |
|---|---|---|
| Workspace install | Anything to work at all | Org admin (one-time) |
chat_user_links | Any read/notify command | Each user (/semilayer login) |
members.role | Mutations (notify on/off, here set) | Org owner/admin |
Read commands (search, similar, query, feed, status,
config show, here show) work for any linked org member.
Mutations (notify on/off/all/none, here set, here clear,
config set/clear) require role-based permission:
notify on/off/all/none→ owner or admin (channel subscriptions are an admin lever)here set/clear→ owner or admin (channel scope is an admin lever)config set/clear→ any linked org member (your own defaults)
The bot returns a clean ephemeral error when a permission gate fails — never a stack trace, never a partial mutation.
Ephemeral by default in shared channels
Read command results post ephemerally by default — only the invoker sees them. This is the safety net for the "I forgot who's in this channel" problem:
To share the result with the channel, opt in:
--public posts to the channel as a normal message. Use it when
you mean to share. Don't use it when you don't.
DMs are not "shared" — responses there post normally without
needing --public.
Notifications (the /notify-subscribed kind, fired by the
platform when ingest fails or a quota crosses) are unaffected by
ephemeral defaults. Subscribed channels post visibly because the
channel itself opted in.
What about other channel members?
Other members of the channel are not consulted in any RBAC check. The bot doesn't know who's listening. This is intentional:
- Per-channel RBAC ("everyone in #public-help can search this lens") is a different product. Adding it would mean syncing Slack/Discord channel membership into SemiLayer ACLs in real time, which is fragile and doesn't model the security primitives most orgs already use.
- The right tool for "this channel can see this data" is to make every channel member a SemiLayer org member with the appropriate role, then trust the platform's RBAC.
- Until then: ephemeral-by-default + the explicit
--publicopt-in keeps accidental over-share from happening.
What gets logged
Workspace installs + revokes are audit-logged today
(admin_audit_log table). The Phase 5 admin surface adds:
chat.channel_scope_set/chat.channel_scope_clearedchat.subscription_added/chat.subscription_removedchat.user_linked/chat.user_unlinkedchat.command_invoked(sampled) andchat.public_post(full)
Post-hoc detection is supported by audit logs. Pre-hoc enforcement of "who can see what in this channel" is not in scope.
FAQ for security teams
Can a Slack admin who isn't a SemiLayer member see SemiLayer
data through the bot?
No. They have to run /semilayer login first, which OAuth-links
them to a SemiLayer identity that an org owner has explicitly
invited. Without that link, every read command returns "Run
/semilayer login first."
Can a developer-role member subscribe a channel to notifications
they shouldn't see?
No. notify on/off/all/none requires owner or admin role on the
org bound to the workspace. Developers + viewers can run
notify list and notify types (read-only) but not change
subscriptions.
Can the bot post messages without an explicit user action? The bot posts when:
- A linked admin runs a command and chooses to post the result
publicly (
--public). - A platform notification matches a
chat_subscriptionsrow that an admin previously created (the opt-in promise). - A future
/watchcommand (Phase 4B) fires on a schedule the creator set up.
The bot never posts based on internal heuristics, never advertises itself, never DMs unprompted.
Can a workspace see another workspace's data?
No. Each chat_workspaces row is bound to one SemiLayer org. The
bot mints its platform JWT with the linked user's full org
membership claim, and the platform service's RBAC plugin enforces
that the requested scope matches a real membership. Cross-org
leakage requires a bug at the platform-service layer, not the
chatops layer.
Where do bot tokens live?
Encrypted at rest via @semilayer/crypto (per-org KMS envelope).
The chatops service decrypts on demand only when posting outbound
messages. They never appear in API responses, logs, or admin UIs.