feat: protocol redesign with member layer, dual node paths, and recipient-based auth#84
Merged
Merged
Conversation
…ient-based auth (#82) Restructure the wireguard-mesh protocol to add a member layer, split node records into owner-controlled (node) and device-controlled (nodeInfo) parts, and introduce recipient-based write authorization so devices can write their own endpoint and nodeInfo records. Protocol changes: - Two node paths: network/node (owner-provisioned) and network/member/node (member-associated devices invited via the member layer) - Member records (: true) assign network/member role via recipient field - nodeInfo child record holds device-operational data (hostname, OS, capabilities) - Endpoint and nodeInfo writes use recipient-of-node authorization - New schemas: member.json, node-request.json, node-info.json - Node schema slimmed to owner-controlled fields only (meshIP, allowedIPs, label) State loading (dwnclient.go): - LoadState() queries 10 record types across both node paths - Merges members, owner nodes, member nodes, nodeInfo, and endpoints - New WithProtocolRole option (anchor reads as author, others use network/node) - nodeRecordToNode() pulls hostname/OS from NodeInfoData child record CLI (main.go): - network join redesigned: discovers pre-created node record from anchor - network create writes nodeInfo after node registration - peer add/list support both node paths - up passes MemberRecordID and ProtocolRole to engine Engine: - Config gains MemberRecordID and ProtocolRole fields - makeEndpointUpdateFunc routes writes to correct protocol path - DWNClient receives protocol role for non-anchor reads All tests pass with -race. Build and vet clean.
LiranCohen
added a commit
that referenced
this pull request
Feb 26, 2026
Add comprehensive E2E integration tests that validate the protocol redesign from PR #84 against a real DWN server: - TestE2EFullMeshLifecycle: exercises the complete mesh lifecycle with both owner-provisioned (network/node) and member-associated (network/member/node) paths, including CreateMember, WriteNodeInfo, WriteEndpoint, and LoadState dual-path merging. - TestE2ERecipientBasedOwnerNodeWrite: regression test for the exact scenario that was broken before PR #84 — a non-anchor node writing its own nodeInfo and endpoint using recipient-based authorization. - TestE2EACLPolicyRoundTrip: writes an ACL policy to the anchor DWN and verifies LoadState produces correct, non-wildcard filter rules with DID-to-IP resolution and port ranges. - TestE2ELoadStateNonAnchor: verifies a non-anchor node can run LoadState using the network/node role for read authorization. These tests require DWN_ENDPOINT to be set and are skipped otherwise. They cover gaps not exercised by existing tests: member layer, nodeInfo writes, dual-path LoadState, ACL round-trip, and non-anchor reads.
LiranCohen
added a commit
that referenced
this pull request
Feb 26, 2026
Add comprehensive E2E integration tests that validate the protocol redesign from PR #84 against a real DWN server: - TestE2EFullMeshLifecycle: exercises the complete mesh lifecycle with both owner-provisioned (network/node) and member-associated (network/member/node) paths, including CreateMember, WriteNodeInfo, WriteEndpoint, and LoadState dual-path merging. - TestE2ERecipientBasedOwnerNodeWrite: regression test for the exact scenario that was broken before PR #84 — a non-anchor node writing its own nodeInfo and endpoint using recipient-based authorization. - TestE2EACLPolicyRoundTrip: writes an ACL policy to the anchor DWN and verifies LoadState produces correct, non-wildcard filter rules with DID-to-IP resolution and port ranges. - TestE2ELoadStateNonAnchor: verifies a non-anchor node can run LoadState using the network/node role for read authorization. These tests require DWN_ENDPOINT to be set and are skipped otherwise. They cover gaps not exercised by existing tests: member layer, nodeInfo writes, dual-path LoadState, ACL round-trip, and non-anchor reads.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Restructures the wireguard-mesh protocol to fix CI integration test failures (#82) by adding a member layer, splitting node records, and introducing recipient-based write authorization so non-owner devices can write their own endpoint and nodeInfo records.
Closes #82.
What changed
Protocol (
protocols/wireguard-mesh.json)network/node(owner-provisioned devices) andnetwork/member/node(member-associated devices)network/memberrecords with$role: trueassign the member role via therecipientfieldnodeInforecord written by the device itself{ "who": "recipient", "of": "network/node" }lets devices write their ownnodeInfoandendpointrecords without needing a protocol role for writesmember.json,node-request.json,node-info.jsonmeshIP,allowedIPs,label,addedAt,sourceDWN)State loading (
internal/control/dwnclient.go)LoadState()now queries 10 record types across both node paths: network config, owner nodes, members, member nodes, relays, ACL policy, nodeInfo (both paths), endpoints (both paths)WithProtocolRoleoption — anchor reads as author (no role), non-anchor nodes use"network/node"nodeRecordToNode()pulls hostname/OS/capabilities fromNodeInfoDatachild recordData structs (
internal/control/data.go)MemberRecord,NodeInfoDatastructsNodeRecordslimmed:Hostname,OS,Capabilitiesremoved (now inInfo *NodeInfoData)MemberRecordID,SourceDWNfieldsRegistration (
internal/mesh/register.go)CreateMember()andWriteNodeInfo()functionsRegisterNode()usesNodeDID(wasSelfDID), supports bothnetwork/nodeandnetwork/member/nodepaths viaMemberRecordIDWriteEndpoint()supports both pathsCLI (
cmd/meshd/main.go)network joinredesigned: discovers pre-created node record (anchor must runpeer addfirst), writesnodeInfonetwork createwritesnodeInfoafter node registrationpeer listqueries both node pathsuppassesMemberRecordIDandProtocolRoleto engineEngine (
internal/engine/engine.go)ConfiggainsMemberRecordIDandProtocolRolefieldsmakeEndpointUpdateFuncroutes writes to correct protocol path based on member statusState (
internal/state/state.go)NetworkStategainsMemberRecordIDandMemberDateCreatedfieldsVerification
Integration tests (
TestTwoNodeConnectivity,TestE2ERegistrationFlow) requireDWN_ENDPOINTand run on push to main.