How Macro's Channel-Based Permission System Works with @mentions: A Complete Technical Guide
Macro's @mention system automatically grants channel-level access to documents, tasks, and projects when they are mentioned in channel messages, using atomic database operations across the comms_entity_mentions and channel_share_permission tables.
Macro implements a share-on-mention architecture that links communication and permissions. When you type @ followed by a document name, task, or project in a channel message, the backend executes a coordinated transaction that records the mention and propagates access rights to all channel members. This design eliminates manual sharing while keeping permissions synchronized with membership changes.
Core Architecture: Two-Stage Permission Flow
The permission system operates through two distinct but atomic stages. Understanding this flow is essential for debugging access issues or extending the platform.
Stage 1: Entity-Mention Recording
Every @mention in a channel message creates a permanent record in the comms_entity_mentions table. This table serves as the audit trail and trigger source for permission propagation.
The insertion logic resides in crates/comms_db_client/src/messages/create_message_mentions.rs. The function create_message_mentions handles bulk insertion when a message contains multiple mentions:
use comms_db_client::messages::create_message_mentions::{
CreateMessageMentionOptions, SimpleMention,
};
let mentions = vec![
SimpleMention {
entity_type: "document".into(),
entity_id: "doc123".into(),
user_id: None
},
SimpleMention {
entity_type: "task".into(),
entity_id: "task456".into(),
user_id: None
},
];
let opts = CreateMessageMentionOptions {
source_entity_type: "message".into(),
source_entity_id: message_id.clone(),
mentions,
};
comms_db_client::messages::create_message_mentions::create_message_mentions(&pool, opts).await?;
The underlying SQL generated by this function:
INSERT INTO comms_entity_mentions
(id, source_entity_type, source_entity_id, entity_type, entity_id, user_id)
VALUES
(gen_random_uuid(), 'message', $1, 'document', 'doc123', NULL),
(gen_random_uuid(), 'message', $1, 'task', 'task456', NULL);
The comms_entity_mentions schema captures five critical fields:
source_entity_type— always'message'for channel mentionssource_entity_id— the UUID of the containing messageentity_type— the category of mentioned item (document,task,call,chat,project)entity_id— the UUID of the target itemuser_id— populated only for @user mentions
Stage 2: Channel Permission Granting
After mention records are created, the share-on-mention service automatically grants channel-wide access. This logic lives in crates/macro_db_client/src/share_on_mention/mod.rs.
The service filters mentions to only shareable item types — documents, tasks, calls, chats, and projects. User mentions trigger notifications but bypass permission changes since users are not shareable resources.
// Called after entity_mention insertion
for mention in mentions {
if let Ok(item_type) = ShareableItemType::from_str(&mention.entity_type) {
macro_db_client::share_on_mention::grant_share(
&pool,
channel_id,
item_type,
mention.entity_id,
).await?;
}
}
The grant_share function creates channel_share_permission rows that bind the target entity to the channel. These rows enable inherited access: any current or future channel member automatically gains access to the mentioned item.
End-to-End Flow: From Message Submit to Permission Grant
The complete sequence executes in tooling/seed_cli/src/service/db/mod.rs and production message handlers:
| Step | Operation | Code Location |
|---|---|---|
| 1. Message submission | UI sends payload with entity_mentions array |
Client → API gateway |
| 2. Mention persistence | INSERT INTO comms_entity_mentions |
create_message_mentions.rs |
| 3. Permission trigger | update_share_permissions_for_mention invoked |
tooling/seed_cli/src/service/db/mod.rs |
| 4. Share grant | channel_share_permission rows created |
macro_db_client/src/share_on_mention/mod.rs |
| 5. Member notification | Real-time updates pushed to channel members | Notification service |
Automatic Permission Revocation on Membership Changes
Channel-based permissions must contract as well as expand. When a user leaves a channel, Macro cleans up access through delete_entity_mentions_for_entities in crates/channels/src/domain/service.rs:
async fn delete_entity_mentions_for_entities(
&self,
fetched_entity_ids: Vec<String>,
channel_id: Uuid,
) -> Result<()> {
// Removes entity_mention rows and related channel_share_permission rows
// ensuring former members lose access to items mentioned while they were present
}
This cleanup maintains principle of least privilege — access persists only while membership is active. The function operates on both table layers:
- Deletes
comms_entity_mentionsrows where the user's messages contained mentions - Cascades to remove
channel_share_permissionentries, which severs the access inheritance
Boundary Conditions: Where @mentions Do NOT Share
Macro deliberately restricts the share-on-mention behavior to prevent notification spam and premature access grants.
Thread-Container Requirement
Only mentions within thread containers trigger sharing:
- ✅ Channel messages
- ✅ Comment threads
Mentions in document or task bodies do not automatically share the item or generate notifications. This prevents alerts during drafting when mentions are added for reference rather than distribution.
User Mention Special Case
When entity_type = 'user', the system:
- Creates a notification for the mentioned user via the notification service
- Skips permission changes — users cannot be "shared" like documents or tasks
This distinction preserves the semantic difference between notifying a person and granting access to a resource.
Key Files and Their Responsibilities
| File | Role |
|---|---|
crates/comms_db_client/src/messages/create_message_mentions.rs |
Bulk insertion of mention records |
crates/comms_db_client/src/entity_mentions/mod.rs |
Public API for mention deletion and cleanup |
crates/macro_db_client/src/share_on_mention/mod.rs |
Core permission-granting service |
crates/channels/src/domain/service.rs |
Membership change handling and permission revocation |
tooling/seed_cli/src/service/db/mod.rs |
End-to-end orchestration for seeding and testing |
apps/docs/concepts/mentions.mdx |
User-facing documentation |
Summary
- Atomic two-stage operation: Every @mention in a channel message creates both an audit record (
comms_entity_mentions) and access rights (channel_share_permission) - Inherited permissions: Channel membership alone controls access to mentioned items — no per-user sharing required
- Automatic cleanup: Membership changes trigger immediate permission revocation via
delete_entity_mentions_for_entities - Scoped triggering: Only thread-container mentions share; body mentions and user mentions follow different paths
- Shareable type filter: Only documents, tasks, calls, chats, and projects receive channel permissions
Frequently Asked Questions
How does Macro handle multiple @mentions in a single message?
Macro processes mentions as a batch through create_message_mentions, which accepts a vector of SimpleMention structs. Each mention generates its own comms_entity_mentions row, and the share-on-mention service iterates through all valid targets to grant channel access. This batch approach ensures atomicity — either all mentions and permissions succeed, or the transaction rolls back.
What happens to permissions when a mentioned item is deleted?
The entity_mentions table maintains foreign key relationships or soft-delete tracking (implementation-dependent in the Macro codebase). When an entity is deleted, the delete_entity_mentions_for_entities function in crates/channels/src/domain/service.rs cleans up associated rows. This prevents orphaned permission grants and ensures the channel_share_permission table stays consistent with actual existent resources.
Can administrators override or revoke share-on-mention permissions manually?
The channel-based permission system treats channel_share_permission rows as the source of truth. Administrators can directly manipulate these rows through the channel_permission CRUD APIs referenced in macro_db_client/src/share_on_mention/mod.rs. However, manual revocation may be reverted if the original mention record persists and a membership change triggers cleanup logic — the system prioritizes automated consistency over manual overrides.
Why don't document body mentions trigger sharing?
According to apps/docs/concepts/mentions.mdx, this limitation prevents premature notification and access during active drafting. Authors frequently reference documents, tasks, or colleagues while composing content, before the document is ready for distribution. Restricting share-on-mention to thread containers (messages and comments) ensures sharing is an intentional, public act rather than a byproduct of editing.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →