# How Macro's Channel-Based Permission System Works with @mentions: A Complete Technical Guide

> Explore Macro's channel based permission system and how @mentions grant access to documents and tasks. Understand the technical mechanics.

- Repository: [Macro/macro](https://github.com/macro-inc/macro)
- Tags: deep-dive
- Published: 2026-08-16

---

**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`](https://github.com/macro-inc/macro/blob/main/crates/comms_db_client/src/messages/create_message_mentions.rs). The function `create_message_mentions` handles bulk insertion when a message contains multiple mentions:

```rust
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:

```sql
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 mentions
- `source_entity_id` — the UUID of the containing message
- `entity_type` — the category of mentioned item (`document`, `task`, `call`, `chat`, `project`)
- `entity_id` — the UUID of the target item
- `user_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`](https://github.com/macro-inc/macro/blob/main/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.

```rust
// 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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/create_message_mentions.rs) |
| 3. Permission trigger | `update_share_permissions_for_mention` invoked | [`tooling/seed_cli/src/service/db/mod.rs`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/crates/channels/src/domain/service.rs):

```rust
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_mentions` rows where the user's messages contained mentions
- Cascades to remove `channel_share_permission` entries, 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`](https://github.com/macro-inc/macro/blob/main/crates/comms_db_client/src/messages/create_message_mentions.rs) | Bulk insertion of mention records |
| [`crates/comms_db_client/src/entity_mentions/mod.rs`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/crates/macro_db_client/src/share_on_mention/mod.rs) | Core permission-granting service |
| [`crates/channels/src/domain/service.rs`](https://github.com/macro-inc/macro/blob/main/crates/channels/src/domain/service.rs) | Membership change handling and permission revocation |
| [`tooling/seed_cli/src/service/db/mod.rs`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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.