Files
iios/packages
maaz519 0336621c01 feat(threads): govern roster reads + add a bulk participant primitive
Phase A of channel-roster import (add everyone from another channel).

SECURITY: listParticipants was gated by iios.thread.read, which has no case in
DevOpaPort and so fell through to default-allow — any caller in a scope could
enumerate ANY thread's members, including private channels they aren't in. Since
PlatformModule binds LocalDevPorts unconditionally (no real OPA adapter exists),
that was live. Adds iios.thread.participant.list: members only, public channels
exempt, ungoverned threads unchanged. This is also Zoom's rule for this feature
('you must be a member of the channel to invite all of its members').

Adds MessageService.addParticipants(): many users in ONE governed call, capped at
MAX_BULK_PARTICIPANTS (200), idempotent (already-members are 'skipped'), and
deliberately non-atomic so one unresolvable user can't sink an import — outcome
is reported per user as {added, skipped, failed}. The dm two-person cap now reads
targetCount so it holds for a batch, not just one add at a time. New REST route
POST /v1/threads/:id/participants/bulk.

The kernel stays generic: it takes an explicit userId list and never learns where
that list came from — chat meaning lives only in the policy plane.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:43:03 +05:30
..