If threads become DAG projections (depends on #7), ACL needs to live on messages rather than a separate Thread entity.
Proposal: per-message ACL with inheritance from parent — each message inherits parent's access scope by default. ACL can be narrowed (private side-branch) or widened (invite someone into a branch) at any node. Each ACL change creates a new encryption scope (MLS sub-group).
ACLScope {
scope_id // = message_id where ACL is defined
members[] // recipient set
mls_epoch // MLS session for this scope
}
The DAG naturally partitions into zones of uniform ACL. Same zone = same MLS key, zero overhead. ACL change = new zone, new MLS sub-group — expensive but rare.
Visibility rule: access to a message does NOT grant access to its parents if their ACL excludes you. UI shows "N hidden messages above" (consistent with ADR-006).
Open questions: MLS sub-group management complexity at scale, context problem for users who see a message without parent context, group key management choice (MLS vs sender keys vs hybrid).
If threads become DAG projections (depends on #7), ACL needs to live on messages rather than a separate Thread entity.
Proposal: per-message ACL with inheritance from parent — each message inherits parent's access scope by default. ACL can be narrowed (private side-branch) or widened (invite someone into a branch) at any node. Each ACL change creates a new encryption scope (MLS sub-group).
The DAG naturally partitions into zones of uniform ACL. Same zone = same MLS key, zero overhead. ACL change = new zone, new MLS sub-group — expensive but rare.
Visibility rule: access to a message does NOT grant access to its parents if their ACL excludes you. UI shows "N hidden messages above" (consistent with ADR-006).
Open questions: MLS sub-group management complexity at scale, context problem for users who see a message without parent context, group key management choice (MLS vs sender keys vs hybrid).