Node association
What is node association
Node association is the process of associating a node to a home/group and establishing ownership.
Why it is needed
Node association establishes ownership of a node by a user, where both are already registered with the backend. Once ownership is established, the client can send Wi-Fi credentials to the node so it can join the network.
Pre-requisites
User is registered and authenticated
Node is registered (for RainMaker nodes; pure Matter nodes need not be pre-registered). The node need not be connected to the cloud: in the primary flow it has no Wi-Fi credentials yet when
verifyruns, and the challenge is exchanged over the local link.Group is created (and must have
mattercapability for Matter flows)User and node can reach each other over a local channel — BLE or SoftAP during provisioning, or the node’s mDNS control endpoint (
_esp_rmaker_ctrl._tcp) once it has joined the network
How is this secure
The challenge is signed by the node using its private key which confirms the node is registered with the backend. The user client and node need to be in vicinity (to enable local communication) which confirms the user owns the node.
Node types
There are three types of nodes that can be associated:
Node Type |
Description |
Verify Input |
NOC Generated? |
Confirm Required? |
|---|---|---|---|---|
RainMaker |
Traditional RainMaker node registered in AWS IoT |
|
No |
No (added to group immediately) |
RainMaker + Matter |
RainMaker node with Matter capability |
|
Yes |
Yes |
Pure Matter |
Matter-only node, not pre-registered in RainMaker |
|
Yes |
Yes |
Note: RainMaker nodes can also use the
challenge_responseflow on Matter-capable groups. In that case they are added directly to the group without NOC generation.
External Flow
Here client can be the app or the cli
User clicks on the “add device” button in the client for the selected home/group
User is redirected to the node association page
User scans the QR code (This QR contains the local communication details)
Does local communication pairing with the node
Client asks node to scan for Wifi networks
User selects the Wifi network and enters the WiFi credentials
Client calls association initiate API to get the request ID and challenge
For RainMaker nodes (challenge_response flow):
Client sends the challenge to the node over local communication
Node hashes the challenge (SHA256), signs it using its private key, and hex-encodes the signature
Client sends the challenge response and node_id to the association verify API
On success, node is added to the group immediately
For Matter nodes (nocsr_elements flow):
Client sends the CSR request to the node (challenge from initiate is used as CSR nonce)
Node creates NOCSRElements TLV containing: CSR, CSRNonce, and optionally vendor_reserved1 (RainMaker node_id)
Client also obtains the attestation challenge and attestation signature from the Matter commissioning session
Client sends
nocsr_elements,attestation_challenge, andattestation_signatureto the verify APIIt gets the NOC (Node Operational Certificate) and matter_node_id back
Does local Matter commissioning with the NOC
Calls the association confirm API to finalize
Once the API returns success, client sends the WiFi credentials to the node locally
Node connects to the WiFi network
Sequence Diagram
sequenceDiagram
title Node Association - External Flow
actor User
participant Client as Client (App/CLI)
participant Node as Node (Device)
participant API as Backend API
User->>Client: Click "Add Device" for group
activate Client
Client->>Client: Redirect to association page
Client->>User: Display QR code (local communication details)
User->>Client: Scan QR code
Client->>Node: Local communication pairing (BLE/SoftAP)
activate Node
Client->>Node: Request WiFi scan
Node->>Client: Return WiFi networks list
Client->>User: Display WiFi networks
User->>Client: Select WiFi network + Enter credentials
Client->>API: POST /v1/groups/{groupId}/node-assoc-requests (No body)
activate API
API->>API: Generate request_id (32 alphanumeric chars)<br/>Generate challenge
Note right of API: Non-Matter: 64 alphanumeric chars<br/>Matter: 32 bytes (64 hex chars)
API->>API: Store in DB (assoc-requests table)<br/>TTL: 5 minutes
API->>Client: Response: {request_id, challenge}
deactivate API
alt RainMaker flow (challenge_response)
Client->>Node: Send challenge (over local comm)
Node->>Node: Hash challenge (SHA256)
Node->>Node: Sign hashed challenge (using private key)
Node->>Node: Hex encode signature
Node->>Client: Return challenge_response (hex encoded)
Client->>API: POST /v1/groups/{groupId}/node-assoc-requests/{requestId}/verify<br/>{challenge_response, node_id}
activate API
API->>Client: {message: "success"}
deactivate API
else Matter flow (nocsr_elements)
Client->>Node: Send CSR request (challenge as nonce)
Node->>Node: Build NOCSRElements TLV<br/>(CSR + CSRNonce + optional vendor_reserved1)
Node->>Client: Return NOCSRElements + attestation data
Client->>API: POST /v1/groups/{groupId}/node-assoc-requests/{requestId}/verify<br/>{nocsr_elements, attestation_challenge, attestation_signature}
activate API
API->>Client: {message: "success", noc, matter_node_id}
deactivate API
Client->>Node: Install NOC (Matter commissioning)
Client->>API: POST /v1/groups/{groupId}/node-assoc-requests/{requestId}/confirm<br/>(No body)
activate API
API->>Client: {message: "success"}
deactivate API
end
alt Success
Client->>Node: Send WiFi credentials (over local comm)
Node->>Node: Connect to WiFi network
Node->>Client: Connection status
end
deactivate Node
deactivate Client
Internal Flow
API Endpoints
All endpoints require POST method. All other methods return 405 Method Not Allowed.
Endpoint |
Purpose |
|---|---|
|
Initiate association |
|
Verify challenge/attestation |
|
Confirm Matter association |
Association Initiate API
POST /v1/groups/{groupId}/node-assoc-requests
Request: No request body required
Process:
Extract
user_idfrom Cognito authentication contextGenerate
request_id(32 alphanumeric characters)Load the group by
group_idand check its capabilitiesGenerate challenge:
Matter-capable group: 32 bytes of cryptographically secure random data, hex-encoded (64 hex characters). This will be used as the CSR nonce.
Non-Matter group: 64 alphanumeric characters (a-z, A-Z, 0-9)
Store in the database:
Table name:
rmng-node-assoc-reqsAttributes:
request_id(PK),challenge,user_id,group_id,is_matter_group,status(“pending”),expiration_time(TTL of 5 minutes)
Response:
{ "request_id": "abc123def456...", "challenge": "<challenge_string>" }
Association Verify API
POST /v1/groups/{groupId}/node-assoc-requests/{requestId}/verify
The verify API supports two mutually exclusive authentication methods. Exactly one of challenge_response or nocsr_elements must be provided.
Common Validation (both flows)
Verify
requestIdandgroupIdare present in path parametersParse the request body and enforce mutual exclusivity of
challenge_responseandnocsr_elementsRetrieve the stored association request from DynamoDB by
request_idVerify
user_idfrom authentication context matches the storeduser_idVerify
group_idfrom path matches the storedgroup_id
Flow 1: challenge_response (RainMaker signature verification)
This flow works for both Matter and non-Matter groups. It verifies the node using its registered certificate in AWS IoT Core.
Request:
{ "challenge_response": "<hex_encoded_signature>", "node_id": "<registered_node_id>" }
node_idis required when usingchallenge_responseProcess:
Load the node’s certificates from AWS IoT Core
Compute SHA256 hash of the stored
challengeHex-decode the
challenge_responseto get the raw signatureVerify the signature against the hashed challenge using the node’s public certificate
Supports both ECDSA and RSA-PSS signature algorithms
On success: Run the ADD NODE TO GROUP FLOW (node is added immediately, no confirm step needed)
On failure: Delete the association request from DB (burns the challenge to prevent brute-force), return 401
Response (success):
{ "message": "success" }
Flow 2: nocsr_elements (Matter attestation verification)
This flow is only valid for Matter-capable groups. It uses Matter’s NOCSRElements TLV structure for device attestation.
Request:
{ "nocsr_elements": "<hex_encoded_NOCSRElements_TLV>", "attestation_challenge": "<hex_encoded_16_bytes>", "attestation_signature": "<hex_encoded_64_bytes_raw_r_s>" }
All three fields are required.
attestation_challengeandattestation_signaturemust accompanynocsr_elements.NOCSRElements TLV Structure (Matter spec format):
0x15 -- Structure start 0x31 0x01 <2-byte-len> <data> -- Tag 1: CSR (DER-encoded PKCS#10) 0x30 0x02 <1-byte-len> <data> -- Tag 2: CSRNonce (32 bytes) 0x30 0x03 <1-byte-len> <data> -- Tag 3: VendorReserved1 (optional, contains RainMaker node_id) 0x30 0x04 <1-byte-len> <data> -- Tag 4: VendorReserved2 (optional) 0x30 0x05 <1-byte-len> <data> -- Tag 5: VendorReserved3 (optional) 0x18 -- Structure end
Process:
Validate this is a Matter-capable group (reject with 400 if not)
Hex-decode
nocsr_elementsand parse the TLV structureValidate CSRNonce: Compare the
CSRNoncefield from the TLV with the storedchallenge(hex-decoded). They must match exactly.Attestation signature verification (depends on
vendor_reserved1):If
vendor_reserved1is present (RainMaker + Matter node):Extract
node_idfromvendor_reserved1(it’s the RainMaker node_id as a UTF-8 string)Load certificates for this node from AWS IoT Core
If node has registered certificates: Verify the attestation signature
Compute TBS (to-be-signed):
NOCSRElements || AttestationChallengeHash TBS with SHA256
Convert the raw signature (64 bytes: r||s) to DER format
Verify using the node’s ECDSA public key from its DAC certificate
If node has no registered certificates: Treat as pure Matter node (skip signature verification, generate random node_id)
If
vendor_reserved1is absent (Pure Matter node):Skip attestation signature verification
Generate a random
node_id(16 hex characters, 8 bytes)
Convert the extracted CSR (DER) to PEM format
Generate Device NOC:
Load Matter group fabric information:
root_ca,root_ca_keyGenerate
matter_node_idfromnode_id(hex encoding of thingName)Create NOC certificate:
Type: End-entity certificate (IsCA: false)
Public Key: From CSR (ECDSA P-256)
Validity: 10 years (not exceeding Root CA validity)
Key Usage: Digital Signature
Extended Key Usage: Server Auth and Client Auth
Subject: Matter Fabric ID (OID 1.3.6.1.4.1.37244.1.5), Matter Node ID (OID 1.3.6.1.4.1.37244.1.1) - UTF-8 encoded
No CAT ID for device NOCs
Serial Number: Cryptographically secure random
Update association request status to
"verified"in DB (storesnode_idandmatter_node_id; refreshes TTL to 5 more minutes)
Response (success):
{ "message": "success", "noc": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", "matter_node_id": "A1B2C3D4E5F6A1B2" }
Association Confirm API
POST /v1/groups/{groupId}/node-assoc-requests/{requestId}/confirm
This API is called after Matter commissioning is complete. It finalizes the association by adding the node to the group.
Request: No request body required
Process:
Validate
requestIdandgroupIdfrom path parametersRetrieve the stored association request from DynamoDB
Verify
user_idmatches the storeduser_idVerify
group_idfrom path matches the storedgroup_idVerify the request status is
"verified"(reject with 400 if not)Verify
node_idis present in the stored requestRun the ADD NODE TO GROUP FLOW with the stored
node_idandgroup_id
Response (success):
{ "message": "success" }
ADD NODE TO GROUP FLOW
This flow is shared between the challenge_response verify path (called directly) and the confirm API (called after Matter commissioning).
Delete the association request from the database (prevents replay attacks)
Duplicate check: Look up the node’s current group (if any)
TableName:
rmng-group-node-assoc, IndexName:rmng-group-node-assoc-by-node-idQuery by
node_idIf the node is already in the target group → the add is a no-op that returns success, with no side effects (no shadow migration, no iparams clear, no data reset, no Alexa/GVA notification). This is deliberate: a Wi-Fi reconfiguration re-runs association against the group the node is already in, and that must not tear down and rebuild its state.
Handle old group (if node is currently in a group):
Run REMOVE NODE FROM GROUP FLOW for the old group (unlink, delete group shadow, clean iparams user tags, notify)
Trigger async node data reset with
{node_ids: [node_id], old_group_id, group_delete: false}. See Async cleanup for details.
Grant the caller full access to the node for the remainder of this request (
NodeAllin the request context). RBAC is evaluated per request from the user’s group mapping — there is no stored per-node grant, and therefore nothing to revoke later; access ends when the node leaves the group.Add node to group via
ShadowNodeAddToGroup:Verify user has primary access to the target group
TableName:
rmng-user-group-assocQuery:
user_id(PK) +group_id(SK)
Add node to the new group
TableName:
rmng-group-node-assocAttributes:
group_id(PK),node_id(SK)
Notify the node of its new group membership on MQTT topic
rainmaker/nodes/<node_id>/from_cloudwith eventgetGroupInfo:{ "event": ["getGroupInfo"], "getGroupInfo": { "pgrp": "group_id_here", "subgrps": ["subgrp_id_1", "subgrp_id_2", "subgrp_id_3"] } }
Async cleanup (node_data_reset Lambda)
When a node is removed from a group (re-association, disassociation, or group deletion), the old node data must be cleaned up. The caller asynchronously invokes the node_data_reset Lambda.
Input:
{
"node_ids": ["node1", "node2"],
"old_group_id": "grp123",
"group_delete": false
}
Field |
Description |
|---|---|
|
Array of node IDs to clean up (single node for re-association/disassociation, multiple for group deletion) |
|
The group the nodes are being removed from |
|
|
Process:
The Lambda iterates all registered node services and calls Delete(nodeID) on each:
Delete triggers via trigger service — removes
triggersandtriggersVerfromrmng-nodes, notifies deviceDelete schedules via schedule service — removes
schedulesandschedulesVerfromrmng-nodes, notifies deviceDelete timeseries data via timeseries service:
Reads node’s parameter names and data types from
rmng-nodes.configFor each
(key, dt)combination:Construct partition key:
<node_id>.<key>.<dt>TableName:
rmng-raw-ts-data— Query by partition key, batch delete all itemsTableName:
rmng-processed-ts-data— Query by partition key, batch delete all items
Clean up automations (depends on
group_deleteflag):If
group_delete = true(group deletion): Delete ALL automations for the group in one shotTableName:
automations, query byold_group_id, batch delete all
If
group_delete = false(single-node removal): Selectively clean automations referencing the nodeTrigger references: Trigger IDs use format
nodeID~automationID~triggerIndex. If any trigger condition starts withnodeID~, delete the entire automationAction references: If the node appears in an automation’s action targets, remove it. If the node was the sole action target, delete the entire automation
TableName:
automations, query byold_group_id
sequenceDiagram
title Node Data Reset - Async Lambda
participant Caller as "Caller (associate/group/disassoc)"
participant Lambda as "node_data_reset Lambda"
participant Services as "Node Service Registry"
participant AutoDB as "DynamoDB automations"
Caller->>Lambda: Invoke async (Event)<br/>{node_ids[], old_group_id,<br/>group_delete}
activate Lambda
loop For each node_id (in parallel)
Lambda->>Services: triggerService.Delete(nodeID)
Lambda->>Services: scheduleService.Delete(nodeID)
Lambda->>Services: timeseriesService.Delete(nodeID)
end
alt group_delete = true
Lambda->>AutoDB: Query + BatchDelete ALL<br/>automations for old_group_id
else group_delete = false
Lambda->>AutoDB: Query automations by old_group_id
loop For each automation
alt trigger contains nodeID~
Lambda->>AutoDB: Delete automation
else node in action targets
alt sole action target
Lambda->>AutoDB: Delete automation
else other targets remain
Lambda->>AutoDB: Update automation<br/>(remove node from targets)
end
end
end
end
deactivate Lambda
Database Schema: assoc-requests
Attribute |
Type |
Description |
|---|---|---|
|
String (PK) |
32 alphanumeric characters, uniquely identifies the request |
|
String |
Hex (Matter) or alphanumeric (non-Matter) challenge string |
|
String |
Cognito user ID who initiated the request |
|
String |
Target group for the association |
|
Boolean |
Whether the group has Matter capability |
|
String |
|
|
String |
Set after verify in Matter flow |
|
String |
Set after verify in Matter flow |
|
Number |
TTL, set to current time + 5 minutes (refreshed on status update) |
Sequence Diagrams
Initiate API
sequenceDiagram
title Node Association - Internal Flow (Initiate API)
participant Client
participant InitiateAPI as "Association Initiate API"
participant GroupDB as "DynamoDB groups"
participant AssocDB as "DynamoDB assoc-requests"
Client->>InitiateAPI: POST /v1/groups/{groupId}/node-assoc-requests<br/>(No body)
activate InitiateAPI
InitiateAPI->>InitiateAPI: Get user_id from authentication context
InitiateAPI->>InitiateAPI: Generate request_id (32 alphanumeric chars)
InitiateAPI->>GroupDB: Load group by group_id
GroupDB->>InitiateAPI: Return group with capabilities
alt Matter-capable group
InitiateAPI->>InitiateAPI: Generate challenge<br/>(32 bytes crypto random, hex encoded = 64 hex chars)
else Non-Matter group
InitiateAPI->>InitiateAPI: Generate challenge<br/>(64 alphanumeric chars)
end
InitiateAPI->>InitiateAPI: Calculate expiration_time<br/>(current time + 5 minutes TTL)
InitiateAPI->>AssocDB: Store request<br/>{request_id (PK), challenge, user_id,<br/>group_id, is_matter_group, status: "pending",<br/>expiration_time}
activate AssocDB
AssocDB->>InitiateAPI: Confirmation
deactivate AssocDB
InitiateAPI->>Client: Response:<br/>{request_id, challenge}
deactivate InitiateAPI
Verify API (challenge_response flow)
sequenceDiagram
title Node Association - Verify API (challenge_response flow)
participant Client
participant VerifyAPI as "Association Verify API"
participant AssocDB as "DynamoDB assoc-requests"
participant IoTCore as "AWS IoT Core"
Client->>VerifyAPI: POST /v1/groups/{groupId}/node-assoc-requests/{requestId}/verify<br/>{challenge_response, node_id}
activate VerifyAPI
VerifyAPI->>AssocDB: Get request by request_id
AssocDB->>VerifyAPI: Return stored request
VerifyAPI->>VerifyAPI: Verify user_id matches<br/>Verify group_id matches
VerifyAPI->>IoTCore: Load node certificates
IoTCore->>VerifyAPI: Return certificates
VerifyAPI->>VerifyAPI: SHA256 hash the stored challenge
VerifyAPI->>VerifyAPI: Hex decode challenge_response
VerifyAPI->>VerifyAPI: Verify signature using node's public key
alt Verification succeeds
VerifyAPI->>VerifyAPI: ADD NODE TO GROUP FLOW<br/>(deletes request, adds node to group)
VerifyAPI->>Client: {message: "success"}
else Verification fails
VerifyAPI->>AssocDB: Delete request (burn challenge)
VerifyAPI->>Client: 401 Invalid challenge response
end
deactivate VerifyAPI
Verify API (nocsr_elements flow)
sequenceDiagram
title Node Association - Verify API (nocsr_elements / Matter attestation flow)
participant Client
participant VerifyAPI as "Association Verify API"
participant AssocDB as "DynamoDB assoc-requests"
participant IoTCore as "AWS IoT Core"
participant GroupDB as "DynamoDB groups"
Client->>VerifyAPI: POST /v1/groups/{groupId}/node-assoc-requests/{requestId}/verify<br/>{nocsr_elements, attestation_challenge, attestation_signature}
activate VerifyAPI
VerifyAPI->>AssocDB: Get request by request_id
AssocDB->>VerifyAPI: Return stored request
VerifyAPI->>VerifyAPI: Verify user_id matches<br/>Verify group_id matches<br/>Verify is_matter_group = true
VerifyAPI->>VerifyAPI: Hex decode nocsr_elements<br/>Parse NOCSRElements TLV<br/>(extract CSR, CSRNonce, vendor_reserved1)
VerifyAPI->>VerifyAPI: Verify CSRNonce == stored challenge (hex decoded)
alt vendor_reserved1 present (RainMaker + Matter)
VerifyAPI->>VerifyAPI: Extract node_id from vendor_reserved1
VerifyAPI->>IoTCore: Load certificates for node_id
IoTCore->>VerifyAPI: Return certificates
alt Node has registered certificates
VerifyAPI->>VerifyAPI: Compute TBS = NOCSRElements || AttestationChallenge
VerifyAPI->>VerifyAPI: SHA256 hash TBS
VerifyAPI->>VerifyAPI: Convert raw signature (r||s) to DER
VerifyAPI->>VerifyAPI: Verify ECDSA signature with node's DAC public key
else No certificates found
VerifyAPI->>VerifyAPI: Treat as pure Matter node<br/>Generate random node_id (16 hex chars)
end
else No vendor_reserved1 (Pure Matter)
VerifyAPI->>VerifyAPI: Generate random node_id (16 hex chars, 8 bytes)
end
VerifyAPI->>VerifyAPI: Convert CSR from DER to PEM
VerifyAPI->>GroupDB: Load Matter group fabric info
GroupDB->>VerifyAPI: Return root_ca, root_ca_key
VerifyAPI->>VerifyAPI: Generate Device NOC<br/>(no CAT ID, 10-year validity)
VerifyAPI->>AssocDB: Update request status to "verified"<br/>Store node_id and matter_node_id<br/>Refresh TTL
VerifyAPI->>Client: {message: "success", noc, matter_node_id}
deactivate VerifyAPI
Confirm API
sequenceDiagram
title Node Association - Internal Flow (Confirm API)
participant Client
participant ConfirmAPI as "Association Confirm API"
participant AssocDB as "DynamoDB assoc-requests"
Client->>ConfirmAPI: POST /v1/groups/{groupId}/node-assoc-requests/{requestId}/confirm<br/>(No body)
activate ConfirmAPI
ConfirmAPI->>AssocDB: Get request by request_id
AssocDB->>ConfirmAPI: Return request<br/>{status: "verified", node_id, ...}
ConfirmAPI->>ConfirmAPI: Verify user_id matches<br/>Verify group_id matches<br/>Verify status is "verified"<br/>Verify node_id is present
ConfirmAPI->>ConfirmAPI: ADD NODE TO GROUP FLOW<br/>(deletes request, adds node to group)
ConfirmAPI->>Client: {message: "success"}
deactivate ConfirmAPI
Node disassociation
What is node disassociation
Node disassociation (= remove node from group) is the process of unlinking a node from a group. The node becomes orphaned and can later be re-associated to any group by a different user. After unlinking, a node_data_reset Lambda is invoked asynchronously to delete the node’s triggers, schedules, timeseries data, and automations referencing the node.
Disassociate/Remove Node from Group API
DELETE /v1/groups/{groupId}/nodes/{nodeId}
Access Control
Requires primary access to the group
Request
No request body required.
Process
Validate
groupIdandnodeIdfrom path parametersGet user-group mapping and verify access
TableName:
rmng-user-group-assocQuery by
user_id(PK) andgroup_id(SK)Verify
access_typeis"primary"
Verify node exists in the group
TableName:
rmng-group-node-assocGetItem by
group_id(PK) andnode_id(SK)
Run REMOVE NODE FROM GROUP FLOW
Invoke
node_data_resetLambda async with{node_ids: [node_id], old_group_id: group_id, group_delete: false}to delete triggers, schedules, timeseries, and automations. See Async cleanup for details.
Matter node limitation: Disassociation unlinks the node and cleans up its data for all node types, but the backend does not revoke the node’s Matter operational certificate (NOC) or remove it from the group’s Matter fabric. A disassociated Matter node therefore keeps a valid NOC on the fabric.
See the Data Cleanup Reference for the full comparison.
Response
{
"message": "success"
}
Sequence Diagram
sequenceDiagram
title Node Disassociation - Internal Flow
participant Client
participant DisassocAPI as "Disassociate API"
participant UserGroupDB as "DynamoDB rmng-user-group-assoc"
participant GroupNodeDB as "DynamoDB rmng-group-node-assoc"
participant IoTCore as "AWS IoT Core"
participant MQTT as "MQTT"
participant ResetLambda as "node_data_reset Lambda"
Client->>DisassocAPI: DELETE /v1/groups/{groupId}/nodes/{nodeId}
activate DisassocAPI
DisassocAPI->>UserGroupDB: Query (user_id, group_id)
UserGroupDB->>DisassocAPI: Return mapping
DisassocAPI->>DisassocAPI: Verify primary access
DisassocAPI->>GroupNodeDB: GetItem (group_id, node_id)
GroupNodeDB->>DisassocAPI: Return mapping
DisassocAPI->>DisassocAPI: Verify node exists in group
Note over DisassocAPI: REMOVE NODE FROM GROUP FLOW
DisassocAPI->>GroupNodeDB: DeleteItem (group_id, node_id)
DisassocAPI->>IoTCore: Delete group shadow<br/>(params-groupId-...)
DisassocAPI->>IoTCore: Clear user tags from iparams
DisassocAPI->>IoTCore: Clear group_id thing attribute
DisassocAPI->>MQTT: Publish to<br/>rainmaker/nodes/{nodeId}/from_cloud<br/>{event: ["getGroupInfo"],<br/>getGroupInfo: {pgrp: "", subgrps: []}}
DisassocAPI->>ResetLambda: Invoke async<br/>{node_ids: [node_id], old_group_id,<br/>group_delete: false}
DisassocAPI->>Client: {message: "success"}
deactivate DisassocAPI
REMOVE NODE FROM GROUP FLOW
This flow is shared between the disassociate API, the delete group API, and association. It unlinks a single node from a group. The caller is responsible for invoking the node_data_reset Lambda afterwards to clean up node data (triggers, schedules, timeseries, automations).
Input: group_id, node_id
Delete group-device mapping
TableName:
rmng-group-node-assocDeleteItem by
group_id(PK) andnode_id(SK)
Delete group IoT shadow
The group shadow
params-<group_id>[-subgroups...]is deletedThe node will stop receiving param updates for this group
Clean
iparamsClear user tags from
iparamsIoT shadowOther iparams data remains
Clear
group_idIoT thing attributeSet the
group_idattribute on the IoT thing to empty stringPrevents the device from subscribing to group control topics
Notify node via MQTT
Topic:
rainmaker/nodes/<node_id>/from_cloudMessage:
{ "event": ["getGroupInfo"], "getGroupInfo": { "pgrp": "", "subgrps": [] } }
Data Cleanup Reference
The following table shows what data is affected by each operation. “Remove Node from Subgroup” and “Delete Subgroup” are defined in the group spec.
This table covers node data only. Table-level changes (rmng-group-node-assoc, groups, rmng-user-group-assoc) are documented in the respective API sections.
Legend: Sync = done in the API request. Async = done via node_data_reset Lambda.
Data / Resource |
Subgroup ops (remove node / delete subgroup) |
Disassoc / Delete Group / Assoc (new group) |
Unshare User |
|---|---|---|---|
Group IoT Shadow ( |
Rename (updated subgrps) |
Delete |
No change |
|
No change |
Delete user tags |
No change |
IoT Thing |
No change |
Delete |
No change |
|
No change |
Delete (async) |
No change |
|
No change |
Delete all for group (async)/Delete automations whose triggers reference node (async) |
No change |
Timeseries ( |
No change |
Delete (async) |
No change |
rmng-nodes.configis never deleted — it is intrinsic to the node and re-reported on boot.
FAQs
What happens if a node has more than 1 certificate?
The verification code iterates through all certificates registered for a node in AWS IoT Core. It succeeds if any one certificate’s public key can verify the signature.
Could be useful for certificate rotation.
Supported signature algorithms:
For challenge_response flow: RSA-PSS, ECDSA (ASN.1 DER format)
For nocsr_elements flow: ECDSA P-256 only (raw r||s format, 64 bytes)
If I have primary access to a group, can I associate the node to that group even if I am not the owner of the group?
Yes. Users with primary access can associate nodes to the group, regardless of whether they are the original owner of the group.
If I have secondary access to a group, can I associate the node to that group even if I am not the owner of the group?
No. Only users with primary access can associate nodes to a group. Users with secondary access do not have permission to associate nodes.
Can I associate the node into a subgroup?
No. Association targets a group; subgroup membership is assigned afterwards with the update-subgroup API. A group is the base context a node exists in, and subgroups are a scoping layer on top of it.
What happens if I use
challenge_responseon a Matter-capable group?It works. The node is verified and added to the group directly without NOC generation. This is useful for RainMaker nodes that are being added to a Matter-capable group but don’t need a Matter NOC.
What if the attestation signature verification fails for a node with vendor_reserved1 but no registered certificates?
The node is treated as a pure Matter node. A random node_id is generated and signature verification is skipped.
Can a node be in multiple groups?
No. A node can only be in one group at a time. During association, the node is automatically removed from its old group (if any) and added to the new group.