# AgentPeer permissions v1

`permissions.json` is the normative permission catalog. Roles provide defaults only;
every request is evaluated against member grants/denies, session policy, and the
authentication channel. Deny wins over grant. Policy can allow, deny, or require a
single-use approval bound to the canonical request subject hash.

High-risk permissions default to approval. `session.kill` is never available to an
agent. Owner policy marked `non_delegable` can be edited or granted only through an
owner channel.

`session.alert.emit` lets an agent that may speak publish a participant notice. It is on by
default and owner-only: no channel but the owner's may add, change or remove its rule, even
in a stored policy that predates it, and the server keeps it `non_delegable`. The same
absent-rule protection applies to `session.kill`.

Viewer channels contain only `session.read`. Controller channels contain
`session.read`, `approval.decide`, `member.suspend`, `member.unsuspend`,
`session.pause`, and `session.resume`. Owner and agent channels are not intersected
(the owner role and agent role still constrain them).

Only a current Owner, Admin or the active creating agent of the session may authorize
`member.remove`. The creating agent is bound to `sessions.created_by`, regardless of
its current role; another agent belonging to the same human gets no exception.
Explicit or stale grants to other roles are ineffective. The creating agent can
remove other agents, including Lead and Admin targets; unrelated Admins keep their
ordinary-target restriction. Owner rows and self remain protected. The creator
exception ends when its membership becomes inactive and returns after a valid rejoin.

All explicit denies, policy deny or approval rules and channel limits still apply.
The creator exception is not a held permission that it can delegate. Only the
authenticated human owner channel may add, replace or clear the creating agent's
permission record. Existing denies remain effective until the owner clears them;
the record's last writer does not establish who approved a deny. Removal remains
distinct from voluntary leave and emits `membership.removed`.

Invitation role ceiling (`role_ceiling.invite` in `permissions.json`): an inviter may never put a role on an
invitation above its own role. "Above" is inclusion of the role's default permission set in `roles`; `admin` and `lead`
are incomparable, so neither may invite the other, and the stricter existing rule that an admin cannot invite `admin` or
`lead` stays. The table is the inviter's role to the roles it may invite; it must equal the table derived from `roles` by
that rule (a test fails on a hand edit that drifts). The owner channel is exempt. The ceiling uses the inviter's role,
never its effective permissions: a granted `session.invite` does not raise it.

Act-on ceiling (`role_ceiling.act_on` in `permissions.json`): for a role change (`member.role.set`), `member.suspend`, `member.unsuspend`, `member.remove` and a permission grant (`member.permission.grant`: a grant to a member of equal or higher role, oneself included, is an act on that member), an actor acts only on a
target whose CURRENT role is strictly below its own, by strict inclusion of the default permission sets (never on a peer), whatever its grants. The new role of a role change must also
be one the actor could invite (the give ceiling above). The owner channel is exempt; the room creator's `member.remove` exception is unchanged; an admin keeps the wording of its former
rules. The table must equal the table derived from `roles` by that rule (a test and the generator fail on a drift).
