Understanding Channel-Based Permissions in Macro's Messaging System

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, 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:

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 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 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 file contains the insertion logic, while 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 and 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:

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:

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:

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, 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 defines the core data structures. Database operations reside in macro_db_client (create.rs and get.rs submodules), while high-level utility functions are exposed through the entity_access_db_utils crate.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →