Skip to content

Matter 1.6.1 Groupcast Implementation - #3298

Draft
Apollon77 wants to merge 92 commits into
mainfrom
groupcast
Draft

Matter 1.6.1 Groupcast Implementation#3298
Apollon77 wants to merge 92 commits into
mainfrom
groupcast

Conversation

@Apollon77

Copy link
Copy Markdown
Collaborator

... suited for SVE.

The implementation is based on current matter.js and utilizes overrides to implement the relevant changes. This will be replaced by generated classes later, but works for now.

This Draft PR will stay a bit until we move to Matter 1.6 officially

Apollon77 and others added 17 commits February 27, 2026 16:45
... suited for SVE.

The implementation is based on current matter.js and utilized overrides to implement the relevant changes. This needs to be revised later
# Conflicts:
#	packages/node/src/behaviors/access-control/AccessControlServer.ts
#	packages/protocol/src/groups/FabricGroups.ts
#	packages/types/src/clusters/access-control.ts
#	packages/types/src/clusters/group-key-management.ts
# Conflicts:
#	packages/node/src/behaviors/general-diagnostics/GeneralDiagnosticsServer.ts
#	packages/node/src/behaviors/group-key-management/GroupKeyManagementServer.ts
#	packages/node/src/behaviors/groups/GroupsServer.ts
#	packages/protocol/src/protocol/MessageExchange.ts
#	packages/testing/src/chip/matter-js-pics.properties
#	packages/types/src/clusters/access-control.d.ts
#	packages/types/src/clusters/basic-information.ts
#	packages/types/src/clusters/general-diagnostics.ts
#	packages/types/src/clusters/group-key-management.ts
#	packages/types/src/clusters/groups.ts
The Groupcast cluster PICS code in the CHIP test harness changed from
GCAST to GC. Rename the test file, update describe/chip() labels, and
drop the stale GCAST.S=1 PICS entry (GC.S=1 is authoritative).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Post-merge codegen output:
- New: types/clusters/groupcast.{d.ts,js} cluster definition
- Schema additions driven by groupcast overrides: AccessControl (AUX feature +
  AuxiliaryACL), BasicInformation + GeneralDiagnostics (DeviceLoadStatus),
  GroupKeyManagement (GCAST feature + GroupcastAdoption), Groups
- Behavior codegen drift: GroupcastBehavior, GroupcastClient, groupcast index

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Apollon77
Apollon77 force-pushed the groupcast branch 3 times, most recently from 6374936 to d286eee Compare April 16, 2026 10:20
Align with Matter 1.6.0 certification scope:

- GKM: comment out GroupcastAdoption struct + attribute (provisional)
- Groups: revert to revision 4, comment out INVALID_IN_STATE adoption
  checks (provisional per spec PR #12893)
- ACL: add quality "C" to AuxiliaryAcl attribute
- Groupcast JoinGroup/UpdateGroupKey: reject KeySetID 0 (IPK), require
  Admin privilege when key or useAuxiliaryACL fields are present
- Groupcast: reconcile unmapped keys (KeySetId=0xFFFF) at startup and
  on GKM groupKeyMap changes
- Groupcast model: add constraint "min 1" on command KeySetId fields
- Update tests for new validations and removed adoption state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Apollon77 and others added 7 commits April 16, 2026 13:12
When the AccessControl Auxiliary feature is active, Group auth mode ACL
entries with wildcard targets (empty) must not grant access to endpoint 0
(root endpoint). Groupcast commands shall not interact with clusters on
the root endpoint.

Adds auxiliaryFeatureEnabled flag to FabricAccessControl, set by
AccessControlServer during initialization and fabric updates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Groupcast cluster's membership mappings are mirrored into GKM's
  GroupKeyMap attribute so the attribute reflects the Groupcast data
  dependency (fixes TC_SC_5_1).

- SessionManager exposes an onGroupMessage observable + emitGroupMessage()
  helper. Observable is a no-op when no listener is attached.

- New typed errors GroupSessionNoKeyError and GroupSessionDecodeError
  replace generic MatterFlowError in GroupSession.decode; SessionManager
  emits NoAvailableKey / FailedAuth events using causedBy() checks.

- ExchangeManager emits MessageReplay on DuplicateMessageError in group
  sessions. InteractionServer emits Success with accessAllowed per
  dispatched group command.

- GroupcastServer dynamically attaches/detaches a listener on the
  SessionManager observable based on fabricUnderTest state, emits the
  cluster event only for the fabric under test.

- Magic number 0xFFFF factored into UNMAPPED_KEYSET_ID constant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CHIP's PeerAddress::Groupcast() wraps the spec-defined IANA address ff05::fa
through MakeIPv6PrefixMulticast(scope=5, prefixLen=64, prefix=0xff05000000000000,
groupId=0xfa), producing the unicast-prefix-based form ff35:40:ff05::fa. The spec
text just says "FF05::FA" without specifying this wrapping, so this looks like
CHIP using its existing helper rather than emitting the literal address.

Bind to the CHIP-emitted form so TC_SC_5_2 and other Groupcast tests pass.
Marked all touched sites with TODO(IANA-WRAP) for easy reversion when upstream
is clarified.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… correctly

Several related fixes to make Groupcast Listener+Sender work end-to-end with
GroupKeyManagement, FabricGroups runtime dispatch, and GroupcastTesting events:

GroupcastServer:
- #syncFabricGroups: set per-group multicast policy BEFORE assigning
  groupKeyIdMap, since the latter triggers ServerGroupNetworking to bind via
  multicastAddressFor — which reads the policy. Old order fell back to
  PerGroupId-derived addresses for IanaAddr-policy groups.
- #syncFabricGroups: also mirror Membership endpoints into gkm.state.groupTable
  (spec attribute) and fabric.groups.endpoints (runtime lookup used by
  InteractionServer for wildcard group dispatch). Without this, group msgs hit
  "No endpoints mapped to group N, skipping wildcard invoke".
- Lower maxMembershipCount/maxMcastAddrCount defaults from 254 to 44 so the
  per-fabric quota floor(44/2)=22 aligns exactly with GKM's per-fabric cap.
  Even alignment avoids spillover semantics; mirror writes never exceed the
  GKM validator.
- Wrap onGroupMessage listener with this.callback so it has a fresh action
  context when invoked after the dispatching transaction has committed
  (otherwise this.state/this.events references are expired).

GroupKeyManagementServer:
- Bump maxGroupsPerFabric default from 21 to 22 (matches Groupcast quota=22).

InteractionServer:
- For group invokes that produce no dispatched commands (wildcard expansion
  filtered out by ACL or no endpoint mappings), emit one fallback
  GroupcastTesting event per requested invoke path with result=FailedAuth and
  accessAllowed=false. TC_ACE_1_6 step 13 expects this signal.

ClientNodeTest: update expected maxGroupsPerFabric attribute to 0x16.

Tests added: endpoint mirror propagation/cleanup, two-fabric quota fill-to-
M_max scenario, GKM mirror behavior, multicast policy ordering guarantee.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Matching the CHIP SDK, only key sets referenced by GroupKeyMap count as
available for decryption: with none the result is NoAvailableKey, even
when a message fails to authenticate against an unmapped key set.
Unmapped key sets only recover the authenticated group id for Groupcast
testing reporting. Mapped candidates that all fail to decrypt remain
FailedAuth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Apollon77 and others added 28 commits July 10, 2026 18:05
# Conflicts:
#	CHANGELOG.md
#	packages/node/src/behaviors/group-key-management/GroupKeyManagementServer.ts
TC_ACE_1_6 now defines three runs testing distinct device configs:
- run1: all-clusters (groupcast on root)   -> groupcast path
- run2: all-clusters-no-groupcast           -> legacy Groups path
- run3: all-devices on-off-light --groupcast -> groupcast path

Gate the root Groupcast cluster (and the Auxiliary ACL it needs) on a
flag in the AllClusters and AllDevices roots, add the no-groupcast
AllClusters variant + subject, and include run1/run2/run3 in the test.
The groupcast-on branch is unchanged, so the default all-clusters
subject used by every other test is unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tatus

The GroupcastTesting event's AccessAllowed conflated command-execution status
with the access-control outcome: a group command that passed ACL but returned
a non-Success status (e.g. InvalidCommand, Busy) was reported AccessAllowed=false.

Per Groupcast spec §11.27.7.6.3 AccessAllowed indicates only whether Access
Control permitted the invoke. Access-denied wildcard paths are dropped before
dispatch (handled by the no-dispatch branch), so any emitted response/status
means access was allowed.

Surfaced by TC_ACE_1_6 run3 (all-devices on-off-light) targeting
ScenesManagement.ViewScene, which returns InvalidCommand for groupId 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Membership is now rebuilt by #deriveMembership from groupProperties + GKM
groupTable/groupKeyMap; commands write GKM directly and re-derive. Removes the
membership->GKM mirror and its reconcile reactors (#syncFabricGroups,
#reconcileUnmappedKeys, #reconcileMembershipEndpoints, #migrate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reate-path

configureAuxiliaryAcl on a legacy-only group must not flip its multicast
address. Shared #defaultMcastAddrPolicy() so create-path and derive agree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…upIfEmpty)

Legacy/external groupTable emptying fully deletes a group from Membership;
groupcast leaveGroup on a Sender-feature device keeps it sender-only. A
non-persistent mark set distinguishes the intentional sender-only keeps from
external removals so the offline groupTable$Changed prune deletes only the latter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…view

Add an IanaAddr survivor group so the reload address check can actually
distinguish real policy re-application from a no-op (PerGroup and "no
policy" compute the same address); drop a masking ?? false fallback on
a field that is always defined; note why two legacy-only-group tests
aren't duplicates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-independent)

ServerGroupNetworking now rebinds a group's multicast membership when its
address policy changes, so reload/fabric-replace bind the correct address
regardless of whether GKM restores endpoints before or after Groupcast applies
policy. #groupMulticastPolicy is a BasicMap so the change is observable. Also
mark retainedSenderOnly only at command commit (no leak on rollback).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Shorten the rebind/policy comments to the load-bearing WHY; drop BasicMap
base-behavior narration. Correct the join ordering comment: policy-before-
endpoints is now an optimization (rebind makes it correctness-independent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ServerGroupNetworking.close() left the joined multicast groups on the shared
UDP socket; a Node dgram socket with active memberships can hang on close(),
blocking runtime shutdown (observed only after groupcast use, needing a second
CTRL-C). Drop all memberships before teardown; make close() async and awaited.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ion conflict

Removing a fabric with a surviving second fabric clears the departing
fabric's GKM groupTable and groupKeyMap in one commit, firing both
GroupcastServer offline reactors against groupcast.state in a single
cascade. The first held the state lock across its async commit while the
second wrote synchronously, throwing SynchronousTransactionConflictError.

Reactors now record pruning intent into pending sets and derive through
#deriveNow, which takes the lock synchronously and derives inline when it
is free (preserving same-tick timing) but hands off to an async-locked,
debounced derive the moment the lock is contended, so no two writers ever
contend synchronously. Commands keep their synchronous derive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A legacy Groups.RemoveAllGroups that empties a groupcast listener group
triggers the auxiliary-ACL update only through groupcast's offline
groupTable/groupKeyMap reactors, which previously derived membership with
their own actor-less context. The resulting AuxiliaryAccessUpdated event
carried adminNodeId=null instead of the administering node.

Thread the writer's online context (delivered as the state-change event's
context argument) through the derive to #emitAuxAcl, used only to name the
admin; state writes keep using the reactor's own locked transaction. The
deferred derive (fabric-removal cascade) still emits with no admin.

Register the AccessControlServer auxiliary-ACL provider reactor offline so
the auxiliaryAcl state write uses its own transaction rather than re-opening
the provider's already-committed one during postCommit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant