How Macro Implements Channel-Based Permissions: Architecture and Code

Macro's channel-based permissions system uses a share-permission model built on the ChannelSharePermission struct, storing access levels in a database table and enforcing them via JWT tokens validated at runtime.

The macro-inc/macro repository implements a robust authorization layer for collaborative documents through its channel-based permissions system. This architecture allows fine-grained control over who can view, comment on, or edit specific channels within a workspace. The implementation spans multiple crates and services, utilizing Rust's type system and PostgreSQL's atomic operations to ensure secure, consistent access control.

Core Data Structures in the Permissions Model

ChannelSharePermission and AccessLevel

The foundation of the system lives in crates/models_permissions/src/share_permission/channel_share_permission.rs, which defines the ChannelSharePermission struct. This struct stores a channel_id paired with an AccessLevel, representing a literal permission grant.

The AccessLevel enum, defined in crates/models_permissions/src/share_permission/access_level.rs, establishes a hierarchy of View, Comment, and Edit privileges. These levels implement comparison operators, allowing runtime checks using the > operator to verify if a user meets the required access threshold.

UpdateChannelSharePermission Payload

Modifications to permissions flow through the UpdateChannelSharePermission struct, which carries an UpdateOperation enum with variants Add, Remove, and Replace. This payload structure enables atomic batch updates while maintaining type safety across API boundaries.

Database Representation

The ChannelSharePermissionRow struct maps to the channel_share_permissions table, linking a share_permission_id to specific channels via channel_id and access_level columns. This relational design supports efficient querying and maintains referential integrity through foreign key constraints.

Database Operations and Atomic Updates

Permission persistence relies on SQL INSERT … ON CONFLICT … DO UPDATE statements wrapped in the create_channel_share_permissions and update_channel_share_permissions functions. These routines accept slices of ChannelSharePermission objects, enabling batch operations that either establish new permissions or modify existing ones atomically.

When the Document Storage Service processes mutations, it utilizes the code in services/document_storage_service/src/outbound/entity_mutation.rs to attach channel permissions to document requests, ensuring the database state remains synchronized with user intentions.

Runtime Enforcement and JWT Validation

Token Generation

The permission verification flow begins in the Document Storage Service, which generates JWT tokens embedding a DocumentPermissionsToken. This token contains a serialized list of ChannelSharePermission objects tied to the authenticated user, effectively packaging the user's channel access rights into a cryptographically secure format.

Request Validation

The sync-service handles enforcement through services/sync-service/src/auth.rs, where incoming requests undergo JWT validation. The service extracts the channel permissions from the token and compares the stored AccessLevel against operation requirements using the enum's ordering logic. Requests failing this check receive an immediate 403 Forbidden response, while authorized requests proceed to the handler.

Implementation Examples

The following examples demonstrate practical usage patterns drawn from the macro-inc/macro source code.

Creating a Channel Share Permission

use models_permissions::share_permission::channel_share_permission::{
    ChannelSharePermission, UpdateChannelSharePermission, UpdateOperation,
};
use models_permissions::share_permission::access_level::AccessLevel;

// Build a new permission granting Edit access to channel "c123"
let permission = ChannelSharePermission {
    channel_id: "c123".into(),
    access_level: AccessLevel::Edit,
};

// Persist it (inside an async context)
macro_db_client::share_permission::channel_permission::create::create_channel_share_permissions(
    &vec![permission],
).await?;

Updating a Channel Permission via the API

POST /api/v1/channel-permissions
{
  "operation": "replace",
  "channel_id": "c123",
  "access_level": "comment"
}

The handler converts this payload into UpdateChannelSharePermission, then calls the database routine with the Replace operation.

Checking Permissions in Request Handlers

use models_permissions::share_permission::access_level::AccessLevel;

// `user_perms` is a Vec<ChannelSharePermission> extracted from the JWT
fn has_edit(user_perms: &[ChannelSharePermission], channel_id: &str) -> bool {
    user_perms.iter()
        .find(|p| p.channel_id == channel_id)
        .map_or(false, |p| p.access_level >= AccessLevel::Edit)
}

If has_edit returns true, the request proceeds; otherwise the service returns a 403 Forbidden error.

Summary

  • Core Abstraction: The ChannelSharePermission struct in crates/models_permissions/src/share_permission/channel_share_permission.rs represents user access to specific channels through a combination of channel_id and AccessLevel.
  • Persistence Layer: Permissions store in the channel_share_permissions table via create_channel_share_permissions and update_channel_share_permissions, using atomic upserts to maintain consistency.
  • Access Hierarchy: The AccessLevel enum defines View < Comment < Edit ordering, enabling runtime comparison operations for authorization checks.
  • Security Model: The Document Storage Service packages permissions into JWT tokens as DocumentPermissionsToken, while the sync-service validates these tokens in services/sync-service/src/auth.rs to enforce access control on every request.
  • Type Safety: All permission structs derive ToSchema via utoipa, ensuring automatic OpenAPI documentation and API contract validation.

Frequently Asked Questions

What database table stores channel permissions in Macro?

The channel_share_permissions table persists channel access rights, with rows represented by the ChannelSharePermissionRow struct linking share_permission_id to specific channel_id values and their associated access_level.

How does Macro handle atomic updates to multiple channel permissions?

The system uses PostgreSQL's INSERT … ON CONFLICT … DO UPDATE syntax wrapped in the create_channel_share_permissions and update_channel_share_permissions functions, accepting vectors of ChannelSharePermission objects to batch modifications atomically.

What are the different access levels available in Macro's channel permissions?

The AccessLevel enum defines three hierarchical levels: View, Comment, and Edit. These levels support comparison operators, allowing code to check if a user's access meets or exceeds required thresholds using standard comparison syntax.

How does the sync-service validate channel permissions?

The sync-service extracts ChannelSharePermission vectors from JWT tokens containing DocumentPermissionsToken claims, then validates requests against these permissions in services/sync-service/src/auth.rs, returning 403 Forbidden for unauthorized operations.

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 →