# Understanding Channel-Based Permissions in Macro's Messaging System

> Learn about Macro's channel based permissions. Understand how SharePermission and ChannelSharePermission grant granular read, write, and admin access to resources within channels.

- Repository: [Macro/macro](https://github.com/macro-inc/macro)
- Tags: how-to-guide
- Published: 2026-08-18

---

**Macro implements channel-based permissions through a dual-layer system where `SharePermission` entities grant access to resources and `ChannelSharePermission` rows bind those permissions to specific channels with granular access levels including read, write, and admin.**

The `macro-inc/macro` repository defines a sophisticated access control model that treats channels as first-class principals in its messaging architecture. By linking share permissions directly to channel identifiers, the system enables fine-grained control over which conversations can view, modify, or administrate shared entities such as documents and emails.

## Core Permission Architecture

### SharePermission and ChannelSharePermission

Macro distinguishes between generic permissions and channel-specific bindings. A **`SharePermission`** represents the base ACL entry that grants a principal (user, team, or channel) access to a shareable entity. The **`ChannelSharePermission`** struct serves as the bridge table that associates these permissions with specific communication channels.

According to the source in [`crates/models_permissions/src/share_permission/channel_share_permission.rs`](https://github.com/macro-inc/macro/blob/main/crates/models_permissions/src/share_permission/channel_share_permission.rs), the system supports multiple channel types including public channels, private conversations, team-derived spaces, and direct messages (DMs), each identified by a unique UUID and owner relationship.

### Access Levels and Data Schema

The permission model uses a strongly-typed `AccessLevel` enum to define capabilities. The database schema stores these relationships in the `ChannelSharePermission` table with the following Rust representation:

```rust
pub struct ChannelSharePermission {
    pub share_permission_id: String, // FK → SharePermission.id
    pub channel_id: String,          // FK → CommsChannels.id
    pub access_level: AccessLevel,   // Enum: Read/Write/Admin
}

```

Mutable operations utilize the `UpdateChannelSharePermission` counterpart, which API endpoints consume to create, modify, or delete permission rows.

## Permission Lifecycle

The system enforces channel-based access through a three-phase lifecycle:

1. **Create Base Permission** – A `SharePermission` is first created for the target entity (document, email, or message), establishing the base access grant to a principal.

2. **Bind to Channel** – The `insert_channel_share_permission` function in [`crates/macro_db_client/src/share_permission/channel_permission/create.rs`](https://github.com/macro-inc/macro/blob/main/crates/macro_db_client/src/share_permission/channel_permission/create.rs) links the `share_permission_id` to a specific `channel_id` alongside an `access_level`, creating the `ChannelSharePermission` row.

3. **Evaluate Access** – When users post messages or request resources, the chat service queries [`get_permissions.rs`](https://github.com/macro-inc/macro/blob/main/get_permissions.rs) in `crates/chat/src/outbound/postgres/queries/` to join `SharePermission` with `ChannelSharePermission` and resolve effective permissions.

## Database Implementation with SQLx

All database interactions leverage **SQLx** compile-time-checked queries for type safety. The [`crates/macro_db_client/src/share_permission/channel_permission/create.rs`](https://github.com/macro-inc/macro/blob/main/crates/macro_db_client/src/share_permission/channel_permission/create.rs) file contains the insertion logic, while [`crates/macro_db_client/src/share_permission/channel_permission/get.rs`](https://github.com/macro-inc/macro/blob/main/crates/macro_db_client/src/share_permission/channel_permission/get.rs) handles retrieval operations.

For higher-level operations, the `entity_access_db_utils` crate provides idempotent helpers such as `upsert_channel_share_permission`, wrapping raw SQL operations to ensure safe permission updates without duplicate entries.

## API Surface and Endpoints

The permission system exposes HTTP endpoints defined in service-specific Swagger files. Key operations include:

- **`POST /entity-mentions`** – Accepts `UpdateChannelSharePermission` payloads to create new channel share permissions, granting channels access to documents or other entities.

- **`DELETE /entity-mentions/:id`** – Removes the `ChannelSharePermission` row, revoking channel access to the associated resource.

These handlers reside in [`services/document_storage_service/src/api/swagger.rs`](https://github.com/macro-inc/macro/blob/main/services/document_storage_service/src/api/swagger.rs) and [`services/document_cognition_service/src/api/swagger.rs`](https://github.com/macro-inc/macro/blob/main/services/document_cognition_service/src/api/swagger.rs), providing RESTful access to the underlying permission logic.

## Practical Implementation Examples

### Granting a Channel Read Access

To link a document's share permission to a channel with read-only access:

```rust
use models_permissions::share_permission::channel_share_permission::{
    ChannelSharePermission, UpdateChannelSharePermission, AccessLevel,
};
use macro_db_client::share_permission::channel_permission::create::insert_channel_share_permission;

// Assume `doc_sp_id` is the SharePermission ID for the document
let channel_perm = UpdateChannelSharePermission {
    share_permission_id: doc_sp_id.clone(),
    channel_id: channel_uuid.to_string(),
    access_level: AccessLevel::Read,
};

let result = insert_channel_share_permission(&db_pool, &channel_perm).await?;
match result {
    InsertChannelSharePermissionResult::Inserted => println!("Permission added"),
    InsertChannelSharePermissionResult::AlreadyExists => println!("Permission already present"),
}

```

### Querying Channel Permissions

Retrieve all permissions associated with a specific channel:

```rust
use macro_db_client::share_permission::channel_permission::get::get_channel_permissions;

let perms = get_channel_permissions(&db_pool, &channel_uuid.to_string()).await?;
for perm in perms {
    println!(
        "Entity {} → {} access",
        perm.share_permission_id, perm.access_level
    );
}

```

### HTTP API Request

Grant write access via the REST endpoint:

```json
POST /entity-mentions
{
  "share_permission_id": "sp-12345",
  "channel_id": "c-67890",
  "access_level": "Write"
}

```

## Summary

- **Macro's channel-based permissions** utilize a junction table pattern where `ChannelSharePermission` links `SharePermission` entities to specific channels.
- The **access control model** supports read, write, and admin levels through a strongly-typed `AccessLevel` enum defined in the permissions models crate.
- **Database operations** use SQLx for compile-time safety, with helper functions in `macro_db_client` and `entity_access_db_utils` crates handling CRUD operations.
- **REST endpoints** at `/entity-mentions` provide the primary interface for creating and deleting channel permissions, documented in the document storage service Swagger files.
- **Permission evaluation** occurs in the chat service through joined queries against `ChannelSharePermission` and `SharePermission` tables.

## Frequently Asked Questions

### What is the difference between SharePermission and ChannelSharePermission?

`SharePermission` is the base ACL entity that grants access to a resource for any principal (user, team, or channel). `ChannelSharePermission` is a specific implementation that binds a `SharePermission` to a channel identifier with a defined access level. This separation allows the same underlying permission logic to work across different contexts while maintaining strict type safety for channel-specific operations.

### How do you programmatically grant a channel write access to a document?

Construct an `UpdateChannelSharePermission` struct with the document's `share_permission_id`, the target `channel_id`, and `AccessLevel::Write`. Pass this to `insert_channel_share_permission` from `macro_db_client`, which handles the SQL insertion and returns a result indicating whether the permission was newly created or already existed.

### Where does the chat service evaluate channel permissions?

The chat service evaluates permissions in [`crates/chat/src/outbound/postgres/queries/get_permissions.rs`](https://github.com/macro-inc/macro/blob/main/crates/chat/src/outbound/postgres/queries/get_permissions.rs), which joins the `SharePermission` and `ChannelSharePermission` tables to resolve what actions channel members may perform on referenced entities during message processing.

### What crates contain the core permission models and database utilities?

The `models_permissions` crate at [`crates/models_permissions/src/share_permission/channel_share_permission.rs`](https://github.com/macro-inc/macro/blob/main/crates/models_permissions/src/share_permission/channel_share_permission.rs) defines the core data structures. Database operations reside in `macro_db_client` ([`create.rs`](https://github.com/macro-inc/macro/blob/main/create.rs) and [`get.rs`](https://github.com/macro-inc/macro/blob/main/get.rs) submodules), while high-level utility functions are exposed through the `entity_access_db_utils` crate.