mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
The engine knew the word "admin", which RFC 9420 does not have. MIP-03's
authorization gate, the admin-depletion guard, the agent-text-stream role
check on join and the self-demote-before-SelfRemove rule all ran inside
`MlsGroup`, and `MlsGroup.create` defaulted every group to Marmot's leaf
capabilities and `required_capabilities`. A plain RFC 9420 group could not be
created at all -- every group came out requiring `marmot_group_data` -- and
`commit()` derived its pre-commit secret under a hardcoded
`MLS-Exporter("marmot", "group-event", 32)`.
`MlsGroupPolicy` is the seam. Three hooks the engine calls where RFC 9420
defers to the application (authorizeCommit, authorizeSelfRemove, validateJoin)
and four values it reads (leaf capabilities, required_capabilities, extra
known extension types, the commit exporter label). `MarmotGroupPolicy` carries
all of them, with the enforcement bodies moved verbatim.
One argument now selects a whole profile: `MlsGroup.create(id, policy =
MarmotGroupPolicy)` gets the rules, the capabilities and the exporter binding
together, which is why the change is one added argument per call site rather
than five.
The default is permissive, and that direction is a deliberate trade. Closed
would make the engine unusable without a policy and would push callers into
writing an allow-everything one anyway. The cost is that a group restored
without its policy silently drops the binding's rules -- a policy is
behaviour, not state, so it is not in `MlsGroupState`. All ten production
construction sites are in `marmot/` and all ten now name it.
Policies see a read-only `GroupView`, not the group. A policy handed the
`MlsGroup` could commit or rotate keys from inside the check meant to gate
those things.
The tests were the safety net, exactly as intended: the first run after the
defaults changed failed 13, every one a Marmot test that had been relying on
`MlsGroup.create` to make its group Marmot-shaped. Those now say so. The other
~180 construction sites kept passing on the permissive default, which is
itself the result worth having -- they are engine tests and they no longer
need Marmot to run.
Adds 8 tests for the seam itself. Five pin the engine half with a recording
policy (the hooks are consulted, a refusal aborts before the epoch moves, a
refused SelfRemove is not staged, the exporter secret comes from the policy).
Three pin the divergence from Marmot's side, including the half no other test
covers: the same group at the same state accepts the same commit once the
policy is gone. Verified by mutation -- ignoring the policy in `commit()` and
re-hardcoding the exporter label each kill exactly their guarding tests.
Also moves the last engine->marmot dependency out of `MlsKeyPackage`:
`last_resort_key_package` is `draft-ietf-mls-extensions-10`'s component, not
Marmot's, so it joins `app_components` and `safe_aad` in `ComponentsList` and
`AppComponentIds` re-exposes all three.
`quartz/mls/` no longer imports `quartz/marmot/` anywhere. Suite 5074 -> 5082,
green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012BfD4txdnsaPRXmNXbup9n