Skip to content

feat: member layer, device ownership model, and cross-network node sharing via Permission Grants #83

@LiranCohen

Description

@LiranCohen

Summary

Design and implement the member/device ownership model that enables:

  • Personal device meshes (Alice's devices talk to each other via her own DWN)
  • Organizational membership (Alice joins Org X, proposes devices, org approves)
  • Org-provisioned devices (Org X creates devices it owns, assigns them to members)
  • Cross-org device sharing (Org X shares a server with Org Y, retaining full control)

All of this is built on a single principle: every device is owned by exactly one DWN, and cross-DWN visibility is controlled by Permission Grants.


Core Model

Every DID has a DWN with the mesh protocol

The mesh protocol is installed identically on every DWN — personal or organizational. There is no structural difference between a "personal mesh" and an "org mesh." They are the same protocol at different scales.

The base structure is:

network
  ├── member ($role: true, recipient = member DID)
  │     ├── nodeRequest (member writes — proposes a device)
  │     └── node (network owner creates on approval)
  │
  ├── node ($role: true, recipient = node DID — owner-provisioned devices)
  │     ├── nodeInfo (node self-writes)
  │     └── endpoint (node self-writes)
  │
  ├── aclPolicy
  └── relay
  • Alice's DWN: Alice is the owner. Her devices are nodes. She can also invite members (e.g., her friend Bob) — same protocol, same structure.
  • Org X's DWN: Org X is the owner. Org-owned servers are top-level nodes. Members (employees) are invited and propose their personal devices.

A DWN with just the owner's devices and no members is the simplest case. Adding a member is just "granting another person access to your mesh." The protocol scales from one person's devices to a large organization without structural changes.

Device ownership is DWN-local

A device's authoritative records (nodeInfo, endpoint) always live on one DWN — the DWN of whoever owns that device. Alice's personal laptop writes to Alice's DWN. An Org X corporate server writes to Org X's DWN. Devices never move between DWNs.

Cross-DWN visibility uses Permission Grants

When a device needs to be visible on a foreign network, the device's owner creates a Permission Grant on their DWN, allowing the foreign network to read that device's data. The foreign network creates a reference record pointing to the device on the source DWN. No data is copied or mirrored — the source DWN remains authoritative.


Authorization Model

Intra-DWN: Roles handle everything

Within a single DWN, no explicit Permission Grants are needed. The protocol's role system handles authorization:

  • member record (recipient = Alice's DID) → gives Alice the network/member role
  • member/node record (recipient = device DID) → gives the device the network/member/node role
  • node record (recipient = device DID) → gives the device the network/node role (for owner-provisioned devices)

These roles grant read access to the records they need via $actions:

{ "role": "network/member", "can": ["read"] }
{ "role": "network/member/node", "can": ["read"] }
{ "role": "network/node", "can": ["read"] }

Write access for devices comes from recipient authorization:

{ "who": "recipient", "of": "network/node", "can": ["create", "update"] }
{ "who": "recipient", "of": "network/member/node", "can": ["create", "update"] }

Cross-DWN: Delegated grants for member devices reading a foreign DWN

Alice is a member of Org X. Alice's member record gives her the network/member role on Org X's DWN. But Alice's devices have their own DIDs — they are not Alice. A device cannot directly invoke Alice's role.

Solution: Alice delegates read-only grants to her devices. When Alice provisions a device for Org X's mesh, she creates a delegated grant allowing the device to read from Org X's DWN acting as Alice. The device signs the request with its own key, includes the delegated grant, and the DWN resolves the author as Alice.

This delegation is read-only. Devices never write to Org X's DWN acting as Alice. Their write operations (nodeInfo, endpoint) go to Alice's own DWN using their own recipient of network/node authorization.

Cross-DWN: Permission Grants for foreign DWNs reading device data

Org X's nodes need to read Alice's device's nodeInfo and endpoint from Alice's DWN. Org X doesn't have a role on Alice's DWN.

Solution: Alice creates a Permission Grant on her DWN allowing Org X to read the device's subtree. Alice creates this grant at the same time as the nodeRequest (see flow below), so Org X can immediately read the device's nodeInfo to make an informed approval decision.

Authorization summary

Operation How authorized Where
Device reads Org X's mesh peers Delegated read grant from Alice (acts as Alice) Org X's DWN
Device writes its own nodeInfo/endpoint recipient of network/node (device's own role) Alice's DWN
Org X reads device nodeInfo/endpoint Permission Grant created by Alice Alice's DWN
Org owner manages members, approves devices author of network (owner authority) Org X's DWN
Member proposes/withdraws devices recipient of network/member Org X's DWN

Permission Grants are only needed for cross-DWN reads. Within a single DWN, the role system handles everything.


Membership and Device Approval

Joining a network — full flow

Step 1: Network owner invites member

Org X's admin creates a member record on Org X's DWN:

  • recipient: Alice's DID
  • Data: { label: "Alice", addedAt: "..." }
  • This assigns Alice the network/member role on Org X's DWN

Step 2: Alice proposes a device

Alice does two things simultaneously:

a) Writes a nodeRequest record under her member record on Org X's DWN:

  • Authorized by: { "who": "recipient", "of": "network/member", "can": ["create", "delete"] }
  • Data: { nodeDID: "did:jwk:aliceLaptop...", sourceDWN: "https://alice.dwn.example" }

b) Creates a Permission Grant on her own DWN:

  • recipient: Org X's DID
  • scope: { interface: "Records", method: "Read", protocol: meshProtocolURI, contextId: "aliceNetworkRecord/laptopNodeRecord" }
  • delegated: true (so Org X can sub-delegate to individual mesh nodes)

Because Alice creates the grant up front, Org X can immediately read the device's nodeInfo (hostname, OS, capabilities) from Alice's DWN to make an informed approval decision — e.g., using the hostname as the label.

Step 3: Org approves

Org X's admin creates a node record under Alice's member record on Org X's DWN:

  • Authorized by: { "who": "author", "of": "network", "can": ["create", "update", "delete"] }
  • recipient: the device's DID (did:jwk:aliceLaptop)
  • Data: { meshIP: "10.200.x.x", sourceDWN: "https://alice.dwn.example", addedAt: "..." }
  • The mesh IP is assigned by the org within their CIDR

Step 4: Operational

Alice creates a delegated read-only grant for her laptop, derived from her network/member role on Org X's DWN. The laptop uses this to read Org X's peer data. Alice manages this delegation herself — the org never needs to interact with individual device DIDs for read access.

Adding more devices later

  1. Alice writes a new nodeRequest on Org X's DWN + creates a Permission Grant on her DWN for the new device's subtree
  2. Org X approves by creating a member/node record
  3. Alice creates a delegated read grant for the new device — no action needed from Org X

Owner-provisioned devices

For devices the org owns directly (corporate laptop, server):

  1. Org X creates a node record at the top level (network/node) on Org X's DWN
  2. The device writes its own nodeInfo and endpoint to Org X's DWN (authorized as recipient of network/node)
  3. No Permission Grants needed — everything is on one DWN
  4. The org can optionally associate the device with a member for organizational purposes

Member removal

When Org X removes Alice:

  • Org X co-prunes Alice's member record → cascading deletion of all nodeRequest and node records under it
  • Alice's network/member role is revoked → her delegated grants become invalid
  • Alice's devices immediately lose read access to Org X's DWN
  • Org X's nodes lose the ability to act on Alice's device grants (Alice can also explicitly revoke them)

Either side can independently sever the relationship.


Cross-Organization Device Sharing

Org X owns a server. Org X wants Org Y's members to be able to reach it.

Constraint: Org X can only share devices they own (nodes on Org X's DWN). They cannot share Alice's personal devices — only Alice can grant access to those.

Sharing flow

Step 1: Org X grants access

Org X creates a Permission Grant on Org X's DWN:

  • recipient: Org Y's DID
  • scope: { interface: "Records", method: "Read", protocol: meshProtocolURI, contextId: "orgXNetworkRecord/serverNodeRecord" }
  • delegated: true (Org Y can sub-delegate to their nodes)

Step 2: Org Y accepts and references

Org Y creates a reference record on Org Y's DWN (a sharedNode or similar record type under network):

  • Data: { nodeDID: "did:jwk:server...", sourceAnchorDID: "did:jwk:orgX...", sourceDWN: "https://orgx.dwn.example", meshIP: "10.200.x.x" }
  • The mesh IP is assigned by Org Y within their CIDR

Step 3: Org Y grants reciprocal access

Org Y creates a Permission Grant on Org Y's DWN:

  • recipient: the shared server's DID (or Org X's DID, delegatable)
  • scope: scoped to the specific node subtrees the server should see
  • The server should NOT see Org Y's full topology — only the specific nodes it needs to communicate with

Step 4: Operational

  • Org Y's nodes read their own DWN → find sharedNode → use delegated grant to read server's nodeInfo + endpoint from Org X's DWN
  • The server reads Org X's DWN → finds sharing config → uses grant to read the specific Org Y peer endpoints from Org Y's DWN

Revocation

  • Org X revokes: Deletes the Permission Grant → Org Y can no longer read server data → server vanishes from Org Y's mesh
  • Org Y revokes: Deletes the sharedNode reference and their reciprocal grant → their nodes stop looking for the server

Either side can unilaterally revoke.

Quarantine

Shared devices are quarantined by default:

  • The shared server can receive connections from Org Y's nodes
  • The shared server cannot initiate connections to Org Y's nodes
  • Enforced by ACL packet filter rules on both sides
  • Org Y can explicitly relax quarantine in their ACL policy

Runtime Network Map Construction

For an owner-provisioned device on Org X

Reads from Org X's DWN:

  1. Native org nodes (top-level network/node) → read nodeInfo + endpoint directly
  2. Member devices (network/member/node) → follow sourceDWN pointer, use delegated grant to read nodeInfo + endpoint from member's personal DWN
  3. Shared nodes from other orgs → follow sharedNode reference, use delegated grant to read from source org's DWN

For Alice's personal laptop (member of Org X)

Participates in multiple meshes:

  1. Alice's personal mesh: reads Alice's DWN → discovers her other personal devices and any members she's invited
  2. Org X's mesh: uses delegated read grant (acting as Alice) to read Org X's DWN → discovers Org X peers

Each mesh has its own CIDR, so there's no address conflict. The engine builds a combined network map.

For a device shared from Org X into Org Y

Reads from Org X's DWN (its home DWN):

  1. Native Org X peers → normal read
  2. Org Y peers (from sharing arrangement) → uses grant to read specific peer data from Org Y's DWN

Protocol $actions Summary

Record Who writes Authorization
network DWN owner Owner authority
member DWN owner { "who": "author", "of": "network", "can": ["create", "update", "co-delete", "co-prune"] }
nodeRequest Member (person) { "who": "recipient", "of": "network/member", "can": ["create", "delete"] }
member/node DWN owner (on approval) { "who": "author", "of": "network", "can": ["create", "update", "delete"] }
node (owner-provisioned) DWN owner { "who": "author", "of": "network", "can": ["create", "update", "delete"] }
nodeInfo Device { "who": "recipient", "of": "network/node", "can": ["create", "update"] } (or of: network/member/node)
endpoint Device { "who": "recipient", "of": "network/node", "can": ["create", "update"] } (or of: network/member/node)

Reading

All roles get read access to the records they need:

{ "role": "network/member", "can": ["read"] }
{ "role": "network/member/node", "can": ["read"] }
{ "role": "network/node", "can": ["read"] }

Cross-DWN reads use delegated grants (for member devices reading a foreign mesh) or Permission Grants (for foreign DWNs reading device data).


Relationship to Other Issues


Open Questions

  1. Grant lifecycle automation: When Alice writes a nodeRequest, she creates the grant on her DWN simultaneously. When she provisions a device, she creates the delegated read grant. The meshd client should automate both. How much should be automatic vs. user-prompted?

  2. Multi-mesh routing: A device in multiple meshes has multiple CIDRs. The WireGuard engine needs routing for all of them. This is a runtime/engine concern.

  3. Context key delivery across DWNs: Encrypted records on a foreign DWN require context keys. The foreign DWN owner may need to deliver context keys to granted parties. The key-delivery protocol may need extension.

  4. Subscription across DWNs: For live updates, devices need to subscribe to foreign DWNs using Permission Grants with method: "Subscribe". The meshd subscription watcher needs to support multiple DWN connections.

  5. Shared node reference record type: The exact record type and schema for cross-org shared node references needs detailed design. Must contain source DWN endpoint, source anchor DID, device DID, and local mesh IP assignment.

  6. Read visibility scope: For v1, all members see all other members and all nodes. Finer-grained visibility (team-scoped access) is a future enhancement.

  7. Delegated grant mechanics: We are designing with the assumption that Alice can create delegated grants directly from her network/member role authority — no explicit Permission Grant from Org X needed. The DWN should accept the delegation, resolve the author to Alice, and check her role. Spec clarification tracked in enboxorg/dwn-spec#49.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions