Documentation
¶
Overview ¶
Package im implements the Matter Interaction Model per Matter Core Specification §10.6.
The Interaction Model is the application-layer protocol that runs on top of the Secure Channel session ([..]/secure/channel). It defines four interactions:
- Read — fetch attribute values
- Write — set attribute values
- Invoke — dispatch cluster commands
- Subscribe — register for ongoing attribute reports
All messages are TLV-encoded ([..]/tlv) with context-tagged fields inside top-level Structures. Path Information Blocks (AttributePathIB, CommandPathIB) use the LIST type per spec.
Architecture ¶
This package owns:
- Path types (ConcreteAttributePath, ConcreteCommandPath) with wildcard support and TLV codecs.
- Status codes (per spec §8.10) and StatusIB.
- The four request / response message types.
- The Dispatcher interface — the surface the cluster-server side fills in so the IM layer stays blind to specific cluster IDs.
This package does NOT own:
- Cluster-attribute encoding — lives in cluster/*.
- Subscription state across a daemon restart — the SQLite store lives in store; cross-restart resumption is deferred by design (notes/parity/by_design.md).
This package DOES own the Timed Request / Timed Action handshake (timed.go: TimedRequest, UnmarshalTimedRequestTLV, StatusResponse per Matter §8.7); the Write/Invoke Timed-flag gate is enforced in bridge/receive.go (checkTimedGate → StatusNeedsTimedInteraction).
Wildcard semantics ¶
A path field is "concrete" when its Has* flag is set; otherwise it is a wildcard matching every value. The wildcard semantics let a commissioner read every attribute on every endpoint of every cluster with a single request — the Dispatcher expands wildcards on the device side.
Index ¶
- Constants
- Variables
- func DataVersionFilterSize(f DataVersionFilter) int
- func DeferAfterResponse(ctx context.Context, fn func()) bool
- func DispatchEndpoints(ctx context.Context, d Dispatcher, req InvokeRequest) map[uint16]bool
- func FabricFilterFromContext(ctx context.Context) (filtered bool, fabricIndex uint8)
- func IsListAppendWrite(ctx context.Context) bool
- func IsPASEFromContext(ctx context.Context) bool
- func IsRequestOpcode(op uint8) bool
- func ListChunkedByDefault(cluster, attribute uint32) bool
- func LookupFabricFilter(ctx context.Context) (filtered bool, fabricIndex uint8, ok bool)
- func MarshalInvokeRequestTLV(enc *tlv.Encoder, req InvokeRequest, fieldsWriter CommandFieldsWriter)
- func MatchDataVersionFilter(filters []DataVersionFilter, endpoint uint16, cluster uint32) (uint32, bool)
- func SubjectFromContext(ctx context.Context) (nodeID uint64, cats []uint32)
- func SuppressResponseOf(payload []byte) bool
- func TimedInteractionFromContext(ctx context.Context) (timed, known bool)
- func UnsupportedCommandf(format string, args ...any) error
- func WithAfterResponse(ctx context.Context, a *AfterResponse) context.Context
- func WithAuthModePASE(ctx context.Context) context.Context
- func WithAuthority(ctx context.Context, fn AuthorityFunc) context.Context
- func WithCommandPath(ctx context.Context, path ConcreteCommandPath) context.Context
- func WithFabricFilter(ctx context.Context, filtered bool, fabricIndex uint8) context.Context
- func WithGroupSubject(ctx context.Context, s GroupSubject) context.Context
- func WithListAppendWrite(ctx context.Context) context.Context
- func WithSubject(ctx context.Context, nodeID uint64, cats []uint32) context.Context
- func WithTimedInteraction(ctx context.Context, timed bool) context.Context
- func WithWriteTransaction(ctx context.Context, tx *WriteTransaction) context.Context
- type ACLChecker
- type AfterResponse
- type AttributeReadPrivilegeProvider
- type AttributeReport
- type AttributeStatus
- type AttributeValue
- type AttributeValueReader
- type AttributeValueWriter
- type AttributeWrite
- type AttributeWritePrivilegeProvider
- type AuthorityFunc
- type AuthorizingInvoker
- type AuthorizingWriter
- type CommandAuthorizer
- type CommandFieldsReader
- type CommandFieldsWriter
- type CommandInvocation
- type CommandInvokePrivilegeProvider
- type CommandPathChecker
- type CommandTargeter
- type ConcreteAttributePath
- type ConcreteCommandPath
- type ConcreteEventPath
- type DataVersionFilter
- type DataVersionReader
- type DelayReportData
- type Dispatcher
- type EventDataReader
- type EventDataWriter
- type EventLog
- func (l *EventLog) Append(rec EventRecord) uint64
- func (l *EventLog) DropBuffered()
- func (l *EventLog) DropFabric(fabricIndex uint8) int
- func (l *EventLog) Query(endpoint uint16, cluster, eventID uint32, minNumber uint64) []EventRecord
- func (l *EventLog) SeedNumber(base uint64)
- func (l *EventLog) SetClock(c clock.Clock)
- func (l *EventLog) SetCounterPersistence(persist func(ceiling uint64), epoch uint64)
- type EventMinimumNumber
- type EventPathChecker
- type EventPriority
- type EventReadAuthorizer
- type EventRecord
- type EventReport
- func AuthorizeEventReports(ctx context.Context, auth EventReadAuthorizer, events []EventReport) []EventReport
- func BuildEventReports(paths []ConcreteEventPath, log *EventLog, filters []EventMinimumNumber) []EventReport
- func DeniedEventPathStatuses(ctx context.Context, auth EventReadAuthorizer, paths []ConcreteEventPath) []EventReport
- func HandleReadEventRequest(req ReadRequest, log *EventLog) []EventReport
- type FieldsContainerConsumed
- type GroupInvokeReport
- type GroupSubject
- type InvokeRequest
- type InvokeResponse
- type InvokeResponseEntry
- type InvokeResult
- type MatterClusterStatusError
- type ReadRequest
- type ReadResult
- type ReceivedEventData
- type ReportData
- type StatusCode
- func AuthorityAt(ctx context.Context, endpoint uint16, clusterID uint32, privilege uint8) StatusCode
- func HandleGroupInvokeRequest(ctx context.Context, d Dispatcher, req InvokeRequest, ...) StatusCode
- func HandleGroupWriteRequest(ctx context.Context, d Dispatcher, req WriteRequest) StatusCode
- func ValidateInvokeBatch(req InvokeRequest) StatusCode
- func ValidateInvokeBatchWithin(req InvokeRequest, maxPaths int) StatusCode
- func ValidateReadPaths(attrs []ConcreteAttributePath, events []ConcreteEventPath) StatusCode
- type StatusCodeError
- type StatusIB
- type StatusResponse
- type SubscribeRequest
- type SubscribeResponse
- type TimedCommandProvider
- type TimedRequest
- type WriteAuthorizer
- type WriteRequest
- type WriteResponse
- type WriteResult
- type WriteTransaction
Constants ¶
const ( OpcodeStatusResponse uint8 = 0x01 OpcodeReadRequest uint8 = 0x02 OpcodeSubscribeRequest uint8 = 0x03 OpcodeSubscribeResponse uint8 = 0x04 OpcodeReportData uint8 = 0x05 OpcodeWriteRequest uint8 = 0x06 OpcodeWriteResponse uint8 = 0x07 OpcodeInvokeRequest uint8 = 0x08 OpcodeInvokeResponse uint8 = 0x09 OpcodeTimedRequest uint8 = 0x0A )
IM message opcodes per Matter Core Spec §10.5 Table 18. These are the values the ProtocolHeader.Opcode field takes when ProtocolID is InteractionModelProtocolID. Receivers fan out by opcode into HandleReadRequest / HandleWriteRequest / HandleInvokeRequest / [HandleSubscribeRequest] (the latter currently routes through the subscription manager directly; see [..]/im/subscription).
const DefaultMaxPathsPerInvoke = 10
DefaultMaxPathsPerInvoke is the ceiling on CommandDataIBs a single InvokeRequest batch may carry. Mirrors matter.js packages/types/src/protocol/definitions/interaction.ts DEFAULT_MAX_PATHS_PER_INVOKE — the same value the bridge advertises via BasicInformation.MaxPathsPerInvoke (0x0016, cluster/core/basic_information.go) and the CASE SessionParameters negotiation. Defined here (the protocol layer) rather than in cluster/core so ValidateInvokeBatch can enforce the exact number the bridge advertises without an import cycle.
const InteractionModelProtocolID uint16 = 0x0001
InteractionModelProtocolID is the Matter Protocol ID under which IM messages travel (Matter Core Spec §10.5 Table 17). The Secure-Channel layer routes datagrams whose ProtocolHeader carries this ProtocolID into the IM dispatcher.
const InteractionModelRevision uint8 = MatterInteractionModelRevision
InteractionModelRevision is kept for back-compat with timed.go's existing call sites. Use MatterInteractionModelRevision (defined in subscribe.go) for new code.
It is the IM protocol revision the bridge advertises in TimedRequest replies and StatusResponses (Matter §8.1.4) and always equals MatterInteractionModelRevision: 12, matter.js HEAD packages/model/src/common/Specification.ts INTERACTION_MODEL_REVISION at the Matter 1.6.1 pin. matter.js caps the value at 12 because revision 13's only delta is the provisional WildcardFilterConfigurationVersion (§8.2.1.7.1). The value 11 once made Apple Home treat subscribe transactions as stale-protocol; 12 is what matter.js ships to every ecosystem.
const ( // MatterInteractionModelRevision is the IM revision the bridge // advertises. Mirrors matter.js HEAD packages/model/src/common/ // Specification.ts INTERACTION_MODEL_REVISION (12 at the Matter // 1.6.1 pin; capped below 13 because revision 13's only delta, // WildcardFilterConfigurationVersion, is provisional). The wire // fixtures in im/testdata read the same constant from matter.js. MatterInteractionModelRevision uint8 = 12 )
SubscribeResponseMessage tag numbers.
const MaxReadPaths = 10_000
MaxReadPaths is the hard acceptance ceiling for paths (attribute and event paths counted together) in a single Read or Subscribe interaction. Mirrors matter.js packages/node/src/node/server/InteractionServer.ts:79-80 (MAX_READ_PATHS / MAX_SUBSCRIBE_PATHS = 10 000, the upper bound of the CapabilityMinimaStruct field constraints); a request beyond it is answered with PathsExhausted (:366-369, :600-604).
Variables ¶
var ( // ErrInvalidWriteResponse is returned for a malformed WriteResponse. ErrInvalidWriteResponse = errors.New("im: invalid WriteResponse") // ErrInvalidSubscribeResponse is returned for a malformed // SubscribeResponse. ErrInvalidSubscribeResponse = errors.New("im: invalid SubscribeResponse") // ErrInvalidReportData is returned for a malformed ReportData. ErrInvalidReportData = errors.New("im: invalid ReportData") )
Errors of the client decoders.
var ( // ErrInvalidTimedRequest is returned for malformed TimedRequests. ErrInvalidTimedRequest = errors.New("im: invalid TimedRequest") // ErrInvalidStatusResponse is returned for malformed StatusResponses. ErrInvalidStatusResponse = errors.New("im: invalid StatusResponse") )
Errors.
var ( // ErrInvalidInvokeRequest is returned for malformed invokes. ErrInvalidInvokeRequest = errors.New("im: invalid InvokeRequest") )
Errors.
var ( // ErrInvalidPath is returned when a TLV-encoded path violates // structural invariants (wrong container type, missing required // field, repeated field). ErrInvalidPath = errors.New("im: invalid path") )
Errors.
var ( // ErrInvalidReadRequest is returned for malformed read requests. ErrInvalidReadRequest = errors.New("im: invalid ReadRequest") )
Errors.
var ( // ErrInvalidStatusIB surfaces when a TLV-encoded StatusIB is // malformed — wrong container type, missing required field. ErrInvalidStatusIB = errors.New("im: invalid StatusIB") )
Errors.
var ( // ErrInvalidSubscribeRequest is returned for malformed subscribes. ErrInvalidSubscribeRequest = errors.New("im: invalid SubscribeRequest") )
Errors.
var ( // ErrInvalidWriteRequest is returned for malformed write requests. ErrInvalidWriteRequest = errors.New("im: invalid WriteRequest") )
Errors.
Functions ¶
func DataVersionFilterSize ¶ added in v0.4.0
func DataVersionFilterSize(f DataVersionFilter) int
DataVersionFilterSize is the encoded size of f as one member of the DataVersionFilters array — what matter.js's #shortenDataVersionFilters measures with TlvDataVersionFilter.encode (interaction/InteractionMessenger.ts:1036-1037).
func DeferAfterResponse ¶ added in v0.2.0
DeferAfterResponse registers fn to run once the response of the interaction ctx belongs to has been sent. It reports false — and does not register fn — when ctx carries no AfterResponse; the caller then does the work at once.
func DispatchEndpoints ¶ added in v0.8.0
func DispatchEndpoints(ctx context.Context, d Dispatcher, req InvokeRequest) map[uint16]bool
DispatchEndpoints returns the endpoints req would dispatch a command to in ctx: each concrete path that passes the checks HandleInvokeRequest makes before dispatching (access, fabric scope, timed quality, existence), and every endpoint a wildcard path expands to. A path that fails a check contributes nothing, and nothing runs. It is matter.js's dispatch-endpoint set, collected in CommandInvokeResponse's validation pass and handed to its dispatch hook before any command runs (packages/protocol/src/action/server/CommandInvokeResponse.ts:159-165 and :297 at 862ad75f), the endpoints a DelayReportData defers.
func FabricFilterFromContext ¶
FabricFilterFromContext extracts the FabricFiltered flag + FabricIndex stamped by WithFabricFilter. Returns (false, 0) when no filter context is present — the safe default that yields the unfiltered list (Matter §7.5.2).
func IsListAppendWrite ¶ added in v0.2.0
IsListAppendWrite reports whether ctx carries WithListAppendWrite.
func IsPASEFromContext ¶
IsPASEFromContext reports whether the request arrived over a PASE session, as stamped by WithAuthModePASE. Returns false when no auth mode is present — the safe default, which leaves the request to the ordinary ACL evaluation rather than handing it Administer.
func IsRequestOpcode ¶
IsRequestOpcode reports whether the opcode identifies a request message the bridge must respond to (Read / Write / Invoke / Subscribe / Timed). Response opcodes (StatusResponse, ReportData, WriteResponse, InvokeResponse, SubscribeResponse) return false — the bridge receives those only as the initiator of a peer-side operation, which v1.1 does not perform.
func ListChunkedByDefault ¶ added in v0.6.0
ListChunkedByDefault reports whether a client writes a list value of the attribute list-chunked — REPLACE-ALL with the empty list, then one append per element — by default: every attribute but AccessControl ACL and Extension (Write.ts:18-21 isAclOrExtensionPath, :149-171). Unicast (client/interaction) and group (groups) writes share it.
func LookupFabricFilter ¶ added in v0.2.0
LookupFabricFilter is FabricFilterFromContext that also reports whether a filter was stamped at all. ok=false means no Interaction Model request is behind the read — a local caller, which matter.js models as a local actor that sees every entry whole (AccessControl.ts hasLocalActor). ok=true with fabricIndex 0 is a remote session that has no fabric yet; the two must not stand for one another, because the first may see what the second may not.
func MarshalInvokeRequestTLV ¶ added in v0.3.0
func MarshalInvokeRequestTLV(enc *tlv.Encoder, req InvokeRequest, fieldsWriter CommandFieldsWriter)
MarshalInvokeRequestTLV encodes req as an InvokeRequestMessage, each command's fields through fieldsWriter, stamped with this module's InteractionModelRevision.
func MatchDataVersionFilter ¶
func MatchDataVersionFilter(filters []DataVersionFilter, endpoint uint16, cluster uint32) (uint32, bool)
MatchDataVersionFilter is the exported form of [matchDataVersionFilter] used by the bridge's Subscribe path (bridge/subscribe.go) to apply the same filter evaluation logic as HandleReadRequest.
func SubjectFromContext ¶
SubjectFromContext extracts the peer subject (NodeID + CATs) stamped by WithSubject. Returns (0, nil) when no subject is present — the safe default that limits the request to entries whose Subjects list is empty (i.e. "any subject on this fabric").
func SuppressResponseOf ¶ added in v0.2.0
SuppressResponseOf reads only the SuppressResponse field (context tag 0) of an InvokeRequestMessage or WriteRequestMessage whose full decode failed, so the error a failed request raises can still be suppressed. It mirrors matter.js InteractionMessenger.ts suppressResponseOf (TlvObject({ suppressResponse: TlvOptionalField(0, TlvBoolean) })): the rest of the structure may break the request's schema, but malformed TLV anywhere in it, or a tag 0 that is not a boolean, reads as "not suppressed" — unknown means the action is answered.
func TimedInteractionFromContext ¶ added in v0.2.0
TimedInteractionFromContext reports the stamp of WithTimedInteraction; known is false when the caller stamped nothing, in which case the per-command timed check is the caller's.
func UnsupportedCommandf ¶
UnsupportedCommandf builds an error that carries the message verbatim and maps to StatusUnsupportedCommand. Use it in the default branch of a cluster server's MatterInvoke.
func WithAfterResponse ¶ added in v0.2.0
func WithAfterResponse(ctx context.Context, a *AfterResponse) context.Context
WithAfterResponse attaches a to ctx.
func WithAuthModePASE ¶
WithAuthModePASE stamps "this request arrived over a PASE session" into ctx. The IM ACL gates read it via IsPASEFromContext and grant the implicit Administer privilege the commissioning channel carries, independent of the session's FabricIndex.
Mirrors matter.js packages/protocol/src/interaction/ FabricAccessControl.ts:189-191 — `subjectDesc.authMode === AccessControl.AccessControlEntryAuthMode.Pase && subjectDesc.isCommissioning` grants AccessLevel.Administer before any ACL entry is consulted. Keying that grant on `fabricIndex == 0` instead loses it at AddNOC, and the default `[CaseAdminSubject]` ACE never covers subject node-id 0 — so every follow-up ACL write, GroupKeySetWrite or other Administer command a commissioner sends over the same PASE channel would answer UnsupportedAccess.
func WithAuthority ¶ added in v0.2.0
func WithAuthority(ctx context.Context, fn AuthorityFunc) context.Context
WithAuthority stamps the access check of the request ctx belongs to. HandleInvokeRequest stamps it before a command runs; a test of a cluster server stamps its own.
func WithCommandPath ¶ added in v0.8.3
func WithCommandPath(ctx context.Context, path ConcreteCommandPath) context.Context
WithCommandPath stamps the concrete path of the command ctx belongs to: the endpoint dispatcher stamps it before a cluster server's MatterInvoke runs, so a server that checks a privilege for part of its request (AuthorityAt) names the location the command runs at.
Mirrors the location matter.js builds from the invoking endpoint for `context.authorityAt(writeLevel, location)` (packages/node/src/behaviors/ thermostat/AtomicWriteHandler.ts:187-193 at 862ad75f).
func WithFabricFilter ¶
WithFabricFilter returns a derived context carrying the FabricFiltered flag from the request and the operational FabricIndex resolved from the inbound session. Cluster servers that expose fabric-scoped attributes (OperationalCredentials.Fabrics, AccessControl.ACL) read this via FabricFilterFromContext inside their MatterReadFiltered and project the underlying list down to the requesting fabric when filtered=true. fabricIndex==0 means a session with no fabric yet — a PASE session before AddNOC. matter.js gives such a session no fabric of its own: a fabric-filtered read of a fabric-scoped list shows it no entry, and an unfiltered one withholds every entry's fabric-sensitive fields (packages/node/src/behavior/state/managed/ values/ListManager.ts FabricFilteredListProxyHandler, packages/protocol/src/action/server/AccessControl.ts mayRead, which refuses a fabric-sensitive field to a session without `session.fabric`). LookupFabricFilter tells such a request apart from a read with no request behind it.
func WithGroupSubject ¶ added in v0.2.0
func WithGroupSubject(ctx context.Context, s GroupSubject) context.Context
WithGroupSubject stamps the Group subject of a group message into ctx. A request carrying one is evaluated under the Group auth mode: only Group access control entries apply, and their subject is the group id (Matter §9.10.5.6; matter.js FabricAccessControl #getIsdFromMessage).
func WithListAppendWrite ¶ added in v0.2.0
WithListAppendWrite marks ctx as carrying a list-append write (a ListIndex=null AttributeDataIB): the cluster server receives the whole list with one element appended, and only that element is new. A server that reports per-element changes (AccessControl's AccessControlEntryChanged) reports just the appended one, as chip's list append (AccessControlAttribute CreateEntry) does.
func WithSubject ¶
WithSubject stamps the operational peer subject — NodeID lifted out of the peer NOC plus the set of CASE Authenticated Tags from the same NOC — into ctx so [TopologyDispatcher.CheckACL] can enforce per-subject ACEs (Matter §9.10.5.6). PASE sessions stamp NodeID=0 and nil CATs; the ACL gate bypasses subject matching on PASE via the fabricIndex==0 check.
Mirrors connectedhomeip/src/access/AccessControl.cpp:441 — the `subjectDescriptor` the access check uses carries (fabricIndex, authMode, subject, cats). go-fabric carries the same triple via ctx so the IM dispatcher's signature stays minimal.
func WithTimedInteraction ¶ added in v0.2.0
WithTimedInteraction stamps whether the request runs inside a valid timed interaction (a TimedRequest preceded it on the exchange and its window has not expired). The transport stamps it once the timed gate has passed; HandleInvokeRequest then answers a timed-required command outside one with NeedsTimedInteraction for that path — matter.js CommandInvokeResponse.ts:291 `limits.timed && !this.session.timed`.
func WithWriteTransaction ¶ added in v0.2.0
func WithWriteTransaction(ctx context.Context, tx *WriteTransaction) context.Context
WithWriteTransaction attaches tx to ctx for HandleWriteRequest.
Types ¶
type ACLChecker ¶
type ACLChecker interface {
// CheckACL returns [StatusUnsupportedAccess] when the requesting fabric
// does not hold the required privilege for (endpoint, clusterID) on
// behalf of the requesting subject. Returns [StatusSuccess] when access
// is granted.
//
// fabricIndex is the requesting fabric index extracted from the session
// context (via [FabricFilterFromContext]). fabricIndex==0 means PASE /
// pre-commissioning — ACL does not yet apply (always granted).
//
// subjectNodeID + subjectCATs identify the requesting peer for per-
// subject ACE matching (Matter §9.10.5.6). For CASE sessions both are
// lifted from the peer NOC; for PASE both are zero / nil and the
// fabricIndex==0 bypass applies. An ACL entry with an empty Subjects
// list matches any subject on the fabric.
//
// requiredPrivilege is the minimum Matter privilege level required:
// 1=View, 3=Operate, 4=Manage, 5=Administer.
CheckACL(ctx context.Context, fabricIndex uint8, subjectNodeID uint64, subjectCATs []uint32, endpoint uint16, clusterID uint32, requiredPrivilege uint8) StatusCode
}
ACLChecker is an optional interface a Dispatcher implementation may provide to gate Write and Invoke calls against the AccessControl cluster's ACL table. When nil / not implemented, all writes and invokes are allowed (permissive default — preserves v1.0 behaviour for callers that have not wired an ACL store).
Mirrors connectedhomeip/src/access/AccessControl.cpp:415-560 and matter.js packages/node/src/node/server/OnlineServerInteraction.ts FabricAccessControl forRequest gate. chip src/app/WriteHandler.cpp:780 enforces "Execute the ACL Access Granting Algorithm before existence checks" — the IM gate calls this BEFORE attribute dispatch.
type AfterResponse ¶ added in v0.2.0
type AfterResponse struct {
// contains filtered or unexported fields
}
AfterResponse collects work a command defers until its interaction's response has been sent — matter.js NodeSession.initiateClose, which defers a session close "until exchanges end" (deferredClose), so a RevokeCommissioning received over the PASE session it closes still answers on it. The transport creates one per interaction, attaches it with WithAfterResponse and calls AfterResponse.Run once the response is on the wire.
func (*AfterResponse) Run ¶ added in v0.2.0
func (a *AfterResponse) Run()
Run runs the deferred work in registration order, once; later calls run nothing.
type AttributeReadPrivilegeProvider ¶
type AttributeReadPrivilegeProvider interface {
// MinReadPrivilege returns the minimum Matter privilege level
// required to read attrID on (endpoint, clusterID). Return 1
// (View) for the common case; return 5 (Administer) for
// fabric-security-sensitive attributes like ACL/Extension.
MinReadPrivilege(endpoint uint16, clusterID, attrID uint32) uint8
}
AttributeReadPrivilegeProvider is an optional interface a Dispatcher may implement to signal that a specific (endpoint, cluster, attribute) path requires a higher read privilege than the default View (1). HandleReadRequest calls this before dispatching each concrete ACL check so elevated-privilege attributes (e.g. AccessControl.ACL and AccessControl.Extension which require Administer per Matter §9.10.5.3) are protected even on read paths. Mirrors chip src/app/clusters/access-control-server/access-control-server.cpp AttributeReadAclRequired and matter.js packages/model/src/standard/elements/access-control.element.ts access: "administer".
type AttributeReport ¶
type AttributeReport struct {
Path ConcreteAttributePath
Status StatusIB
Value AttributeValue
// IsStatus distinguishes a "status-only" entry (Status meaningful,
// Value ignored) from a data entry (Value meaningful, Status zero).
IsStatus bool
// DataVersion is the per-cluster version tag chip emits in
// AttributeDataIB.DataVersion (Matter §10.6.1.4). 0 means "let
// the marshaller default to 1" — controllers tolerate any
// non-zero version on first read.
DataVersion uint32
// HasDataVersion reports that a received AttributeDataIB carried a
// DataVersion — it is optional on the wire (TlvAttributeReportData.ts).
// Set by [UnmarshalReportDataTLV]; the encoder ignores it.
HasDataVersion bool
// TagCompression is the received path's EnableTagCompression flag
// (TlvAttributePath.ts tag 0): the omitted path fields are the
// previous path's (InputChunk.ts:252-273). Set by
// [UnmarshalReportDataTLV]; the encoder ignores it.
TagCompression bool
}
AttributeReport is one (path, value | status) tuple inside the AttributeReports array.
type AttributeStatus ¶
type AttributeStatus struct {
Path ConcreteAttributePath
Status StatusIB
}
AttributeStatus is one entry in WriteResponses.
type AttributeValue ¶
type AttributeValue struct {
// Value is the cluster-native Go value. Nil for unobserved /
// nullable attributes.
Value any
// IsNull is true when the attribute value is explicitly null on
// the wire (Matter NULL TLV element). Mutually exclusive with a
// non-nil Value.
IsNull bool
}
AttributeValue carries the result of a Read or the input of a Write. The bridge speaks cluster-native Go types ([bool], [uint8], [int16], …) and the IM layer is responsible only for routing — the TLV encoder bound to the cluster-server side does the wire-shape work.
type AttributeValueReader ¶
type AttributeValueReader func(path ConcreteAttributePath, el tlv.Element, dec *tlv.Decoder) (AttributeValue, error)
AttributeValueReader extracts an AttributeValue from the TLV stream at the decoder's current position. Cluster-native type decoding lives outside the IM layer; the reader is supplied by the caller — typically the endpoint assembler.
The path argument carries the AttributeDataIB's path (decoded just before this call) so the reader can dispatch on (cluster, attribute) to a cluster-native decoder. dec is positioned at the value's first element when el.IsContainer is true — the reader is responsible for draining the container before returning.
type AttributeValueWriter ¶
type AttributeValueWriter func(enc *tlv.Encoder, tag tlv.Tag, v AttributeValue)
AttributeValueWriter is the function the Dispatcher's caller supplies for writing a cluster-native value into TLV. The IM layer stays cluster-blind; the writer translates Value into the right TLV-encoded shape (uint8, int16, struct, …).
type AttributeWrite ¶
type AttributeWrite struct {
Path ConcreteAttributePath
Value AttributeValue
// DataVersion is optional per spec; when non-zero the device
// MUST surface DataVersionMismatch if its current version
// differs.
DataVersion uint32
HasDataVersion bool
}
AttributeWrite is one entry in WriteRequests — a (path, value) pair the Dispatcher applies.
type AttributeWritePrivilegeProvider ¶
type AttributeWritePrivilegeProvider interface {
// MinWritePrivilege returns the minimum Matter privilege level
// required to write attrID on (endpoint, clusterID). Return 3
// (Operate) for the common case; return 4 (Manage) / 5 (Administer)
// for elevated attributes.
MinWritePrivilege(endpoint uint16, clusterID, attrID uint32) uint8
}
AttributeWritePrivilegeProvider is an optional interface a Dispatcher may implement to signal that a specific (endpoint, cluster, attribute) path requires a higher write privilege than the default Operate (3). HandleWriteRequest calls this before the per-write ACL check so elevated-privilege attributes (e.g. AccessControl.ACL / Extension which require Administer, or BasicInformation.NodeLabel which requires Manage) cannot be written by a merely-Operate subject. Mirrors the writeAccess bits in matter.js packages/model/src/standard/elements/*.element.ts and chip's per-attribute write-ACL guards.
type AuthorityFunc ¶ added in v0.2.0
type AuthorityFunc func(endpoint uint16, clusterID uint32, privilege uint8) StatusCode
AuthorityFunc answers whether the subject of a request holds privilege at (endpoint, clusterID): StatusSuccess when it does, the denial status otherwise.
type AuthorizingInvoker ¶ added in v0.2.0
type AuthorizingInvoker interface {
// InvokeAuthorized dispatches path, whose endpoint is a wildcard, to
// every endpoint hosting path.Cluster that accepts path.Command, in
// ascending endpoint order, skipping a location authorize denies.
InvokeAuthorized(ctx context.Context, path ConcreteCommandPath, fields any, authorize CommandAuthorizer) []InvokeResult
}
AuthorizingInvoker is an optional interface a Dispatcher implements so a wildcard-endpoint command — the only shape a group command takes — can be dispatched to every endpoint that hosts the cluster and accepts the command, each location authorized before the command runs.
type AuthorizingWriter ¶
type AuthorizingWriter interface {
// WriteAuthorized behaves like [Dispatcher.Write] but consults
// authorize for every resolved (endpoint, cluster, attribute) before
// the value is applied. A denied location on a wildcard-endpoint path
// is skipped silently (Matter §8.4.3.2 — a wildcard interaction
// discloses only authorized paths); a denied concrete path yields the
// denial status. A nil authorize dispatches exactly like Write.
WriteAuthorized(ctx context.Context, path ConcreteAttributePath, value AttributeValue, authorize WriteAuthorizer) []WriteResult
}
AuthorizingWriter is an optional interface a Dispatcher may implement so HandleWriteRequest can gate every location a wildcard-endpoint write expands to BEFORE the value is applied. Post-hoc filtering of the returned results is not sufficient: a write mutates cluster state (and drives device commands) inside the expansion loop.
Mirrors matter.js packages/protocol/src/action/server/AttributeWriteResponse.ts:324-343 (#writeAttributeForWildcard authorizes the resolved attribute at its own location and returns without emitting a status on denial).
type CommandAuthorizer ¶ added in v0.2.0
type CommandAuthorizer func(endpoint uint16, clusterID, commandID uint32) StatusCode
CommandAuthorizer decides whether a command may run at a resolved (endpoint, cluster, command), returning StatusSuccess to proceed.
type CommandFieldsReader ¶
type CommandFieldsReader func(path ConcreteCommandPath, dec *tlv.Decoder, el tlv.Element) (any, error)
CommandFieldsReader extracts the cluster-native fields struct from the TLV stream. The IM layer does not own cluster-native decoding; the implementation switches on `path.Cluster` / `path.Command` and pulls elements from `dec` until the matching EndContainer arrives.
`el` is the FIELDS container's opening element (TLV TypeStructure); `dec` is positioned just inside that container — calling `dec.Next()` returns the first field. Path is supplied so a single reader can dispatch across all cluster commands without an out-of-band cluster registry.
Implementations that don't recognise the (cluster, command) pair MUST drain the container via the decoder's skip helper and return (nil, nil); the IM layer treats that as "fields skipped" and propagates path-only Invoke to the cluster server.
A typed reject (an error carrying a StatusCodeError) normally leaves `dec` inside the fields container and the IM layer drains the rest. A reject the reader can only reach at the container's EndContainer — a mandatory field found missing — has consumed it already; such an error also implements FieldsContainerConsumed so the container is not drained twice.
type CommandFieldsWriter ¶
CommandFieldsWriter is the encoder counterpart of CommandFieldsReader.
type CommandInvocation ¶
type CommandInvocation struct {
Path ConcreteCommandPath
Fields any // cluster-native struct produced by [CommandFieldsReader]
CommandRef uint16
HasCommandRef bool
// DecodeStatus is non-Success when the [CommandFieldsReader] rejected
// the command's fields with a typed [StatusCodeError] (a value outside
// its schema type, for instance). Fields is nil then, and
// [HandleInvokeRequest] answers the command with that status instead
// of invoking it — the per-command outcome matter.js produces when
// requestTlv.validate throws (packages/protocol/src/action/server/
// CommandInvokeResponse.ts:446-448, :472-496).
DecodeStatus StatusCode
}
CommandInvocation is one entry in InvokeRequests.
type CommandInvokePrivilegeProvider ¶
type CommandInvokePrivilegeProvider interface {
// MinInvokePrivilege returns the minimum Matter privilege level
// required to invoke cmdID on (endpoint, clusterID). Return 3
// (Operate) for the common case; return 4 (Manage) / 5 (Administer)
// for elevated commands.
MinInvokePrivilege(endpoint uint16, clusterID, cmdID uint32) uint8
}
CommandInvokePrivilegeProvider is an optional interface a Dispatcher may implement to signal that a specific (endpoint, cluster, command) requires a higher invoke privilege than the default Operate (3). HandleInvokeRequest calls this before the per-invoke ACL check so administrative commands (e.g. OperationalCredentials.RemoveFabric, AdministratorCommissioning.OpenCommissioningWindow) cannot be invoked by a merely-Operate subject. Mirrors the invokeAccess bits in matter.js packages/model/src/standard/elements/*.element.ts and chip's per-command invoke-ACL guards.
type CommandPathChecker ¶ added in v0.3.0
type CommandPathChecker interface {
CommandPathStatus(path ConcreteCommandPath) StatusCode
}
CommandPathChecker is the optional Dispatcher capability HandleInvokeRequest consults for a concrete command path's existence before the payload check: UNSUPPORTED_ENDPOINT, UNSUPPORTED_CLUSTER or UNSUPPORTED_COMMAND for a path the node does not serve, Success otherwise. A command whose payload failed to decode on a cluster the endpoint does not serve owes the existence status, not the decode one — matter.js CommandInvokeResponse.ts #processConcrete checks endpoint, cluster and command before it validates the request (TC-IDM-1.2).
type CommandTargeter ¶ added in v0.8.0
type CommandTargeter interface {
CommandTargets(path ConcreteCommandPath, authorize CommandAuthorizer) []uint16
}
CommandTargeter is an optional interface a Dispatcher implements to name the endpoints a wildcard-endpoint command would run on, without running it: those AuthorizingInvoker.InvokeAuthorized dispatches to.
type ConcreteAttributePath ¶
type ConcreteAttributePath struct {
Node uint64
Endpoint uint16
Cluster uint32
Attribute uint32
ListIndex uint16
HasNode bool
HasEndpoint bool
HasCluster bool
HasAttribute bool
HasListIndex bool
// ListAppend marks a null ListIndex: the write appends its value — one
// list element — to the list (Matter §10.6.4.3.1; the chunked list
// encoding a controller uses for a list too large for one message).
ListAppend bool
}
ConcreteAttributePath identifies one attribute. Each Has* flag distinguishes "field present, value below" from "wildcard". The optional ListIndex points into an array attribute.
func DedupAttributePaths ¶
func DedupAttributePaths(paths []ConcreteAttributePath) []ConcreteAttributePath
DedupAttributePaths returns paths with every repeat of an earlier AttributePathIB removed, first occurrence first. A repeated path expands to exactly the results the first one did, so reading it again only duplicates the report; dropping it bounds the materialised report by the node's own attribute count rather than by how many copies of a path fit into one request. Returns the input slice when nothing repeats.
func UnmarshalAttributePathTLV ¶
func UnmarshalAttributePathTLV(dec *tlv.Decoder) (ConcreteAttributePath, error)
UnmarshalAttributePathTLV reads a single AttributePathIB starting at the decoder's current position. The opening List element must already be ahead in the stream.
func (ConcreteAttributePath) IsWildcardAttribute ¶
func (p ConcreteAttributePath) IsWildcardAttribute() bool
IsWildcardAttribute reports whether the path matches every attribute.
func (ConcreteAttributePath) IsWildcardCluster ¶
func (p ConcreteAttributePath) IsWildcardCluster() bool
IsWildcardCluster reports whether the path matches every cluster.
func (ConcreteAttributePath) IsWildcardEndpoint ¶
func (p ConcreteAttributePath) IsWildcardEndpoint() bool
IsWildcardEndpoint reports whether the path matches every endpoint.
func (ConcreteAttributePath) MarshalTLV ¶
func (p ConcreteAttributePath) MarshalTLV(enc *tlv.Encoder, tag tlv.Tag)
MarshalTLV encodes p as a Matter AttributePathIB (LIST). The caller supplies the wrapping tag so the path can ride either anonymously (inside an Array) or context-tagged (inside an enclosing Structure).
Field widths are deliberately magnitude-driven via tlv.Encoder.PutUint: Apple Home's MTRDevice IM-decoder accepts the narrow-encoded path fields (verified empirically — Apple parses 35 reports when paths ride as TypeUnsignedInt1 for sub-256 values), and the matter.js reference behaviour collapses to the same wire shape. The strict- width treatment is reserved for the AttributeData fields `DataVersion` (Matter §10.6.1.4 TlvUInt32) and the ReportData `SubscriptionId` (§10.6.4.1 TlvUInt32) — see read.go for those.
type ConcreteCommandPath ¶
type ConcreteCommandPath struct {
Endpoint uint16
Cluster uint32
Command uint32
HasEndpoint bool
HasCluster bool
HasCommand bool
}
ConcreteCommandPath identifies one cluster command on a specific endpoint. Endpoint is optional (group invocations omit it). Cluster and Command are mandatory on the wire — the Has* flags exist so a freshly-decoded path can be validated against the spec contract.
func CommandPathFromContext ¶ added in v0.8.3
func CommandPathFromContext(ctx context.Context) (path ConcreteCommandPath, ok bool)
CommandPathFromContext returns the path WithCommandPath stamped; ok is false outside a dispatched command.
func UnmarshalCommandPathTLV ¶
func UnmarshalCommandPathTLV(dec *tlv.Decoder) (ConcreteCommandPath, error)
UnmarshalCommandPathTLV reads a single CommandPathIB.
func (ConcreteCommandPath) MarshalTLV ¶
func (p ConcreteCommandPath) MarshalTLV(enc *tlv.Encoder, tag tlv.Tag)
MarshalTLV encodes p as a Matter CommandPathIB (LIST).
type ConcreteEventPath ¶
type ConcreteEventPath struct {
Node uint64
Endpoint uint16
Cluster uint32
Event uint32
HasNode bool
HasEndpoint bool
HasCluster bool
HasEvent bool
// IsUrgent flips the per-path urgency bit; combined with
// EventPriorityCritical it forces an immediate report.
IsUrgent bool
}
ConcreteEventPath identifies one event. Has* flags distinguish "field present, value below" from "wildcard".
func (ConcreteEventPath) IsWildcardCluster ¶
func (p ConcreteEventPath) IsWildcardCluster() bool
IsWildcardCluster reports whether the path matches every cluster.
func (ConcreteEventPath) IsWildcardEndpoint ¶
func (p ConcreteEventPath) IsWildcardEndpoint() bool
IsWildcardEndpoint reports whether the path matches every endpoint.
func (ConcreteEventPath) IsWildcardEvent ¶
func (p ConcreteEventPath) IsWildcardEvent() bool
IsWildcardEvent reports whether the path matches every event.
func (ConcreteEventPath) MarshalTLV ¶
func (p ConcreteEventPath) MarshalTLV(enc *tlv.Encoder, tag tlv.Tag)
MarshalTLV encodes p as an EventPathIB (LIST).
func (ConcreteEventPath) Matches ¶
func (p ConcreteEventPath) Matches(other ConcreteEventPath) bool
Matches returns true when this subscription path covers other. Wildcards in this path expand to match any concrete value in other.
type DataVersionFilter ¶
DataVersionFilter is a single (endpoint, cluster, version) triple the controller sends to tell the bridge "I have this version cached; skip encoding attributes for this cluster if your DataVersion still matches". Matter Core Spec §10.6.5.
type DataVersionReader ¶
type DataVersionReader interface {
// CurrentDataVersion returns the current DataVersion for the cluster at
// (endpoint, clusterID). Returns (0, false) when the cluster is not
// tracked or does not implement version tracking.
CurrentDataVersion(ctx context.Context, endpoint uint16, clusterID uint32) (uint32, bool)
}
DataVersionReader is an optional interface a Dispatcher implementation may provide to support DataVersion-conditional writes. When the dispatcher implements this interface, HandleWriteRequest calls CurrentDataVersion before applying each write that carries a DataVersion field.
Mirrors matter.js packages/node/src/node/server/InteractionServer.ts DataVersion mismatch check before cluster write dispatch, and chip src/app/WriteHandler.cpp AttributeAccessInterface DataVersion verification.
type DelayReportData ¶ added in v0.2.0
DelayReportData is the DelayReportData structure of an InvokeRequest (matter.js TlvDelayReportData): both fields uint16 milliseconds, default 0.
type Dispatcher ¶
type Dispatcher interface {
// Read resolves attribute reads. The path may carry wildcards;
// the implementation expands them to one ReadResult per match.
Read(ctx context.Context, path ConcreteAttributePath) []ReadResult
// Write resolves a single attribute write. Unlike Read, write
// paths are typically concrete; a wildcard write fans out to
// multiple WriteResults (one per matching attribute).
Write(ctx context.Context, path ConcreteAttributePath, value AttributeValue) []WriteResult
// Invoke dispatches a single command. fields is the decoded
// cluster-native struct for the command's request payload (or
// nil for parameterless commands). The response value is the
// cluster-native struct for the command's response (or nil for
// status-only commands).
Invoke(ctx context.Context, path ConcreteCommandPath, fields any) InvokeResult
}
Dispatcher is the cluster-server-side surface the IM layer routes Read / Write / Invoke requests through. The endpoint assembler in [..]/north/matter/endpoint constructs and registers a Dispatcher that walks the model registry to satisfy each call.
Implementations:
- MUST return StatusUnsupportedEndpoint for endpoints the bridge does not advertise.
- MUST return StatusUnsupportedCluster when the endpoint exists but the requested cluster is absent.
- MUST return StatusUnsupportedAttribute / StatusUnsupportedCommand when the cluster exists but the requested ID is missing.
- SHOULD return StatusBusy when a transient backend condition prevents servicing the request.
Wildcards: when a path field has its Has* flag false, the implementation expands across every concrete value matching. The returned Reads slice carries one (path, value, status) per concrete match.
type EventDataReader ¶ added in v0.4.0
type EventDataReader func(path ConcreteEventPath, el tlv.Element, dec *tlv.Decoder) (AttributeValue, error)
EventDataReader extracts an event's Data from the TLV stream, as AttributeValueReader does an attribute value: dec is positioned after el, and a container's members are the reader's to drain.
type EventDataWriter ¶
type EventDataWriter func(enc *tlv.Encoder, tag tlv.Tag, v AttributeValue)
EventDataWriter writes the event-specific Data payload into TLV. It mirrors AttributeValueWriter — the IM layer stays cluster-blind.
type EventLog ¶
type EventLog struct {
// contains filtered or unexported fields
}
EventLog buffers emitted events for retrospective Read-Event queries. Bounded: keeps the most recent N entries per priority bucket. Older entries are evicted in FIFO order. Thread-safe.
Matter §10.6.6 requires Critical events to be persisted; this implementation is an in-memory buffer (survives the session, not reboots). A persistent layer can be layered on top by draining the buffer at shutdown and replaying on boot — v1.1 leaves that for a later milestone.
Mirrors matter.js packages/protocol/src/interaction/EventHandler.ts.
func NewEventLog ¶
func NewEventLog() *EventLog
NewEventLog constructs an EventLog with [defaultBufferConfig].
func (*EventLog) Append ¶
func (l *EventLog) Append(rec EventRecord) uint64
Append records an event with a freshly-allocated monotonic Number and the current wall-clock epoch timestamp, then returns the assigned Number. The record is placed in the priority bucket corresponding to rec.Priority; the oldest entry in that bucket is evicted when the bucket is full.
func (*EventLog) DropBuffered ¶ added in v0.2.0
func (l *EventLog) DropBuffered()
DropBuffered discards every buffered record and keeps the numbering, so the next Append continues where the log left off. This log is volatile — it does not survive a restart — and the bridge calls this when it goes offline, so an in-process Stop/Start loses its events exactly as a process restart does. Mirrors matter.js packages/node/src/behavior/system/events/EventsBehavior.ts, which clears a volatile event store when the node goes offline (#4594).
With a counter persistence wired, the current numbering is persisted as the ceiling first, so a restart continues right after the last number handed out rather than past the rest of the reserved epoch. Mirrors matter.js packages/protocol/src/events/BaseEventStore.ts:62-67, :141-147 (clear with keepNumbering: reserveCurrentNumbering before the events go).
func (*EventLog) DropFabric ¶ added in v0.8.0
DropFabric removes the records of fabric-sensitive events that belong to fabricIndex — a fabric the node no longer has — and returns how many went. A record of another event keeps its place, whatever its payload says (BasicInformation's Leave carries the index of the fabric it left). Mirrors matter.js packages/node/src/behavior/cluster/FabricScopedDataHandler.ts:114-170 at 862ad75f (limitNodeDataToAllowedFabrics: the occurrences of events whose access is fabric-scoped or fabric-sensitive, bound to a fabric that is not allowed, are removed), run when a fabric is deleted (ServerEnvironment.ts:186-195). They stayed in the log.
func (*EventLog) Query ¶
func (l *EventLog) Query(endpoint uint16, cluster, eventID uint32, minNumber uint64) []EventRecord
Query returns all EventRecords whose Number >= minNumber and that match the supplied (endpoint, cluster, eventID) filter.
The lower bound is INCLUSIVE: minNumber is the EventFilterIB.EventMin the controller sent, and Matter §10.6.9 treats EventMin as the minimum EventNumber of interest — the record whose Number == EventMin is returned, not skipped. Mirrors matter.js packages/protocol/src/events/OccurrenceManager.ts #findMinEventNumberIndex ("first event number that is greater than or equal to eventMin") and chip src/app/EventManagement.cpp IncludeEventInReport (drops only mCurrentEventNumber < mStartingEventNumber).
Wildcard semantics follow chip-tool / Apple MTRDevice conventions:
- endpoint == 0xFFFF → match any endpoint
- cluster == 0xFFFFFFFF → match any cluster
- eventID == 0xFFFFFFFF → match any event
Results are returned in ascending Number order. Callers typically pass minNumber=0 to retrieve all buffered events.
func (*EventLog) SeedNumber ¶
SeedNumber raises the event-number counter to at least base. Called at boot with the persisted ceiling so numbering resumes past every number that may have been handed out before the previous shutdown (Matter §7.14.2.1 monotonicity). A base at or below the current counter is ignored — the counter never moves backwards.
func (*EventLog) SetClock ¶ added in v0.7.2
SetClock sets the clock that stamps a record appended without a timestamp; nil is clock.System.
func (*EventLog) SetCounterPersistence ¶
SetCounterPersistence wires the durable half of the EventNumber counter: persist is invoked (with the log's lock released, at most once per epoch; an Append that needs the new ceiling waits for it) with the new ceiling whenever the counter reaches the current one. epoch <= 0 selects the default 0x10000, chip's CHIP_DEVICE_CONFIG_EVENT_ID_COUNTER_EPOCH. Pass nil to detach.
type EventMinimumNumber ¶
type EventMinimumNumber struct {
// NodeID is the optional node filter from EventFilterIB tag 0.
// In a bridge context this is always the bridge's own NodeID or
// omitted; go-fabric stores it for completeness but does not
// use it in the query — only EventMin is applied.
NodeID uint64
HasNodeID bool
// EventMin is the minimum event number (INCLUSIVE lower bound): the
// bridge emits events with Number >= EventMin — see [EventLog.Query].
// Corresponds to EventFilterIB tag 1.
EventMin uint64
}
EventMinimumNumber is one entry in the EventFilters list that a commissioner sends to tell the bridge "the lowest event number I still want is EventMin; everything below it I already have."
Mirrors Matter Core Spec §10.6.9 EventFilterIB and matter.js packages/types/src/protocol/types/TlvEventFilter.ts. chip src/app/ReadHandler.cpp:598 ProcessEventFilters stores EventMin from EventFilterIB.EventMin and uses it to gate the event query.
type EventPathChecker ¶ added in v0.8.0
type EventPathChecker interface {
EventPathStatus(path ConcreteEventPath) StatusCode
}
EventPathChecker is an optional interface a Dispatcher implements to answer the existence of a concrete event path: UNSUPPORTED_ENDPOINT, UNSUPPORTED_CLUSTER, UNSUPPORTED_EVENT, or Success — matter.js EventReadResponse.ts #addConcrete's existence checks, in their order.
type EventPriority ¶
type EventPriority uint8
EventPriority is the Matter §10.6.6.1 priority enum.
const ( // EventPriorityDebug — least important; controllers may drop. EventPriorityDebug EventPriority = 0 // EventPriorityInfo — informational. EventPriorityInfo EventPriority = 1 // EventPriorityCritical — must be delivered. EventPriorityCritical EventPriority = 2 )
type EventReadAuthorizer ¶
type EventReadAuthorizer struct {
Checker ACLChecker
FabricIndex uint8
PASE bool
SubjectNodeID uint64
SubjectCATs []uint32
// Paths answers whether a concrete event path exists; nil takes every
// path to exist.
Paths EventPathChecker
}
EventReadAuthorizer bundles the accessing subject + ACL checker used to gate event reads and subscriptions, mirroring the (fabricIndex, subject, CATs) tuple the attribute read path threads through HandleReadRequest. A zero FabricIndex marks a PASE / pre-commissioning session (ACL not yet applicable); PASE marks the same session after AddNOC adopted it onto the new fabric — the implicit Administer grant is keyed on the auth mode (matter.js packages/protocol/src/interaction/FabricAccessControl.ts:189-191), so a FabricIndex alone no longer identifies the commissioning channel. A nil Checker disables enforcement (fail-open, matching the attribute path's "dispatcher without ACLChecker" fallback).
func (EventReadAuthorizer) Allows ¶ added in v0.2.0
Allows reports whether the accessing subject may read the events of (endpoint, cluster): the event read privilege — View, Administer for AccessControl and Groupcast — checked against the ACL. A PASE session, a zero fabric or a missing checker allows, as AuthorizeEventReports does.
type EventRecord ¶
type EventRecord struct {
// Number is the bridge-wide monotonic event counter stamped onto
// every emitted event. Matter §10.6.6.5 requires the number to be
// globally monotonic across the node; a single bridge-wide counter
// satisfies that constraint and is simpler than per-cluster counters.
Number uint64
// Priority is the Matter §10.6.6.1 priority class.
Priority EventPriority
// Endpoint is the Matter endpoint ID that emitted the event.
Endpoint uint16
// Cluster is the Matter cluster ID.
Cluster uint32
// EventID is the event ID within the cluster.
EventID uint32
// EpochMS is milliseconds since the Unix epoch (Matter §10.6.6.1
// EpochTimestamp, POSIX milliseconds).
EpochMS uint64
// Payload is the cluster-specific event struct (e.g. StartUpEvent,
// ReachableChangedEvent). Encoding is handled by the cluster-specific
// value writer in [bridge/reply.go]. `any` is justified: event
// payloads vary per (cluster, event) pair and cannot be expressed in
// a single Go type without fragmenting the interface.
Payload any
}
EventRecord is one persisted event entry. Mirrors matter.js packages/protocol/src/interaction/EventHandler.ts::EventRecord.
EpochMS is milliseconds since the Unix epoch (Matter §10.6.6.1 EpochTimestamp is POSIX milliseconds — matter.js TlvPosixMs); Priority mirrors the EventPriority constants.
type EventReport ¶
type EventReport struct {
Path ConcreteEventPath
Number uint64 // Monotonic event number per cluster
Priority EventPriority
Timestamp uint64 // EpochTimestamp: POSIX milliseconds (encoded at tag 3)
Data AttributeValue
Status StatusIB
IsStatus bool
// Received is what [UnmarshalReportDataTLV] read of a received
// EventDataIB beyond the fields above; nil on an event this node
// builds, which the encoder writes with Timestamp as the
// EpochTimestamp.
Received *ReceivedEventData
}
EventReport is one (path, value | status) tuple inside the EventReports array of a ReportDataMessage.
Mirrors matter.js packages/protocol/src/action/server/EventReadResponse.ts (EventReadResponse.#asValue / EventReadResponse.#asStatus) — the two constructors that produce the ReadResult.EventValue / EventStatus shapes that the IM layer eventually encodes into EventDataIB / EventStatusIB.
func AuthorizeEventReports ¶
func AuthorizeEventReports(ctx context.Context, auth EventReadAuthorizer, events []EventReport) []EventReport
AuthorizeEventReports filters events down to those the accessing subject may read and drops fabric-sensitive records that belong to another fabric. Denied paths/records are SILENTLY OMITTED — a wildcard event read discloses only authorized events (Matter §8.4.3.2), never an error status. A PASE session (FabricIndex==0, or PASE / IsPASEFromContext once AddNOC adopted it onto a fabric) or a nil Checker returns every event unchanged.
(a) ACL gate: each event is gated at its read privilege — View by default,
Administer for AccessControl (fabric-sensitive, Matter §9.10.7.1). Mirrors matter.js packages/protocol/src/action/server/EventReadResponse.ts #addConcrete/#addEventForWildcard (authorize at event.limits.readLevel).
(b) Fabric-sensitive drop: a fabric-sensitive record is disclosed only to the
fabric that owns it, independent of the read's FabricFiltered flag. Fails closed — a record whose fabric cannot be positively matched to the accessing fabric is never disclosed. Mirrors EventReadResponse.ts #readAllowedEvents (payload.fabricIndex !== accessingFabricIndex → skip).
func BuildEventReports ¶
func BuildEventReports(paths []ConcreteEventPath, log *EventLog, filters []EventMinimumNumber) []EventReport
BuildEventReports evaluates paths against log and returns a slice of EventReport suitable for embedding into a ReportData.
This is the event-only counterpart of HandleReadRequest for attributes. It mirrors the path-expansion + deduplication logic in matter.js packages/protocol/src/action/server/EventReadResponse.ts (EventReadResponse.process → #readAllowedEvents) and the event retrieval in matter.js packages/protocol/src/interaction/EventHandler.ts (getEvents). Wildcard-expand-then-filter semantics and sort-by-Number ascending output order match the matter.js reference implementation.
Each ConcreteEventPath is evaluated independently; results are de-duplicated by EventRecord.Number before returning so a wildcard path that overlaps with a concrete path does not produce duplicates.
filters carries the EventFilterIB minimum-event-number constraints the controller sent (Matter §10.6.9). Pass nil to return all buffered events. When filters is non-empty the minimum EventMin across all entries is used as the INCLUSIVE lower bound — events with Number < minNumber are excluded. The returned EventReport.Timestamp is sourced from EventRecord.EpochMS (POSIX milliseconds — Matter §10.6.6.1 EpochTimestamp, encoded at tag 3), matching the millisecond timestamp the subscribe path emits so the same event reads identically live and out-of-band.
Callers that have a ReadRequest available should prefer the HandleReadEventRequest wrapper.
func DeniedEventPathStatuses ¶ added in v0.2.0
func DeniedEventPathStatuses(ctx context.Context, auth EventReadAuthorizer, paths []ConcreteEventPath) []EventReport
DeniedEventPathStatuses returns an UNSUPPORTED_ACCESS EventStatusIB for every concrete event path (endpoint, cluster and event named) whose events the subject may not read. A denied concrete path is answered with its status, a denied wildcard is skipped silently — matter.js EventReadResponse.ts #addConcrete returns #asStatus(path, denied) where the wildcard expansion just leaves the cluster out. TC-ACL-2.9 reads AccessControlEntryChanged without Administer and expects the status.
A concrete path that does not exist answers its existence status — UNSUPPORTED_ENDPOINT, UNSUPPORTED_CLUSTER or UNSUPPORTED_EVENT — to a subject that passes the View pass, which comes first; the event's own read privilege is checked for an event that exists. Mirrors matter.js packages/protocol/src/action/server/EventReadResponse.ts:158-224 at 862ad75f (#addConcrete, core spec §8.4.3.2); such a path used to be answered with nothing.
func HandleReadEventRequest ¶
func HandleReadEventRequest(req ReadRequest, log *EventLog) []EventReport
HandleReadEventRequest evaluates the EventRequests in req against log and returns a slice of EventReports suitable for embedding into a ReportData.
This is a thin wrapper around BuildEventReports that extracts the EventRequests and EventFilters from the supplied ReadRequest. The event-only core logic lives in BuildEventReports; this wrapper exists so call sites that already hold a ReadRequest (e.g. the read-opcode handler) do not need to unpack the path slice manually.
Mirrors matter.js packages/protocol/src/action/server/EventReadResponse.ts (EventReadResponse.process) for the request-level dispatch and matter.js packages/protocol/src/interaction/EventHandler.ts (getEvents) for the per-path log query. Passes req.EventFilters to BuildEventReports for minimum-event-number gating.
type FieldsContainerConsumed ¶ added in v0.2.0
FieldsContainerConsumed is implemented by a CommandFieldsReader reject returned after the reader consumed the fields container's EndContainer.
type GroupInvokeReport ¶ added in v0.2.0
type GroupInvokeReport struct {
// Status is the status the interaction ended with; nothing is sent.
Status StatusCode
// Processed reports that the request passed the checks that end a
// group interaction before any command is considered (a TimedRequest
// flag, a concrete or incomplete path, a wildcard sharing the
// message) — in matter.js those throw before the invoke results are
// iterated, so no outcome is reported for them.
Processed bool
// Requested are the command paths the request named.
Requested []ConcreteCommandPath
// Dispatched are the command's results on every endpoint it ran on;
// an endpoint access control denied is not among them.
Dispatched []InvokeResult
}
GroupInvokeReport is what a group InvokeRequest did, for the report Groupcast testing makes of every group message (matter.js InteractionServer.handleInvokeRequest emits its GroupMessageEventInfo from the same facts).
func HandleGroupInvoke ¶ added in v0.2.0
func HandleGroupInvoke(ctx context.Context, d Dispatcher, req InvokeRequest, timedRequired func(clusterID, commandID uint32) bool) GroupInvokeReport
HandleGroupInvoke runs an InvokeRequest that arrived as a group message. ctx carries the fabric (WithFabricFilter) and the Group subject (WithGroupSubject). timedRequired reports a command whose model access requires a timed interaction; may be nil.
Mirrors matter.js InteractionServer.handleInvokeRequest for a group session and CommandInvokeResponse.process / #processWildcard / #wildcardTargetOf: a TimedRequest flag on a group message mismatches the (absent) timed interaction; every command path must leave the endpoint out (a concrete path is InvalidAction) and name its cluster and command; a wildcard path may not share the message with another command; the command then runs on each member endpoint of the group that hosts the cluster and accepts the command, where the Group subject holds the command's privilege and the command needs no timed interaction. A denied endpoint is skipped silently, as every wildcard expansion skips it.
type GroupSubject ¶ added in v0.2.0
GroupSubject is the Incoming Subject Descriptor of a group message: the destination group, whether the key that authenticated the message is the one GroupKeyMap maps the group to, and the local endpoints that are members of the group. Mirrors matter.js Subject.Group (packages/protocol/src/action/server/Subject.ts).
func GroupSubjectFromContext ¶ added in v0.2.0
func GroupSubjectFromContext(ctx context.Context) (GroupSubject, bool)
GroupSubjectFromContext returns the Group subject stamped by WithGroupSubject, or false for a unicast request.
type InvokeRequest ¶
type InvokeRequest struct {
SuppressResponse bool
TimedRequest bool
Invokes []CommandInvocation
// DelayReportData is the request's DelayReportData field, nil when
// absent. matter.js always decodes it and refuses a malformed one; the
// bridge defers the subscription reports it asks for
// (BD-Matter-DelayReportData).
DelayReportData *DelayReportData
}
InvokeRequest is the in-memory form of an InvokeRequestMessage.
func UnmarshalInvokeRequestTLV ¶
func UnmarshalInvokeRequestTLV(dec *tlv.Decoder, fieldsReader CommandFieldsReader) (InvokeRequest, error)
UnmarshalInvokeRequestTLV decodes an InvokeRequestMessage.
type InvokeResponse ¶
type InvokeResponse struct {
SuppressResponse bool
Responses []InvokeResponseEntry
// MoreChunkedMessages marks every chunk of a chunked InvokeResponse
// but the last (Matter §10.7.2; matter.js TlvInvokeResponse tag 2).
MoreChunkedMessages bool
}
InvokeResponse is the in-memory form of an InvokeResponseMessage.
func HandleInvokeRequest ¶
func HandleInvokeRequest(ctx context.Context, d Dispatcher, req InvokeRequest) InvokeResponse
HandleInvokeRequest dispatches a parsed InvokeRequest through d and returns the assembled InvokeResponse.
ACL gate: mirrors matter.js packages/node/src/node/server/OnlineServerInteraction.ts FabricAccessControl.forRequest and chip src/app/CommandHandler.cpp "Execute the ACL Access Granting Algorithm before existence checks". When d implements ACLChecker, the requesting fabric's privilege is verified before each command path is dispatched. The required privilege for a plain Invoke is Operate (3) per Matter §9.10.4.4; individual cluster servers may enforce additional Manage/Admin gates internally for sensitive commands such as RemoveFabric. fabricIndex is extracted via FabricFilterFromContext; a PASE session bypasses the ACL check so commissioning invokes (ArmFailSafe, CommissioningComplete) arrive before the fabric's ACL entry exists — fabricIndex==0 before AddNOC, IsPASEFromContext after it adopted the session onto the new fabric.
func UnmarshalInvokeResponseTLV ¶ added in v0.3.0
func UnmarshalInvokeResponseTLV(dec *tlv.Decoder, fieldsReader CommandFieldsReader) (InvokeResponse, error)
UnmarshalInvokeResponseTLV decodes an InvokeResponseMessage, each command response's fields through fieldsReader (nil skips them).
func (InvokeResponse) HasCommandData ¶
func (ir InvokeResponse) HasCommandData() bool
HasCommandData reports whether any entry is a CommandDataIB (a command that produced a response payload) rather than a CommandStatusIB. Per Matter §8.8.3.2.1 a SuppressResponse invoke still ships a response when a CommandDataIB is generated; when the response carries only status entries the server sends nothing. Mirrors matter.js packages/node/src/node/server/InteractionServer.ts:1043-1074 (`suppressedBuffer` is flushed on the first `cmd-response`; otherwise the invoke returns without sending).
func (InvokeResponse) MarshalTLV ¶
func (ir InvokeResponse) MarshalTLV(enc *tlv.Encoder, fieldsWriter CommandFieldsWriter)
MarshalTLV encodes ir. fieldsWriter is invoked for each non-status response that carries a payload.
type InvokeResponseEntry ¶
type InvokeResponseEntry struct {
Path ConcreteCommandPath
Response any
HasResponse bool
Status StatusIB
IsStatus bool
CommandRef uint16
HasCommandRef bool
}
InvokeResponseEntry is one element in InvokeResponses — either a CommandData (with response payload) or a CommandStatus.
type InvokeResult ¶
type InvokeResult struct {
Path ConcreteCommandPath
Response any // cluster-native response struct or nil for status-only
Status StatusCode
// ClusterStatus carries the cluster-specific status byte per
// Matter §10.6.2.2 — see [WriteResult.ClusterStatus] for the
// envelope semantics.
ClusterStatus uint8
HasClusterStatus bool
}
InvokeResult is the outcome of a single command dispatch.
type MatterClusterStatusError ¶
type MatterClusterStatusError interface {
StatusCodeError
// MatterClusterStatus returns the cluster-specific status code to
// embed in StatusIB.ClusterStatus per Matter Core Spec §10.6.2.2.
MatterClusterStatus() uint8
}
MatterClusterStatusError extends StatusCodeError for cluster-side command handlers that need to return a cluster-specific status code alongside the generic IM status code (Matter §10.6.2.2 Table 53). When the dispatcher finds this interface via errors.As, it encodes StatusIB with both Status=IMStatus and ClusterStatus=clusterCode.
Use case: AdministratorCommissioning §11.19.7.3 PAKEParameterError (cluster-specific code 0x02) alongside Status=ClusterSpecificFailure (0x01). Implement both MatterStatusCode() and MatterClusterStatus():
type pakeErr struct{}
func (pakeErr) Error() string { return "PAKE parameter error" }
func (pakeErr) MatterStatusCode() StatusCode { return StatusFailure }
func (pakeErr) MatterClusterStatus() uint8 { return 0x02 }
type ReadRequest ¶
type ReadRequest struct {
AttributeRequests []ConcreteAttributePath
EventRequests []ConcreteEventPath
// EventFilters carries per-source minimum-event-number hints per
// Matter §10.6.4 EventFilterIB. The bridge skips events with
// Number < EventMin in BuildEventReports (EventMin is an inclusive
// lower bound — see [EventLog.Query]).
EventFilters []EventMinimumNumber
FabricFiltered bool
DataVersionFilters []DataVersionFilter
}
ReadRequest is the in-memory form of a ReadRequestMessage. EventRequests carries the §10.6.4 EventPathIB list when the controller requests historical events (e.g. chip-tool's `read-event-by-id`). AttributeRequests and EventRequests may both be present in the same ReadRequest.
func UnmarshalReadRequestTLV ¶
func UnmarshalReadRequestTLV(dec *tlv.Decoder) (ReadRequest, error)
UnmarshalReadRequestTLV reads a ReadRequestMessage. Skips the optional Event* fields — they're not consumed by v1.1.
func (ReadRequest) MarshalTLV ¶
func (r ReadRequest) MarshalTLV(enc *tlv.Encoder)
MarshalTLV encodes r at the top level (always anonymous tag) as a client sends it. Mirrors matter.js packages/types/src/protocol/messages/TlvReadRequest.ts:19-35 at 0ceb202d with the request action/request/Read.ts:40-89 builds: the optional lists — AttributeRequests (0), EventRequests (1), EventFilters (2) — only when they have members, IsFabricFiltered (3) always, DataVersionFilters (4) only with members, and InteractionModelRevision (0xFF) MatterInteractionModelRevision (packages/model/src/common/Specification.ts:124), in field order.
type ReadResult ¶
type ReadResult struct {
Path ConcreteAttributePath
Value AttributeValue
Status StatusCode
// DataVersion carries the per-cluster DataVersion that the cluster
// server reported at read time. 0 means "not provided" and the IM
// layer defaults to 1 in the wire encoding (Matter §10.6.1.4).
DataVersion uint32
}
ReadResult is one (path, value, status) tuple produced by a Read. Wildcard reads fan out into multiple ReadResults — one per (endpoint, cluster, attribute) match.
type ReceivedEventData ¶ added in v0.4.0
type ReceivedEventData struct {
HasEpochTimestamp bool
SystemTimestamp uint64 // tag 4, milliseconds since boot
HasSystemTimestamp bool
DeltaEpochTimestamp uint64 // tag 5
HasDeltaEpochTimestamp bool
DeltaSystemTimestamp uint64 // tag 6
HasDeltaSystemTimestamp bool
// HasData reports that the EventDataIB carried Data (tag 7).
HasData bool
}
ReceivedEventData is the part of a received EventDataIB the encoder does not write (TlvEventData.ts): which timestamps were present — the EpochTimestamp is EventReport.Timestamp — the other three, and whether Data was.
type ReportData ¶
type ReportData struct {
SubscriptionID uint32
HasSubscription bool
Reports []AttributeReport
// EventReports carries the §10.6.6 EventReportIB list. Bridge
// emits these when a subscribed cluster fires an event (e.g.
// GenericSwitch InitialPress, DoorLock LockOperation).
EventReports []EventReport
// MoreChunkedMessages signals that this ReportDataMessage is one
// of several chunks for the same logical report. Per Matter
// §10.6.6 the sender splits oversized ReportData into multiple
// datagrams, sets the flag on every chunk except the last, and
// the receiver re-assembles by concatenating attribute / event
// arrays in arrival order.
MoreChunkedMessages bool
SuppressResponse bool
}
ReportData is the encoder side: the device assembles a ReportDataMessage from the dispatcher's ReadResult slice and ships it.
func HandleReadRequest ¶
func HandleReadRequest(ctx context.Context, d Dispatcher, req ReadRequest) ReportData
HandleReadRequest dispatches a parsed ReadRequest through d and returns the assembled ReportData. The caller marshals the result. Wildcard expansion is the Dispatcher's responsibility; this layer only routes paths.
DataVersionFilter evaluation: for each (endpoint, cluster) pair in req.DataVersionFilters, if the cluster's current DataVersion matches the cached version the controller sent, the cluster's attributes are omitted from the report — the controller already has a fresh copy. Matter §10.6.5 / mirrors matter.js InteractionServer.ts startReadInteraction DataVersionFilter check.
ACL gate: when d implements ACLChecker, every RESOLVED result is authorized at its concrete (endpoint, cluster) using the resolved attribute's read privilege — run per result AFTER path expansion so a wildcard-endpoint read is authorized per concrete endpoint, not once against endpoint 0. A FULLY-CONCRETE path (endpoint + cluster + attribute all named) that is denied returns an AttributeStatusIB carrying UnsupportedAccess (0x7e); a WILDCARD-expanded result that is denied is SILENTLY OMITTED — a wildcard read discloses only authorized paths (Matter §8.4.3.2). PASE sessions bypass the check — commissioning reads must succeed before the fabric's ACL entry exists, and keep succeeding after AddNOC adopted the session onto the new fabric (matter.js packages/protocol/src/interaction/FabricAccessControl.ts:189-191 keys the implicit Administer grant on the auth mode, not on the fabric), so the bypass fires on fabricIndex==0 OR IsPASEFromContext. Mirrors matter.js packages/protocol/src/action/server/AttributeReadResponse.ts:238-274 (addConcrete → error status) and readAttributeForWildcard (bare return).
func UnmarshalReportDataTLV ¶ added in v0.4.0
func UnmarshalReportDataTLV(dec *tlv.Decoder, valueReader AttributeValueReader, eventReader EventDataReader) (ReportData, error)
UnmarshalReportDataTLV decodes a ReportDataMessage as a client receives it — matter.js packages/types/src/protocol/messages/TlvDataReport.ts:17-31 at 0ceb202d: SubscriptionId (0), AttributeReports (1), EventReports (2), MoreChunkedMessages (3), SuppressResponse (4). An AttributeReportIB is a status (AttributeStatusIB) or data (AttributeDataIB: DataVersion (0, optional), Path (1), Data (2), the value read through valueReader); an EventReportIB a status (EventStatusIB) or data (EventDataIB: Path (0), EventNumber (1), Priority (2), the four optional timestamps (3-6), Data (7, optional) through eventReader) — TlvAttributeReport.ts, TlvAttributeReportData.ts, TlvEventReport.ts, TlvEventData.ts. A nil reader skips the values. The path's EnableTagCompression flag is kept on the report for the client to resolve (InputChunk.ts:252-273).
func (ReportData) MarshalTLV ¶
func (r ReportData) MarshalTLV(enc *tlv.Encoder, valueWriter AttributeValueWriter)
MarshalTLV encodes r as a ReportDataMessage. valueWriter writes each AttributeValue into TLV; the IM layer does not own cluster-native type encoding. Event payload writing reuses valueWriter — events surface their data with the same cluster-native shapes that attributes do.
type StatusCode ¶
type StatusCode uint8
StatusCode is a Matter Interaction Model status code per Core Spec §8.10 Table 96. Only the values that have a non-trivial mapping to our Dispatcher surface are spelled out — extending is a one-line constant addition.
const ( StatusSuccess StatusCode = 0x00 StatusFailure StatusCode = 0x01 StatusInvalidSubscription StatusCode = 0x7d StatusUnsupportedAccess StatusCode = 0x7e StatusUnsupportedEndpoint StatusCode = 0x7f StatusInvalidAction StatusCode = 0x80 StatusUnsupportedCommand StatusCode = 0x81 StatusInvalidCommand StatusCode = 0x85 StatusUnsupportedAttribute StatusCode = 0x86 StatusConstraintError StatusCode = 0x87 StatusUnsupportedWrite StatusCode = 0x88 StatusResourceExhausted StatusCode = 0x89 StatusNotFound StatusCode = 0x8b StatusInvalidDataType StatusCode = 0x8d StatusUnsupportedRead StatusCode = 0x8f StatusDataVersionMismatch StatusCode = 0x92 StatusTimeout StatusCode = 0x94 StatusBusy StatusCode = 0x9c StatusUnsupportedCluster StatusCode = 0xc3 StatusNeedsTimedInteraction StatusCode = 0xc6 StatusUnsupportedEvent StatusCode = 0xc7 // Matter 1.4–1.5 additions per matter.js // packages/types/src/globals/Status.ts (HEAD 2025-05). StatusUnsupportedNode StatusCode = 0x9b StatusAccessRestricted StatusCode = 0x9d StatusPathsExhausted StatusCode = 0xc8 StatusTimedRequestMismatch StatusCode = 0xc9 StatusFailsafeRequired StatusCode = 0xca StatusInvalidInState StatusCode = 0xcb StatusNoCommandResponse StatusCode = 0xcc StatusTermsAndConditionsChanged StatusCode = 0xcd StatusMaintenanceRequired StatusCode = 0xce StatusDynamicConstraintError StatusCode = 0xcf StatusAlreadyExists StatusCode = 0xd0 StatusInvalidTransportType StatusCode = 0xd1 )
StatusCode values.
func AuthorityAt ¶ added in v0.2.0
func AuthorityAt(ctx context.Context, endpoint uint16, clusterID uint32, privilege uint8) StatusCode
AuthorityAt reports whether the subject of the request ctx belongs to holds privilege at (endpoint, clusterID). A cluster server calls it when a command needs more than the command's own privilege for part of its request — Groupcast's JoinGroup with a key, say, which requires Administer although the command needs Manage. Without a stamped check it denies: a privilege nobody could verify is not held.
Mirrors matter.js `session.authorityAt(AccessLevel, location)` (packages/protocol/src/action/server/AccessControl.ts), which GroupcastServer #requireAdmin evaluates.
func HandleGroupInvokeRequest ¶ added in v0.2.0
func HandleGroupInvokeRequest(ctx context.Context, d Dispatcher, req InvokeRequest, timedRequired func(clusterID, commandID uint32) bool) StatusCode
HandleGroupInvokeRequest runs an InvokeRequest that arrived as a group message and returns the status it ended with; see HandleGroupInvoke.
func HandleGroupWriteRequest ¶ added in v0.2.0
func HandleGroupWriteRequest(ctx context.Context, d Dispatcher, req WriteRequest) StatusCode
HandleGroupWriteRequest runs a WriteRequest that arrived as a group message. ctx carries the fabric and the Group subject.
Mirrors matter.js InteractionServer.handleWriteRequest for a group session and AttributeWriteResponse.process / #processWildcard / #writeAttributeForWildcard: chunking together with SuppressResponse is InvalidAction, a TimedRequest flag mismatches, a group write without SuppressResponse is InvalidAction; the writes then run in order, each on every member endpoint that hosts the attribute, where the attribute is writable and the Group subject holds its write privilege; a concrete path ends the interaction with InvalidAction ("Group writes can not be concrete paths") — after the writes before it have run, as in matter.js.
func ValidateInvokeBatch ¶
func ValidateInvokeBatch(req InvokeRequest) StatusCode
ValidateInvokeBatch enforces the batch-invoke path rules a controller may violate when it packs more than one CommandDataIB into a single InvokeRequest. Returns StatusInvalidAction on the first offending command, else StatusSuccess. A single-command invoke always passes. Mirrors matter.js packages/protocol/src/action/server/CommandInvokeResponse.ts:64-92,171-185 (CommandInvokeResponse.process / #processConcrete):
- the batch must not exceed DefaultMaxPathsPerInvoke paths, checked before any per-command validation or dispatch (matter.js packages/node/src/node/server/InteractionServer.ts:950-955 `if (invokeRequests.length > this.#maxPathsPerInvoke) throw new StatusResponseError(..., Status.InvalidAction)`);
- a wildcard-endpoint path is illegal in a batch (matter.js "Wildcard path must not be used with multiple invokes");
- every concrete path in a batch MUST carry a CommandRef ("The CommandRef field must be specified for all commands in a batch invoke");
- duplicate concrete (endpoint, cluster, command) paths are rejected ("Duplicate concrete command path on batch invoke");
- duplicate CommandRefs are rejected ("Duplicate commandRef on batch invoke").
The wire decoder guarantees every command path carries a cluster and a command, so a "wildcard" here is a path without a concrete endpoint.
func ValidateInvokeBatchWithin ¶ added in v0.8.6
func ValidateInvokeBatchWithin(req InvokeRequest, maxPaths int) StatusCode
ValidateInvokeBatchWithin is ValidateInvokeBatch with the node's own MaxPathsPerInvoke in place of DefaultMaxPathsPerInvoke; maxPaths 0 is the default. Mirrors matter.js packages/node/src/node/server/InteractionServer.ts:198 (`#maxPathsPerInvoke = node.state.basicInformation.maxPathsPerInvoke ?? DEFAULT_MAX_PATHS_PER_INVOKE`) and :1017, the limit the node advertises.
func ValidateReadPaths ¶
func ValidateReadPaths(attrs []ConcreteAttributePath, events []ConcreteEventPath) StatusCode
ValidateReadPaths enforces the Matter §8.4.3.2 path rules shared by Read and Subscribe: a wildcard cluster (HasCluster == false) combined with a concrete non-global attribute, or with a concrete event, is illegal — the whole action must be rejected up front with InvalidAction rather than silently expanded. Returns StatusInvalidAction on the first offending path, StatusPathsExhausted when the request names more than MaxReadPaths paths in total, else StatusSuccess. Mirrors matter.js packages/node/src/node/server/InteractionServer.ts validateReadAttributesPath / validateReadEventPath (#3926) followed by the path-count ceiling (:362-369, :600-604) — in that order. Callers (the read and subscribe dispatchers) run this before building any report and answer with the returned status.
func (StatusCode) IsSuccess ¶
func (s StatusCode) IsSuccess() bool
IsSuccess reports whether the status code is the canonical success value (0x00). Used at every Dispatcher boundary to short-circuit error paths.
func (StatusCode) String ¶
func (s StatusCode) String() string
String returns a short label for diagnostics; not part of the wire format.
type StatusCodeError ¶
type StatusCodeError interface {
MatterStatusCode() StatusCode
Error() string
}
StatusCodeError is the optional interface cluster-server errors implement when they carry a well-typed Matter IM status code. The dispatcher (endpoint/dispatcher.go classifyError) maps an error that implements it to its status; any other error answers FAILURE — the dispatcher reads no status out of an error's text.
Pattern: define a package-private sentinel type that wraps an error string and returns its status code, then export an errors.New-style variable that implements this interface. The dispatcher then needs no import of the cluster package:
type busyError struct{}
func (busyError) Error() string { return "busy" }
func (busyError) MatterStatusCode() StatusCode { return StatusBusy }
var ErrBusy StatusCodeError = busyError{}
This replaces fragile string-contains heuristics with exact typed dispatch.
var ErrUnsupportedCommand StatusCodeError = unsupportedCommandError{"im: unsupported command"}
ErrUnsupportedCommand is the sentinel UnsupportedCommandf values match under errors.Is. Matter §10.6.7.4 UnsupportedCommand (0x81).
type StatusIB ¶
type StatusIB struct {
Status StatusCode
ClusterStatus uint8
HasClusterStatus bool
}
StatusIB is the Status Information Block — the Matter-level (status, cluster-status) tuple that rides inside Read / Write / Invoke responses.
func UnmarshalStatusIBTLV ¶
UnmarshalStatusIBTLV decodes a StatusIB from the next element in the decoder.
type StatusResponse ¶
type StatusResponse struct {
Status StatusCode
}
StatusResponse is the in-memory form of a StatusResponseMessage. The bridge emits it as the reply to a TimedRequest (always Success) and as the top-level rejection of a malformed action; it receives one from the controller after every ReportData chunk and every ongoing subscription report (Matter §8.6.2), where the carried status decides whether the interaction may continue.
func UnmarshalStatusResponseTLV ¶
func UnmarshalStatusResponseTLV(dec *tlv.Decoder) (StatusResponse, error)
UnmarshalStatusResponseTLV parses a StatusResponseMessage TLV payload. Mirrors matter.js TlvStatusResponse decoding in packages/protocol/src/interaction/InteractionMessenger.ts:183-196 (throwIfErrorStatusMessage), which reads the status of every inbound StatusResponse before deciding whether the exchange goes on.
func (StatusResponse) MarshalTLV ¶
func (sr StatusResponse) MarshalTLV(enc *tlv.Encoder)
MarshalTLV encodes sr at the top level (anonymous tag).
type SubscribeRequest ¶
type SubscribeRequest struct {
KeepSubscriptions bool
MinIntervalFloor uint16
MaxIntervalCeiling uint16
AttributeRequests []ConcreteAttributePath
// EventRequests carries the subscription's interest in cluster
// events (Matter §10.6.6). Wildcards work the same way as
// AttributeRequests: leaving HasEndpoint / HasCluster / HasEvent
// false matches every value.
EventRequests []ConcreteEventPath
// EventFilters carries per-source minimum-event-number hints the
// controller sends to avoid re-receiving events it has already seen
// (Matter §10.6.9 EventFilterIB). The bridge evaluates the filters
// in BuildEventReports and skips events whose Number ≤ the
// supplied EventMin.
EventFilters []EventMinimumNumber
FabricFiltered bool
// DataVersionFilters carries per-cluster version hints the
// controller sends to avoid re-receiving attribute data it already
// has cached (Matter §10.6.5). The bridge evaluates the filters
// during the initial ReportData assembly and skips clusters whose
// current DataVersion matches the cached one.
DataVersionFilters []DataVersionFilter
}
SubscribeRequest is the in-memory form of a SubscribeRequestMessage.
This type is the message shape only. The state machine around it — cadence enforcement and the per-fabric subscription cap — lives in im/subscription.Manager, which the daemon attaches to the bridge, so both are in force in production. Replay buffering is the part that remains unbuilt. The message-layer surface is kept stable against cluster-side callers that need to express "subscribe to OnOff state".
func UnmarshalSubscribeRequestTLV ¶
func UnmarshalSubscribeRequestTLV(dec *tlv.Decoder) (SubscribeRequest, error)
UnmarshalSubscribeRequestTLV decodes a SubscribeRequestMessage.
func (SubscribeRequest) MarshalTLV ¶
func (r SubscribeRequest) MarshalTLV(enc *tlv.Encoder)
MarshalTLV encodes r as a client sends it. Mirrors matter.js packages/types/src/protocol/messages/TlvSubscribeRequest.ts:19-44 at 0ceb202d: KeepSubscriptions (0), MinIntervalFloor (1) and MaxIntervalCeiling (2) in seconds, the optional lists — AttributeRequests (3), EventRequests (4), EventFilters (5) — only with members, IsFabricFiltered (7), DataVersionFilters (8) only with members, InteractionModelRevision (0xFF), in field order.
type SubscribeResponse ¶
SubscribeResponse is the in-memory form of a SubscribeResponseMessage.
func UnmarshalSubscribeResponseTLV ¶ added in v0.4.0
func UnmarshalSubscribeResponseTLV(dec *tlv.Decoder) (SubscribeResponse, error)
UnmarshalSubscribeResponseTLV decodes a SubscribeResponseMessage — matter.js TlvSubscribeResponse.ts:13-21: SubscriptionId (0) and MaxInterval (2), both mandatory.
func (SubscribeResponse) MarshalTLV ¶
func (sr SubscribeResponse) MarshalTLV(enc *tlv.Encoder)
MarshalTLV encodes sr.
SubscriptionId keeps the fixed 4-byte form Apple's MTRDevice needs (by_design L5-Apple-Width). MaxInterval is encoded at the smallest fitting width, as matter.js does (TlvSubscribeResponse.ts:18 at 0ceb202d); chip reads it with TLVReader::Get(uint16_t&), which accepts any unsigned width that fits (src/app/MessageDef/SubscribeResponseMessage.cpp:87-90, src/lib/core/TLVReader.cpp:216-226 at 6170af84), so an earlier note here that a narrowed MaxInterval collapses chip-tool's liveness timer does not match chip's reader.
type TimedCommandProvider ¶ added in v0.3.0
type TimedCommandProvider interface {
IsTimedCommand(endpoint uint16, clusterID, cmdID uint32) bool
}
TimedCommandProvider is the optional Dispatcher capability HandleInvokeRequest consults for whether a concrete command needs a timed interaction: the dispatcher asks the cluster server it resolves (contract.TimedCommander, answered from a generated definition's model) and falls back to schema.IsTimedInvoke for a hand-written server or a path it does not resolve. A dispatcher without it is answered by schema.IsTimedInvoke alone. Mirrors matter.js packages/protocol/src/action/server/CommandInvokeResponse.ts:292-295, which reads `limits.timed` from the command's model.
type TimedRequest ¶
type TimedRequest struct {
// TimeoutMs is the maximum number of milliseconds the bridge
// must wait between this TimedRequest and the follow-up
// Write / Invoke that it gates. Per Matter §10.7.1 the value is
// uint16 (max 65 535 ms ≈ 65 s).
TimeoutMs uint16
}
TimedRequest is the in-memory form of a TimedRequestMessage. The commissioner sends one before a Write/Invoke that the spec requires to be timed (e.g. door-lock unlocks). The bridge replies with a StatusResponse{Success} and stamps a per-exchange deadline; the matching follow-up Write/Invoke is then gated by `Bridge.checkTimedGate` per Matter §8.7 — late or missing follow-ups get rejected with TIMEOUT (0x94) or NEEDS_TIMED_INTERACTION (0xC6).
func UnmarshalTimedRequestTLV ¶
func UnmarshalTimedRequestTLV(dec *tlv.Decoder) (TimedRequest, error)
UnmarshalTimedRequestTLV parses a TimedRequestMessage TLV payload.
func (TimedRequest) MarshalTLV ¶ added in v0.4.0
func (r TimedRequest) MarshalTLV(enc *tlv.Encoder)
MarshalTLV encodes r as a client sends it — matter.js packages/types/src/protocol/messages/TlvTimedRequest.ts:13-17 (Timeout (0) in milliseconds, InteractionModelRevision (0xFF)), as InteractionClientMessenger.sendTimedRequest fills it (interaction/InteractionMessenger.ts:1124-1136).
type WriteAuthorizer ¶
type WriteAuthorizer func(endpoint uint16, clusterID, attrID uint32) StatusCode
WriteAuthorizer decides whether the requesting subject may write the RESOLVED (endpoint, cluster, attribute). It returns StatusSuccess when access is granted and a denial status (typically StatusUnsupportedAccess) otherwise.
A wildcard-endpoint write carries no endpoint on the requested path, so the only place its privilege can be evaluated is where the path resolves. Authorizing the un-expanded request instead would check a zero-value endpoint and then apply the value to every endpoint the wildcard reaches.
type WriteRequest ¶
type WriteRequest struct {
SuppressResponse bool
TimedRequest bool
Writes []AttributeWrite
// MoreChunkedMessages signals that further WriteRequest chunks
// follow on the same exchange (Matter §10.6.5 tag 3). Each chunk
// is answered with its own WriteResponse — matter.js
// InteractionServer.ts:521-532 sends a per-chunk WriteResponse
// and then reads the next chunk. The flag is invalid in
// combination with SuppressResponse or a timed interaction
// (InteractionServer.ts:397/:408 → InvalidAction).
MoreChunkedMessages bool
}
WriteRequest is the in-memory form of a WriteRequestMessage.
func UnmarshalWriteRequestTLV ¶
func UnmarshalWriteRequestTLV(dec *tlv.Decoder, valueReader AttributeValueReader) (WriteRequest, error)
UnmarshalWriteRequestTLV decodes a WriteRequestMessage. valueReader is invoked for the Data field of each AttributeDataIB; pass a [DefaultValueReader]-style helper that captures the Element tree when cluster-aware decoding is not yet available.
func (WriteRequest) MarshalTLV ¶ added in v0.4.0
func (w WriteRequest) MarshalTLV(enc *tlv.Encoder, valueWriter AttributeValueWriter)
MarshalTLV encodes w as a client sends it. Mirrors matter.js packages/types/src/protocol/messages/TlvWriteRequest.ts:16-27 at 0ceb202d with the request action/request/Write.ts:56-184 builds: SuppressResponse (0) only when set, TimedRequest (1), WriteRequests (2) of AttributeDataIB — DataVersion (0) when set, Path (1), Data (2) through valueWriter (TlvAttributeData.ts) — MoreChunkedMessages (3), which a client sends false (Write.ts:77), and InteractionModelRevision (0xFF).
type WriteResponse ¶
type WriteResponse struct {
Responses []AttributeStatus
}
WriteResponse is the in-memory form of a WriteResponseMessage.
func HandleWriteRequest ¶
func HandleWriteRequest(ctx context.Context, d Dispatcher, req WriteRequest) WriteResponse
HandleWriteRequest dispatches a parsed WriteRequest through d and returns the assembled WriteResponse.
DataVersion mismatch check: mirrors matter.js packages/node/src/node/server/InteractionServer.ts DataVersion check before write dispatch, and chip src/app/WriteHandler.cpp AttributeAccessInterface DataVersion verification. When the caller supplies a non-zero DataVersion in an AttributeDataIB and d implements DataVersionReader, the cluster's current version is queried; a mismatch returns StatusDataVersionMismatch (0x92) instead of dispatching the write.
ACL gate: mirrors matter.js packages/node/src/node/server/OnlineServerInteraction.ts FabricAccessControl.forRequest and chip src/app/WriteHandler.cpp:780 "Execute the ACL Access Granting Algorithm before existence checks". When d implements ACLChecker, the requesting fabric's privilege is verified before each write path is dispatched, at the location the path resolves to. The required privilege for a plain Write is Operate (3) per Matter §9.10.4.4, raised per attribute by AttributeWritePrivilegeProvider. fabricIndex is extracted via FabricFilterFromContext; a PASE session bypasses the ACL check — fabricIndex==0 before AddNOC, and IsPASEFromContext after it, mirroring matter.js packages/protocol/src/interaction/FabricAccessControl.ts:189-191, which keys the implicit Administer grant on the session's auth mode.
func UnmarshalWriteResponseTLV ¶ added in v0.4.0
func UnmarshalWriteResponseTLV(dec *tlv.Decoder) (WriteResponse, error)
UnmarshalWriteResponseTLV decodes a WriteResponseMessage — matter.js TlvWriteResponse.ts:15-19: WriteResponses (0), an array of AttributeStatusIB (TlvAttributeStatus.ts: Path (0), Status (1)).
func (WriteResponse) MarshalTLV ¶
func (wr WriteResponse) MarshalTLV(enc *tlv.Encoder)
MarshalTLV encodes wr.
type WriteResult ¶
type WriteResult struct {
Path ConcreteAttributePath
Status StatusCode
// ClusterStatus carries the cluster-specific status byte from
// Matter §10.6.2.2 StatusIB. Surfaced when a cluster server
// returns an error that implements [MatterClusterStatusError]
// (e.g. AdministratorCommissioning §11.19.7.3 PAKEParameterError
// = 0x02). Zero + HasClusterStatus=false means "no cluster-specific
// status" — wire encoding omits the ClusterStatus tag.
ClusterStatus uint8
HasClusterStatus bool
}
WriteResult is one (path, status) pair produced by a Write.
type WriteTransaction ¶ added in v0.2.0
type WriteTransaction struct {
// contains filtered or unexported fields
}
WriteTransaction carries what one Write interaction remembers across the WriteRequest messages of a chunked write: the attribute its last data element was written to successfully (chip WriteHandler mLastSuccessfullyWrittenPath, which lives as long as the interaction). The transport keeps one per chunked write and hands it to every chunk through WithWriteTransaction; the zero value starts a transaction.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package subscription implements the Matter subscription state machine layered on top of the im message codecs.
|
Package subscription implements the Matter subscription state machine layered on top of the im message codecs. |