class

Matter::Protocol::MessageHandler

Inherits Matter::Protocol::SessionManager < Reference < Object

Protocol message handler that routes incoming messages to appropriate handlers

Handles:

  • Secure Channel protocol (0x0000) - PASE, CASE, etc.
  • Interaction Model protocol (0x0001) - Read, Write, Invoke, Subscribe

Constants

DEFAULT_MAX_SESSIONS = 32_u16

Session management defaults

DEFAULT_SESSION_CLEANUP_INTERVAL = 5.seconds
DEFAULT_SUBSCRIPTION_GRACE_PERIOD = 30.seconds
DEFAULT_TRANSPORT_RETRY_WINDOW = 4.seconds
Log = ::Log.for("matter.protocol")
MRP_DUPLICATE_RESPONSE_MAX_ENTRIES = 512
MRP_DUPLICATE_RESPONSE_TTL = 10.seconds

Keep a short-lived cache of encrypted responses keyed by the incoming message counter. This allows us to handle MRP retransmissions from controllers (notably iOS) without re-invoking cluster logic.

MSG_CASE_SIGMA1 = 48_u8
MSG_CASE_SIGMA2 = 49_u8
MSG_CASE_SIGMA3 = 50_u8
MSG_PASE_PAKE1 = 34_u8
MSG_PASE_PAKE2 = 35_u8
MSG_PASE_PAKE3 = 36_u8
MSG_PBKDF_PARAM_REQUEST = 32_u8
MSG_PBKDF_PARAM_RESPONSE = 33_u8
MSG_STANDALONE_ACK = 16_u8

Secure Channel Message Types

MSG_STATUS_REPORT = 64_u8
PROTOCOL_BDX = 2_u16
PROTOCOL_INTERACTION_MODEL = 1_u16
PROTOCOL_SECURE_CHANNEL = 0_u16

Protocol IDs

PROTOCOL_USER_DIRECTED_COMM = 3_u16

Constructors

new(transport : Transport::UDPTransport, setup_pin : UInt32, discriminator : UInt16, fabric_table : FabricTable, iterations : UInt32 = 1000_u32, salt : Bytes = Random::Secure.random_bytes(32), vendor_id : UInt16 = 65521_u16, product_id : UInt16 = 32769_u16, max_sessions : UInt16 = DEFAULT_MAX_SESSIONS, subscription_grace_period : Time::Span = DEFAULT_SUBSCRIPTION_GRACE_PERIOD, transport_retry_window : Time::Span = DEFAULT_TRANSPORT_RETRY_WINDOW, persistence : Persistence::Base | Nil = nil)
Source

Instance methods

active_subscriptions

Public getter for active subscriptions (for persistence)

Source
cancel_cleanup_on_traffic(session_id : UInt16) : Bool

Cancel pending cleanup for a session if traffic is detected Called when we receive a message on a session that has cancel_on_traffic=true

Source
case_fabric
Source
case_fabric=(case_fabric : Fabric | Nil)
Source
case_initiator_session_id
Source
case_initiator_session_id=(case_initiator_session_id : UInt16 | Nil)
Source
case_responder

CASE support

Source
case_responder=(case_responder : Session::Case::CaseResponder | Nil)

CASE support

Source
case_responder_session_id
Source
case_responder_session_id=(case_responder_session_id : UInt16 | Nil)
Source
clusters
Source
configure_pase_pin(pin : UInt32, iterations : UInt32, salt : Bytes) : Nil

Configure PASE server parameters for a basic commissioning window.

Source
configure_pase_server(passcode_verifier : Bytes, iterations : UInt32, salt : Bytes) : Nil

Configure PASE server parameters for an enhanced commissioning window. The passcode verifier is the pre-computed w0||L (97 bytes) used by SPAKE2+.

Source
delete_session(session_id : UInt16) : Bool

Remove a session (and its subscriptions) from application code.

This updates internal state and triggers persistence hooks, then calls on_session_removed if configured.

Source
delete_subscription(subscription_id : UInt32) : Bool

Remove an active subscription from application code.

This updates internal state and triggers persistence hooks, then calls on_subscription_removed if configured.

Source
discriminator
Source
discriminator=(discriminator : UInt16)
Source
fabric_table
Source
find_matching_subscription(session_id : UInt16, paths : Array(InteractionModel::AttributePath)) : ActiveSubscription | Nil

Find existing subscription that matches a new subscription request (same session, overlapping paths)

Source
handle_message(msg : Codec::MessageCodec::Message, peer : Socket::IPAddress) : Nil

Main message routing entry point

Source
initiator_session_id

PASE session IDs from PBKDF exchange

Source
initiator_session_id=(initiator_session_id : UInt16 | Nil)

PASE session IDs from PBKDF exchange

Source
iterations
Source
iterations=(iterations : UInt32)
Source
mark_case_resumption_failed(session_id : UInt16) : Nil

Mark a session for cleanup due to CASE resumption failure Called when CASE resumption is attempted but fails

Source
mark_transport_failure(session_id : UInt16) : Nil

Mark a session as having transport failure and schedule cleanup Called when transport reports the peer is unreachable after retries

Source
max_sessions

Session management configuration

Source
max_sessions=(max_sessions : UInt16)

Session management configuration

Source
next_subscription_id

Subscription support

Source
next_subscription_id=(next_subscription_id : UInt32)

Subscription support

Source
notify_subscriptions(endpoint_id : UInt16, cluster_id : UInt32, attribute_id : UInt32)

Handle attribute change and send updates to matching subscriptions This is called by clusters when their attributes change

Source
notify_subscriptions_batched(attributes : Array(Tuple(UInt16, UInt32, UInt32)))

Batch multiple attribute changes into a single subscription update This is important to avoid overwhelming controllers (especially iOS) with rapid-fire updates. Each tuple is (endpoint_id, cluster_id, attribute_id).

Source
on_commissioned

Commissioning callback - called when a fabric is successfully added (AddNOC complete) The device should use this to switch from commissioning to operational mDNS advertisement

Source
on_commissioned=(on_commissioned : Proc(Fabric, Nil) | Nil)

Commissioning callback - called when a fabric is successfully added (AddNOC complete) The device should use this to switch from commissioning to operational mDNS advertisement

Source
on_get_fabric

Fabric access callback - set by the device implementation This allows the message handler to access fabric data for CASE

Source
on_get_fabric=(on_get_fabric : Proc(Fabric | Nil) | Nil)

Fabric access callback - set by the device implementation This allows the message handler to access fabric data for CASE

Source
on_session_established

Session established callback - called when a new secure session is established (CASE or PASE) The device can use this to persist sessions for reconnection after restart

Source
on_session_established=(on_session_established : Proc(Session::SecureContext, Nil) | Nil)

Session established callback - called when a new secure session is established (CASE or PASE) The device can use this to persist sessions for reconnection after restart

Source
on_session_removed

Callback fired when a superseded session is cleaned up Device can use this to remove session from persistent storage

Source
on_session_removed=(on_session_removed : Proc(UInt16, Nil) | Nil)

Callback fired when a superseded session is cleaned up Device can use this to remove session from persistent storage

Source
on_subscription_established

Subscription established callback - called when a new subscription becomes active The device can use this to persist subscriptions for reconnection after restart

Source
on_subscription_established=(on_subscription_established : Proc(ActiveSubscription, Nil) | Nil)

Subscription established callback - called when a new subscription becomes active The device can use this to persist subscriptions for reconnection after restart

Source
on_subscription_removed

Callback fired when a subscription is removed (expired or renewed)

Source
on_subscription_removed=(on_subscription_removed : Proc(UInt32, Nil) | Nil)

Callback fired when a subscription is removed (expired or renewed)

Source
operational_credentials_cluster
Source
pase_passcode_verifier

If set, PASE responder will use a pre-computed passcode verifier (w0||L) instead of deriving it from the setup pin (used for enhanced commissioning windows).

Source
pase_passcode_verifier=(pase_passcode_verifier : Bytes | Nil)

If set, PASE responder will use a pre-computed passcode verifier (w0||L) instead of deriving it from the setup pin (used for enhanced commissioning windows).

Source
pase_responder
Source
pbkdf_request_payload

PASE context: store request/response payloads for context hashing

Source
pbkdf_request_payload=(pbkdf_request_payload : Bytes | Nil)

PASE context: store request/response payloads for context hashing

Source
pbkdf_response_payload
Source
pbkdf_response_payload=(pbkdf_response_payload : Bytes | Nil)
Source
persist_all_sessions

Persist all active CASE sessions to storage Call this periodically (e.g., every 30s) or before graceful shutdown to ensure message counters and other session state are up to date

Source
persistence
Source
process_expired_subscriptions

Process expired subscriptions and schedule session cleanup if needed

Source
process_pending_cleanups

Manually trigger cleanup of expired pending sessions (useful for testing)

Source
product_id
Source
product_id=(product_id : UInt16)
Source
renew_subscription(old_subscription_id : UInt32, new_subscription : ActiveSubscription) : Nil

Handle subscription renewal - called when a new SubscribeRequest comes in for the same attribute paths from the same session

Source
reset_pase_server

Restore default PASE parameters and clear any enhanced verifier.

Source
responder_session_id
Source
responder_session_id=(responder_session_id : UInt16 | Nil)
Source
salt
Source
salt=(salt : Bytes)
Source
sessions
Source
setup_cluster_notifications

Wire up attribute change notification callbacks for all clusters This should be called after all clusters have been added to the clusters hash It enables automatic subscription updates when attributes change

Source
setup_pin

Device credentials for PASE

Source
setup_pin=(setup_pin : UInt32)

Device credentials for PASE

Source
subscription_grace_period
Source
subscription_grace_period=(subscription_grace_period : Time::Span)
Source
transport
Source
transport_retry_window
Source
transport_retry_window=(transport_retry_window : Time::Span)
Source
vendor_id
Source
vendor_id=(vendor_id : UInt16)
Source

Nested types