Admin means "can manage the group" for _group records (#58)#97
Merged
Conversation
Group roles were decorative — nothing distinguished member from admin, and membership rosters were governed by ordinary record write access, so anyone who could write a _group record could add or remove members. - groupRoleFromAssociations() replaces the boolean isGroupMember() check, reporting which roster label matched (admin implies member). - Permission's group variant gains an optional role: 'admin', restricting an ACL entry to admins; absent role keeps today's any-member behavior. - ScopedStack gates update/associate/dissociate/delete/undelete/ setPermissions on _group records behind admin-or-owner instead of the generic write-bit/grant check, closing the self-amplifying-roster hole. - ScopedStack.create() stamps the creator as a group's first admin so no group is ever management-orphaned. Spec and tests updated accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KDmUW3oNxPSCLf2ftqaF8q
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Group roles were decorative — nothing distinguished
memberfromadmin, and membership rosters were governed by ordinary record write access: whoever could write a_grouprecord could add or remove members, making membership self-amplifying.groupRoleFromAssociations()(packages/core/src/access.ts) replaces the booleanisGroupMember()check, reporting which roster label matched ('admin' | 'member' | null;adminimpliesmember).Permission's group variant gains an optionalrole?: 'admin'(packages/core/src/types.ts), restricting an ACL entry to admins; absentrolekeeps today's any-member behavior.ScopedStackgatesupdate/associate/dissociate/delete/undelete/restoreVersion/setPermissionson_grouprecords behind admin-or-owner instead of the generic write-bit/grant check (packages/core/src/stack.ts), closing the self-amplifying-roster hole.ScopedStack.create()stamps the creator as a group's firstadminat create time, so a group is never management-orphaned.docs/spec.mdupdated: Group section states member/admin semantics, the role-gating rule, and the bootstrap rule; Permissions section documents therolefield and updated group-resolution rule; write-bit trust-model section notes_group's carve-out.No version bump for
_group@1— the role lives in associations and in thePermissiontype, both record-level, not schema-level (consistent with the no-install-base policy from #57).Refs #51 (the general association-mutation gating this was meant to slot into — not yet implemented, so this lands as a self-contained
_group-specific gate), #49 (identity — out of scope here), #57 (no-version-bump policy).Test plan
packages/coretypecheck cleanpackages/corelint cleanpackages/coretest suite passing (387/387), including 15 new cases:write: truedoes not substitute for admin status (the vulnerability this closes)setPermissionsrequires admin, not just record authorshiprole: 'admin'ACL entry excludes a plain member; absentrolebehaves exactly as todayGenerated by Claude Code