Skip to content

feat: protocol redesign with member layer, dual node paths, and recipient-based auth#84

Merged
LiranCohen merged 1 commit into
mainfrom
feat/protocol-redesign
Feb 26, 2026
Merged

feat: protocol redesign with member layer, dual node paths, and recipient-based auth#84
LiranCohen merged 1 commit into
mainfrom
feat/protocol-redesign

Conversation

@LiranCohen

Copy link
Copy Markdown
Contributor

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)

  • Two node paths: network/node (owner-provisioned devices) and network/member/node (member-associated devices)
  • Member layer: network/member records with $role: true assign the member role via the recipient field
  • nodeInfo child record: device-operational data (hostname, OS, capabilities) split from the node record into a separate nodeInfo record written by the device itself
  • Recipient-based write auth: { "who": "recipient", "of": "network/node" } lets devices write their own nodeInfo and endpoint records without needing a protocol role for writes
  • New schemas: member.json, node-request.json, node-info.json
  • Slimmed node schema: only owner-controlled fields (meshIP, 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)
  • New WithProtocolRole option — anchor reads as author (no role), non-anchor nodes use "network/node"
  • nodeRecordToNode() pulls hostname/OS/capabilities from NodeInfoData child record

Data structs (internal/control/data.go)

  • New MemberRecord, NodeInfoData structs
  • NodeRecord slimmed: Hostname, OS, Capabilities removed (now in Info *NodeInfoData)
  • Added MemberRecordID, SourceDWN fields

Registration (internal/mesh/register.go)

  • New CreateMember() and WriteNodeInfo() functions
  • RegisterNode() uses NodeDID (was SelfDID), supports both network/node and network/member/node paths via MemberRecordID
  • WriteEndpoint() supports both paths

CLI (cmd/meshd/main.go)

  • network join redesigned: discovers pre-created node record (anchor must run peer add first), writes nodeInfo
  • network create writes nodeInfo after node registration
  • peer list queries both node paths
  • up passes MemberRecordID and ProtocolRole to engine

Engine (internal/engine/engine.go)

  • Config gains MemberRecordID and ProtocolRole fields
  • makeEndpointUpdateFunc routes writes to correct protocol path based on member status

State (internal/state/state.go)

  • NetworkState gains MemberRecordID and MemberDateCreated fields

Verification

go build ./...          # Zero build errors
go vet ./...            # Zero vet warnings
go test ./... -count=1 -race  # All tests pass, no data races

Integration tests (TestTwoNodeConnectivity, TestE2ERegistrationFlow) require DWN_ENDPOINT and run on push to main.

…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 LiranCohen merged commit 640bccc into main Feb 26, 2026
2 checks passed
@LiranCohen LiranCohen deleted the feat/protocol-redesign branch February 26, 2026 20:22
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: protocol redesign — node record split, recipient authorization, and cross-network node sharing

1 participant