NotASithLord / NotASithLord/peerd

dweb: per-dwapp derived subidentities — break cross-dwapp linkability of the single mesh DID

Open
#320 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
410
Forks
45
Avg merge
11d 5h
Merged PRs (30d)
1

Description

Problem

There is exactly one Ed25519 mesh identity per browser profile (vault DWEB_IDENTITY_SECRET, held by the offscreen base node — offscreen/dweb-base.js: "one identity per browser"). Every surface reuses it: base-mesh presence, every dwapp's room membership (rosters are DIDs), publishing, and A2A. Consequence: any peer who shares two rooms with you can link your activity across every dwapp you run — the union of your dwapp usage is one globally-correlatable identity. The hello() bridge op hands the DID string to every dwapp, so apps themselves can also correlate out-of-band.

This is invisible now (small mesh) and expensive later: fixing it after the mesh has a population means rotating every user's visible identity in every room at once.

Proposal

Domain-separated derived subidentities, consistent with the codebase's existing discipline (the D-8 rule; content/manifest.js's tagged-signature scheme):

  • seed_dwapp = HKDF(master_secret, "peerd/dwapp-identity/v1" || dwapp_id) → per-dwapp Ed25519 keypair → per-dwapp did:key.
  • Room presence, announce, publish/DM, mute all use the derived DID for that dwapp.
  • Publisher identity stays the master DID — publishing is deliberately attributable (dwapp_id = H(publisher‖slug) depends on it); unlinkability is for usage, not authorship.
  • The agent envoy (reserved peerd-agent room, the dweb actor) keeps the master DID or gets its own fixed derivation — it is the user's addressable agent, so stability there is the point.
  • hello() reports the derived DID, shrinking what a dwapp can correlate.

Derivation is deterministic, so no new vault state: the same master secret yields the same per-dwapp identity on every unlock.

Granularity decision

Per-dwapp (not per-room): the bridge already enforces one room per app, and a stable in-app identity is what users expect socially (your name in the commons shouldn't rotate). Per-room is strictly stronger unlinkability but can be a later knob ("peerd/dwapp-identity/v1" || dwapp_id || roomId) — the versioned info-string leaves room.

Design-through list (the real work)

  • DM routing and mute lists key on DID (offscreen/dweb-base.js, room membership) — the base node must hold N subidentities concurrently and route ch=3 traffic per derived key.
  • dweb_block semantics: blocking a peer's publisher DID vs. one of their room subidentities are different acts; decide what the user-facing block means (probably: block the identity you can see; publisher-level ban stays the discovery-plane mechanism).
  • Signaling/DHT: provider records and rendezvous currently assume the one DID; audit which planes may carry a subidentity without breaking Kademlia bookkeeping.
  • Grant keying (bridge.js appKey) is content-hash based and orthogonal — unaffected.
  • Rate caps (background/dweb-inbound-rate-cap.js) are per-did — per-subidentity caps must not multiply an attacker's budget (cap by master on our side, by observed did for inbound).

Prior art

Cloudflare OS scopes identity per-account-per-vendor and deliberately passes gatekeepers an opaque random observer id instead of a real identity precisely to prevent cross-context correlation. Same instinct, P2P setting.

Do this while the mesh is small; it's a wire-visible identity change.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in offscreen/dweb-base.js, tracing the single identity, hello(), room membership, DM/mute routing, and signaling paths; then inspect content/manifest.js, bridge.js, and background/dweb-inbound-rate-cap.js. Map which operations use the master DID and which must use a derived per-dwapp DID. Done requires a consistent wire-visible identity design, preserved publisher and agent semantics, and resolved block and rate-cap behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
distributed-systems, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.