Skip to content

Conversation

vijs
Copy link
Collaborator

@vijs vijs commented Oct 20, 2025

Summary

Implement Joint Fabric nodesync feature in jfa-admin-app

Related issues

Addresses Issue: #40592.

Testing

Verified using the below instructions.

$ gn gen --check out/host/ --args='chip_device_config_enable_joint_fabric=true'
$ ninja -C out/host/ src/lib/dnssd/tests:tests_run

@github-actions
Copy link

PR #41535: Size comparison from fdd77ec to 079e297

Full report (1 build for stm32)
platform target config section fdd77ec 079e297 change % change
stm32 light STM32WB5MM-DK FLASH 469956 469956 0 0.0
RAM 141248 141248 0 0.0

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the Joint Fabric nodesync feature, which allows a Joint Fabric Administrator to synchronize datastore entries (like Group IDs, Bindings, and ACLs) with other nodes on the fabric. This is achieved by introducing a JFADatastoreSync class that acts as a delegate for the JointFabricDatastore. When entries are added or removed from the datastore, the delegate is called to perform the synchronization with the target node over a CASE session. The implementation uses an asynchronous pattern with callbacks to handle device communication. The changes are well-structured, but I have a few suggestions to improve code clarity and consistency.

Comment on lines 50 to 51
class DevicePairedCommand : public Controller::DevicePairingDelegate
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The DevicePairedCommand class inherits from Controller::DevicePairingDelegate, but it doesn't seem to function as a pairing delegate. Its purpose is to execute actions on an already connected device, not to handle pairing events. This inheritance is unnecessary and could be misleading. Consider removing the inheritance from Controller::DevicePairingDelegate.

class DevicePairedCommand
{

{
ChipLogProgress(DeviceLayer, "OnDeviceConnectionFailureFn - Not syncing device with node id: " ChipLogFormatX64,
ChipLogValueX64(cbContext->nodeId));
// TODO: Remove Node Id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This TODO comment suggests that some cleanup logic is missing for when a device connection fails. Please clarify what "Remove Node Id" means in this context and implement the intended failure handling. For example, should the node be removed from a list of active nodes, or should there be a retry mechanism?

it->statusEntry.state = Clusters::JointFabricDatastore::DatastoreStateEnum::kDeletePending;

// TODO: Update device
Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type endpointGroupIdNullEntry{ 0 };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using a zero-initialized struct ({ 0 }) to signal a deletion to the delegate is an implicit convention. While it works, it can be hard to understand for future maintainers. Consider adding a comment to explain that a zero-initialized struct is used to signify a deletion for the SyncNode delegate method.

Comment on lines 127 to 133
virtual CHIP_ERROR
SyncNode(NodeId nodeId,
Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type & endpointGroupIDEntry,
std::function<void()> onComplete)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The SyncNode overload for DatastoreEndpointGroupIDEntryStruct takes its struct parameter by a non-const reference, while the other SyncNode overloads take const references. This is inconsistent. Since the endpointGroupIDEntry object is not modified by the caller, it should be passed by const reference to maintain consistency across the delegate interface.

This change will also need to be applied to the implementing classes:

  • examples/jf-admin-app/linux/include/JFADatastoreSync.h
  • examples/jf-admin-app/linux/JFADatastoreSync.cpp
        virtual CHIP_ERROR
        SyncNode(NodeId nodeId,
                 const Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type & endpointGroupIDEntry,
                 std::function<void()> onComplete)
        {
            return CHIP_ERROR_NOT_IMPLEMENTED;
        }

@github-actions
Copy link

github-actions bot commented Oct 20, 2025

PR #41535: Size comparison from c1f377d to 351f096

Full report (37 builds for bl602, bl702, bl702l, cc13x4_26x4, cc32xx, efr32, esp32, nrfconnect, nxp, psoc6, qpg, realtek, stm32, telink)
platform target config section c1f377d 351f096 change % change
bl602 lighting-app bl602+mfd+littlefs+rpc FLASH 1106918 1106918 0 0.0
RAM 178882 178882 0 0.0
bl702 lighting-app bl702+eth FLASH 661238 661238 0 0.0
RAM 134969 134969 0 0.0
bl702+wifi FLASH 837350 837350 0 0.0
RAM 124421 124421 0 0.0
bl706+mfd+rpc+littlefs FLASH 1070318 1070318 0 0.0
RAM 117261 117261 0 0.0
bl702l contact-sensor-app bl702l+mfd+littlefs FLASH 899416 899416 0 0.0
RAM 105540 105540 0 0.0
lighting-app bl702l+mfd+littlefs FLASH 983336 983336 0 0.0
RAM 109748 109748 0 0.0
cc13x4_26x4 lighting-app LP_EM_CC1354P10_6 FLASH 770804 770804 0 0.0
RAM 103312 103312 0 0.0
lock-ftd LP_EM_CC1354P10_6 FLASH 782560 782560 0 0.0
RAM 108472 108472 0 0.0
pump-app LP_EM_CC1354P10_6 FLASH 728372 728372 0 0.0
RAM 97380 97380 0 0.0
pump-controller-app LP_EM_CC1354P10_6 FLASH 712840 712840 0 0.0
RAM 97580 97580 0 0.0
cc32xx air-purifier CC3235SF_LAUNCHXL FLASH 554514 554514 0 0.0
RAM 205752 205752 0 0.0
lock CC3235SF_LAUNCHXL FLASH 587754 587754 0 0.0
RAM 205840 205840 0 0.0
efr32 lock-app BRD4187C FLASH 963328 963328 0 0.0
RAM 126332 126332 0 0.0
BRD4338a FLASH 757032 757024 -8 -0.0
RAM 256960 256960 0 0.0
window-app BRD4187C FLASH 1058772 1058772 0 0.0
RAM 122560 122560 0 0.0
esp32 all-clusters-app c3devkit DRAM 103440 103440 0 0.0
FLASH 1796188 1796188 0 0.0
IRAM 83862 83862 0 0.0
nrfconnect all-clusters-app nrf52840dk_nrf52840 FLASH 933232 933232 0 0.0
RAM 161317 161317 0 0.0
nxp contact mcxw71+release FLASH 692208 692208 0 0.0
RAM 61496 61496 0 0.0
lighting mcxw71+release FLASH 723720 723720 0 0.0
RAM 68156 68156 0 0.0
lock mcxw71+release FLASH 773976 773976 0 0.0
RAM 61940 61940 0 0.0
psoc6 all-clusters cy8ckit_062s2_43012 FLASH 1676940 1676940 0 0.0
RAM 213908 213908 0 0.0
all-clusters-minimal cy8ckit_062s2_43012 FLASH 1593556 1593556 0 0.0
RAM 211116 211116 0 0.0
light cy8ckit_062s2_43012 FLASH 1460100 1460100 0 0.0
RAM 197728 197728 0 0.0
lock cy8ckit_062s2_43012 FLASH 1492652 1492652 0 0.0
RAM 225448 225448 0 0.0
qpg lighting-app qpg6200+debug FLASH 837272 837272 0 0.0
RAM 127716 127716 0 0.0
lock-app qpg6200+debug FLASH 774052 774052 0 0.0
RAM 118692 118692 0 0.0
realtek light-switch-app rtl8777g FLASH 706760 706760 0 0.0
RAM 106912 106912 0 0.0
lighting-app rtl8777g FLASH 757856 757856 0 0.0
RAM 127236 127236 0 0.0
stm32 light STM32WB5MM-DK FLASH 470252 470252 0 0.0
RAM 141320 141320 0 0.0
telink bridge-app tl7218x FLASH 710662 710662 0 0.0
RAM 90552 90552 0 0.0
light-app-ota-compress-lzma-shell-factory-data tl3218x FLASH 797040 797040 0 0.0
RAM 41008 41008 0 0.0
light-app-ota-shell-factory-data tl7218x FLASH 788240 788240 0 0.0
RAM 93652 93652 0 0.0
light-switch-app-ota-compress-lzma-factory-data tl7218x_retention FLASH 715126 715126 0 0.0
RAM 51852 51852 0 0.0
light-switch-app-ota-compress-lzma-shell-factory-data tlsr9528a FLASH 748430 748430 0 0.0
RAM 70900 70900 0 0.0
light-switch-app-ota-factory-data tl3218x_retention FLASH 725278 725278 0 0.0
RAM 34600 34600 0 0.0
lighting-app-ota-factory-data tlsr9118bdk40d FLASH 602556 602556 0 0.0
RAM 108700 108700 0 0.0
lighting-app-ota-rpc-factory-data-4mb tlsr9518adk80d FLASH 820860 820864 4 0.0
RAM 92048 92048 0 0.0

@codecov
Copy link

codecov bot commented Oct 20, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 51.06%. Comparing base (c1f377d) to head (351f096).

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #41535      +/-   ##
==========================================
+ Coverage   51.04%   51.06%   +0.02%     
==========================================
  Files        1386     1385       -1     
  Lines      100958   100901      -57     
  Branches    13061    13056       -5     
==========================================
- Hits        51534    51530       -4     
+ Misses      49424    49371      -53     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link

PR #41535: Size comparison from cde4477 to 4fda0bc

Full report (4 builds for cc32xx, realtek)
platform target config section cde4477 4fda0bc change % change
cc32xx air-purifier CC3235SF_LAUNCHXL FLASH 554330 554330 0 0.0
RAM 205736 205736 0 0.0
lock CC3235SF_LAUNCHXL FLASH 587578 587578 0 0.0
RAM 205832 205832 0 0.0
realtek light-switch-app rtl8777g FLASH 706584 706584 0 0.0
RAM 106904 106904 0 0.0
lighting-app rtl8777g FLASH 757672 757672 0 0.0
RAM 127236 127236 0 0.0

Comment on lines +86 to +90
if constexpr (std::is_same_v<T,
app::Clusters::JointFabricDatastore::Structs::DatastoreEndpointGroupIDEntryStruct::Type>)
{
attributeId = Attributes::EndpointGroupIDList::Id;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sync is being done with a regular Matter device (not another Datastore) and so in the case of a DatastoreEndpointGroupIDEntryStruct, the operation to send is:

IM Type: Command
EndpointId=newGroupEntry.endpointID
ClusterId=0x0004 (groups)
CommandId=0x00 (add group)
Arg0=newGroupEntry.groupID
Arg1=GroupName

Comment on lines +91 to +95
else if constexpr (std::is_same_v<T,
app::Clusters::JointFabricDatastore::Structs::DatastoreNodeKeySetEntryStruct::Type>)
{
attributeId = Attributes::NodeKeySetList::Id;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of DatastoreNodeKeySetEntryStruct, the operation to send is:

IM Type: Command
EndpointId=0 (root endpoint)
ClusterId=0x003F (group key management)
CommandId=0x00 (key set write)
Arg0=groupKeySet

Comment on lines +98 to +100
{
attributeId = Attributes::EndpointBindingList::Id;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of DatastoreEndpointBindingEntryStruct, the operation to send is:

IM Type: AttributeWrite
EndpointId=0 (root endpoint)
ClusterId=0x001E (bindings cluster)
AttributeId=0x00 (binding)
Arg0=list of all bindings for this node from datastore

Comment on lines +102 to +104
{
attributeId = Attributes::NodeACLList::Id;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of DatastoreACLEntryStruct, the operation to send is:

IM Type: AttributeWrite
EndpointId=0 (root endpoint)
ClusterId=0x001F (ACL cluster)
AttributeId=0x00 (ACL)
Arg0=list of all ACLs for this node from datastore

Comment on lines +111 to +112
CHIP_ERROR err = cluster.WriteAttribute(cbContext->objectToWrite, pairingCommand, Clusters::JointFabricDatastore::Id,
attributeId, OnWriteSuccessResponse, OnWriteFailureResponse, NullOptional);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be moved above since some are using WriteAttribute while others will use SendCommand

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants