docs(export): PRD research pass + bridge protocol spec - #118
Conversation
Fold the 2026-08-04 research findings into the export PRD and add BRIDGE_PROTOCOL.md, the plugin<->bridge-node local protocol contract both the TypeScript node and the Python client will be built against. PRD changes: - Status: Accepted, build in progress; decisions of 2026-08-04 recorded - Endpoint identity: matter.js keys persisted endpoint numbers on the string Endpoint.id (verified at 0.17.8) -> id derives from the Indigo device ID; drift detector + storage-loss refuse-to-start required - Matter side binds UDP 5540 with the Aggregator at EP1 (Alexa constraint, cheap now, painful to retrofit); no conflict with matter-server (TCP 5580) - Valve and Fan roles descoped to v2: their matter.js clusters are stubs and ecosystem support is poor - Pressure/Flow sensors kept, Apple-ignores caveat documented - Pairing UX: manual code via event log, QR via IWS page; passcode and discriminator randomised per install - XOQ3 answered (AgentSpec extraction, pulled forward to before E1), XOQ4 answered (matter.js generates a self-signed chain for the configured VID; examples' 0xFFF1/0x8000), XOQ6 answered (~145MB floor, ~0.3MB/endpoint, measured) - Bridge node ships as an exact-pinned published npm package (indigo-matter-bridge); matter.js itself exact-pinned Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JxqGhP3DcENf68AZK21U4S
|
Warning Review limit reached
Next review available in: 49 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe PR adds a v1 WebSocket protocol specification for the Indigo Matter bridge. It updates the export PRD with implementation and validation decisions, and increments the plugin version to ChangesBridge protocol and export plan
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/BRIDGE_PROTOCOL.md`:
- Around line 25-28: Update the get_status and get_pairing command documentation
to distinguish inner result payloads from complete response frames. Either show
each command’s full success envelope including message_id and result, or
explicitly label the documented Result values as inner payloads, while keeping
the envelope definition consistent.
- Around line 55-59: Update the pluginVersion value in the attach example to
match the released PluginVersion 2026.7.22, or explicitly mark the value as a
placeholder if the example is intended to remain version-agnostic.
- Around line 197-204: Update the command event schema in the protocol
documentation so args is explicitly optional for commands without arguments, or
update the lock example to include an empty args object. Also document the valid
command names and corresponding argument shapes for each role, keeping the
schema consistent with set_state keys.
- Around line 212-213: Update the loopback WebSocket protocol documentation to
address unauthenticated access to privileged commands and pairing data: either
specify per-installation peer authentication with message integrity, or
explicitly define and enforce a trusted local-client boundary before exposing
device control. Revise the “Loopback only” security statement to reflect the
selected protection and threat model.
- Around line 41-46: Define the fail-closed handshake path for protocol mismatch
in the attach section by specifying which peer (plugin or node) must reject when
protocolVersion differs, the exact Indigo error frame and error code that must
be sent, and the ordering constraint that the rejecting peer must send the error
response before closing the socket. Also clarify the reconnect behavior after
rejection to prevent race conditions where the socket closes before the error
frame is processed. Apply the same clarifications to section 7 invariants at
lines 221-222 to ensure consistency across both locations.
In `@docs/PRD-indigo-matter-export.md`:
- Around line 116-119: Update the Drift detection requirement in the PRD so
changes to an existing UniqueID → endpoint number mapping are treated as fatal:
refuse startup and require explicit migration or repair. Permit automatic
startup only when the persisted map gains additive mappings, while retaining the
existing warning and checkEndpointNumbers() context.
- Around line 120-125: Update the storage-loss requirements in the PRD to define
an authoritative durable marker for prior commissioning, stored independently of
the bridge-node directory (such as plugin/Indigo storage or a backup manifest),
and specify how it is restored. Clarify that startup must refuse when this
marker exists but the bridge directory is missing, while allowing initialization
when neither exists, and add a test covering bridge-directory deletion after
commissioning.
- Around line 259-262: Update the endpoint-capacity guidance in the PRD to
define explicit per-ecosystem limits and a memory threshold using the stated RSS
baseline and per-endpoint growth. Set warnings and refusal behavior before the
Alexa limit and before projected memory usage exceeds the 200 MB budget,
replacing the vague “~100 exports” warning while preserving the documented Apple
and Alexa constraints.
- Line 5: Update the “Governing ADR” link in PRD-indigo-matter-export.md to use
the repository-relative target adr/0006-indigo-as-matter-bridge.md, preserving
the existing ADR reference text.
- Around line 141-148: Update the Alexa topology statement in the PRD to include
a reproducible or official reference supporting the UDP 5540 and Aggregator
Endpoint 1 requirements. If no suitable reference exists, explicitly label these
constraints as unverified assumptions used for bridge design while preserving
the existing topology details.
- Around line 239-244: Update the Distribution section of the PRD to remove the
unsupported published indigo-matter-bridge npm-package claim and describe the
bridge artifact actually installed by server_process.py, including its existing
matter-server@1.2.2 distribution details. Do not assert exact pinning, lockfile,
or integrity metadata for a bridge package unless those artifacts are added to
the installer.
- Around line 255-258: Update the Echo guard section to remove reliance on
ctx.offline as the origin marker. Document the concrete event-context contract
if one exists; otherwise specify a command correlation or origin field that
bridge-node echo suppression uses to ignore only matching Indigo-originated
writes while allowing legitimate controller updates.
- Around line 286-289: The PRD’s IWS-served QR page must define protections for
the embedded Matter setup code. Update the QR response requirements to restrict
access to authorized or loopback clients, display the credential only briefly or
invalidate it after one use, set Cache-Control: no-store, and prevent the setup
code from appearing in logs or referrer data.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 23bc427f-af78-4bd3-972a-8810ee6fa1d0
📒 Files selected for processing (3)
docs/BRIDGE_PROTOCOL.mddocs/PRD-indigo-matter-export.mdindigo-matter.indigoPlugin/Contents/Info.plist
| {"command": "attach", "args": { | ||
| "protocolVersion": 1, | ||
| "pluginVersion": "2026.8.1", | ||
| "endpoints": [ <EndpointSpec>, … ] | ||
| }} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Align the attach example with the released plugin version.
The PR objective bumps PluginVersion to 2026.7.22, but this example sends 2026.8.1. Update the example or mark it as a placeholder.
Suggested correction
- "pluginVersion": "2026.8.1",
+ "pluginVersion": "2026.7.22",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {"command": "attach", "args": { | |
| "protocolVersion": 1, | |
| "pluginVersion": "2026.8.1", | |
| "endpoints": [ <EndpointSpec>, … ] | |
| }} | |
| {"command": "attach", "args": { | |
| "protocolVersion": 1, | |
| "pluginVersion": "2026.7.22", | |
| "endpoints": [ <EndpointSpec>, … ] | |
| }} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/BRIDGE_PROTOCOL.md` around lines 55 - 59, Update the pluginVersion value
in the attach example to match the released PluginVersion 2026.7.22, or
explicitly mark the value as a placeholder if the example is intended to remain
version-agnostic.
| - **Distribution:** the bridge node is a **published npm package** | ||
| (`indigo-matter-bridge`, TypeScript, decided 2026-08-04), exact-pinned by the | ||
| plugin the same way `matter-server@1.2.2` is — the existing | ||
| `npm install --prefix` machinery works unchanged. matter.js itself is | ||
| **exact-pinned** (no caret): patch releases have changed what Apple Home | ||
| renders with zero code change on the bridge side. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 8 'indigo-matter-bridge|`@matter/`(main|nodejs)|npm install|package-lock|npm-shrinkwrap' \
--glob '*.py' --glob '*.json' --glob '*.lock' --glob '*.md' .Repository: simons-plugins/indigo-matter
Length of output: 17606
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== changed/staged files =="
git status --short
echo "== server_process.py relevant sections =="
wc -l "indigo-matter.indigoPlugin/Contents/Server Plugin/server_process.py"
sed -n '34,70p' "indigo-matter.indigoPlugin/Contents/Server Plugin/server_process.py"
sed -n '150,360p' "indigo-matter.indigoPlugin/Contents/Server Plugin/server_process.py"
sed -n '730,770p' "indigo-matter.indigoPlugin/Contents/Server Plugin/server_process.py"
echo "== tests around install/default spec =="
rg -n -C 10 'DEFAULT_INSTALL_SPEC|matter-server@1\.2\.2|npm install|package-lock|remove_package|install\(' tests/indigo-matter_tests.py tests || true
echo "== repository package metadata/locks (if present) =="
git ls-files | rg '(^|/)(package-lock\.json|package\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?)$' || trueRepository: simons-plugins/indigo-matter
Length of output: 34297
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path("indigo-matter.indigoPlugin/Contents/Server Plugin/server_process.py")
text = p.read_text()
for needle in [
"indigo-matter-bridge",
"DEFAULT_INSTALL_SPEC",
"package-lock.json",
"npm install",
"remove_package",
"remove_package",
]:
print(f"{needle!r}: {needle in text}")
if "DEFAULT_INSTALL_SPEC" in text:
idx = text.index("DEFAULT_INSTALL_SPEC")
for i, line in enumerate(text.splitlines(), 1):
if str(i) == str(text[:idx].count("\n")+1):
print(f"DEFAULT_INSTALL_SPEC at line {i}: {line}")
print("package-lock references:", text.count("package-lock.json"))
print("npm install references:")
for i, line in enumerate(text.splitlines(), 1):
if "npm install" in line:
print(f"line {i}: {line}")
print("remove_package lines:")
start = text.index(" def remove_package")
lines=text.splitlines()
for idx,line in enumerate(text[start:].splitlines(),1):
print(f"{idx}: {line}")
if idx>80:
break
PYRepository: simons-plugins/indigo-matter
Length of output: 5482
Replace the bridge npm-package claim with the actual bridge distribution artifact.
The PRD says the plugin installs a published indigo-matter-bridge npm package, but server_process.py still uses matter-server@1.2.2; there is no bridge package version or lockfile/integrity source in the installer. Update this doc to describe the actual bridge artifact, or ship the actual npm pin and lockfile.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/PRD-indigo-matter-export.md` around lines 239 - 244, Update the
Distribution section of the PRD to remove the unsupported published
indigo-matter-bridge npm-package claim and describe the bridge artifact actually
installed by server_process.py, including its existing matter-server@1.2.2
distribution details. Do not assert exact pinning, lockfile, or integrity
metadata for a bridge package unless those artifacts are added to the installer.
| - **Echo guard:** matter.js attribute-change events fire for our own | ||
| Indigo-originated writes as well as controller commands; the bridge node | ||
| discriminates on the event context (`ctx.offline`) or the Indigo↔ecosystem | ||
| loop is infinite. |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
What does ctx.offlinemean in@matter/main 0.17.8 attribute-change callbacks, and can it distinguish application-originated writes from controller-originated writes?
💡 Result:
In @matter/main, ctx.offline was a field within ActionContext used to distinguish between local operations and remote requests [1]. However, this field has been deprecated and removed [1]. In modern versions of matter.js (including 0.17.8), the framework uses a more robust approach to distinguish the origin of operations [1]. The ActionContext class has been refactored to explicitly separate fields for local versus remote interactions [1]. Instead of relying on an offline flag, you should use the following mechanisms provided by the framework to determine the actor type: 1. Type Guards: You can determine the actor type by using the hasRemoteActor or hasLocalActor type guards on the context [1]. 2. Type Assertions: You can use assertRemoteActor to verify the context, which helps in identifying if a write originated from a remote authenticated peer rather than a local application-originated action [1]. These mechanisms allow you to distinguish between writes triggered by local application logic (e.g., internal state changes) and those triggered by external controller requests (e.g., via a remote command) [1]. If you are currently relying on ctx.offline, you must update your code to use these new type guards, as the offline field is no longer supported [1].
Citations:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Repository files around PRD:"
fd -a 'PRD-indigo-matter-export\.md$' . || true
echo
echo "Context in PRD around cited lines:"
if [ -f docs/PRD-indigo-matter-export.md ]; then
sed -n '230,275p' docs/PRD-indigo-matter-export.md | nl -ba -v230
fi
echo
echo "Search for ctx.offline / indirect echo markers in repository:"
rg -n "ctx\.offline|offline|origin|hasLocalActor|hasRemoteActor|action context|ActionContext|attribute-change|onAttributeChanged" . || true
echo
echo "Search Matter dependency/version clues:"
rg -n "matter\.js|`@matter`|matter-js|matter-matterjs|0\.17|3\.3|deps|dependencies|package-lock|bun\.lock|pnpm-lock|yarn\.lock" -g '!node_modules' -g '!dist' -g '!build' . || trueRepository: simons-plugins/indigo-matter
Length of output: 324
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Context in PRD around cited lines:"
sed -n '230,275p' docs/PRD-indigo-matter-export.md
echo
echo "Search for ctx.offline / indirect echo markers in repository:"
rg -n "ctx\.offline|offline|origin|hasLocalActor|hasRemoteActor|assertRemoteActor|action context|ActionContext|attribute-change|onAttributeChanged" docs || true
echo
echo "Search Matter dependency/version clues:"
rg -n "matter\.js|`@matter`|matter-js|0\.17|3\.3|deps|dependencies|package-lock|bun\.lock|pnpm-lock|yarn\.lock" -g '!node_modules' -g '!dist' -g '!build' . || trueRepository: simons-plugins/indigo-matter
Length of output: 19199
🌐 Web query:
matter.js 0.17.8 ActionContext offline hasLocalActor hasRemoteActor attribute changed
💡 Result:
In matter.js, the ActionContext class underwent a significant refactor to better delineate between operations triggered locally and those triggered by authenticated peers [1][2]. As part of this change, the previously used offline field in ActionContext was removed and replaced [1][2]. To handle the distinction between local and remote actors, the library introduced new type guards and a type assertion, which should be used instead of checking the offline property [1][2]: hasLocalActor: A type guard used to determine if the context involves a local actor [1][2]. hasRemoteActor: A type guard used to determine if the context involves a remote actor [1][2]. assertRemoteActor: A new type assertion used when you need to ensure the context is a remote actor context [1][2]. These changes reflect the refactoring of ActionContext into either a RemoteActorContext or a LocalActorContext [1][2]. If you were previously checking if an action was offline, you should now verify the actor type using these new mechanisms [1][2]. This update was implemented to provide a more robust way to handle context-specific logic, such as in AccessControlServer where checks like if (!hasRemoteActor(context)) are now standard [3].
Citations:
- 1: https://github.com/matter-js/matter.js/blob/main/CHANGELOG.md
- 2: https://github.com/project-chip/matter.js/blob/main/CHANGELOG.md
- 3: https://cdn.jsdelivr.net/npm/@matter/node@0.16.10/src/behaviors/access-control/AccessControlServer.ts
Use an explicit origin marker for echo suppression.
ctx.offline is not defined by the pinned tooling as a write-origin marker. If Indigo applies changes as local/offline context, the echo guard also drops legitimate controller updates received in the same context. Document the concrete context contract, or add a command correlation/origin field and suppress only the matching self-originated event.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/PRD-indigo-matter-export.md` around lines 255 - 258, Update the Echo
guard section to remove reliance on ctx.offline as the origin marker. Document
the concrete event-context contract if one exists; otherwise specify a command
correlation or origin field that bridge-node echo suppression uses to ignore
only matching Indigo-originated writes while allowing legitimate controller
updates.
| - **Endpoint count:** no hard cap in v1, but log a warning past ~100 exports — | ||
| ecosystem per-home accessory limits (Apple's in particular) will bite first. | ||
| ecosystem per-home accessory limits will bite first (Alexa hard-caps at 50 | ||
| bridged devices; Apple degrades past ~200). Memory is not the constraint: | ||
| measured ~145MB RSS floor + ~0.3MB per endpoint at 0.17.8 (XOQ6 answered). |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win
Align capacity warnings with the stated limits.
At about 145 MB plus 0.3 MB per endpoint, 200 endpoints use about 205 MB before other process overhead. This exceeds the stated 200 MB budget. The warning at about 100 exports is also above the stated Alexa limit of 50. Define per-ecosystem and memory thresholds. Warn or refuse before crossing them.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/PRD-indigo-matter-export.md` around lines 259 - 262, Update the
endpoint-capacity guidance in the PRD to define explicit per-ecosystem limits
and a memory threshold using the stated RSS baseline and per-endpoint growth.
Set warnings and refusal behavior before the Alexa limit and before projected
memory usage exceeds the 200 MB budget, replacing the vague “~100 exports”
warning while preserving the documented Apple and Alexa constraints.
Protocol spec (BRIDGE_PROTOCOL.md): - open_commissioning_window + window_closed event; get_pairing now models the non-durable passcode (windowOpen/expiry) instead of a static code - set_state response contract: node always responds; plugin sends without awaiting; client MUST log unmatched error responses; per-connection ordering guaranteed - full per-role command table (names + args) beside the state-key table; systemMode domain and 0-100<->0-254 rounding pinned - error-code catalogue (1.1); node-side version_mismatch behaviour; attach deadline for unattached sockets; supersede rationale corrected (half-open socket recovery, not the reconnect loop) - mass-removal guard on attach (intent: replace_all) - factory_reset preserveEndpointNumbers flag + rebuild_endpoint_map as the PRD §7 explicit-rebuild path; endpoint map persisted outside the matter.js storage context - fixed §5.1->§4.3 cross-refs, unified StatusReport shape, pinned WS port 5581, golden frames as shared JSON fixture files PRD: - thermostat row: no fan in v1 (fan descope applied consistently) - §7 lock/valve row trimmed to lock; added 5540 bind-conflict row - §4.2 bridge storage now stated as sacred as the controller's - milestone gating untangled: XAC2 lands at E7, XAC3's code display at E6, E3 gates on round-trip against a manually started node - 5540/EP1 claim attributed to matter.js ECOSYSTEMS.md, made a pref - XAC3/XOQ1/§9 now cite ADR-0007 (drafted in the workspace repo, proposed) instead of narrowing accepted ADR-0006 silently - MATTER.md citation fixed; XOQ2 tombstone; UI-C gap note; §5.4 watchdog gains RSS logging; §5.5 two-port readout CLAUDE.md: drop stale hardcoded PluginVersion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JxqGhP3DcENf68AZK21U4S
Summary
PR 1 of the Matter export build (PRD-indigo-matter-export.md, ADR-0006). Docs only.
Endpoint.id(verified by execution at 0.17.8); ids derive from the immutable Indigo device ID, with a drift detector and storage-loss refuse-to-start required by the PRD.indigo-matter-bridge).protocolVersionhandshake that fails closed on skew, endpoint/state/command vocabulary, golden-frame testing contract).Test plan
Docs-only; CI version-check satisfied by the PluginVersion bump (2026.7.21 → 2026.7.22).
🤖 Generated with Claude Code
https://claude.ai/code/session_01JxqGhP3DcENf68AZK21U4S
Summary by CodeRabbit
New Features
Documentation
Chores