Skip to content

MIP-00/MIP-02: Add client_id tag for multi-device KeyPackage discovery#40

Draft
justinmoon wants to merge 1 commit intomarmot-protocol:masterfrom
justinmoon:mip00-client-id-tag
Draft

MIP-00/MIP-02: Add client_id tag for multi-device KeyPackage discovery#40
justinmoon wants to merge 1 commit intomarmot-protocol:masterfrom
justinmoon:mip00-client-id-tag

Conversation

@justinmoon
Copy link
Copy Markdown

@justinmoon justinmoon commented Feb 16, 2026

NOTE: this PR was created during a livestream with myself and Derek Ross. We were trying to figure out how to add multiple clients to Derek's Burrow app. This PR isn't polished and is purely meant to start a discussion. Everything after this sentence (including the MIP edits) was written by coding agent:

Summary

Each device installation generates a random 32-byte client_id on first launch and includes it in every Kind:443 KeyPackage event. This allows inviters to group KeyPackages by client_id and select exactly one per device, solving the multi-device discovery problem.

The Problem

When inviting a user to a group, the inviter queries relays for the user's KeyPackage events. If the user has multiple devices (e.g., Android + iOS), each device publishes KPs under the same npub. There is currently no way to distinguish which device produced which KeyPackage.

Naive strategies like "pick the N most recent" fail: if one device refreshes more frequently, all the top KPs may belong to the same device, and the other device is silently excluded from the group.

The Solution

A new client_id tag on Kind:443 events:

["client_id", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"]

The inviter's algorithm becomes:

  1. Query all Kind:443 events for the target pubkey
  2. Group by client_id tag
  3. Select the newest KP per group
  4. Create a separate Welcome for each

Changes

  • MIP-00: Added client_id to the example event and field explanations. New "Multi-Device Support" section covering the discovery problem, tag specification, selection algorithm, privacy considerations, and backward compatibility.
  • MIP-02: Added "Multi-Device Considerations" section for Welcome delivery to multiple devices.
  • CHANGELOG: Documented both additions.

Privacy

The client_id is a random opaque value — it reveals nothing about device type, OS, or hardware. It does reveal that a given npub has N active devices, which is already observable once those devices join a group (each becomes a visible leaf node).

Backward Compatibility

KPs without client_id are treated as belonging to unique, unknown devices. As adoption increases, untagged KPs become rare.

Related

  • Pika issue: https://github.com/ArcadeLabsInc/pika/issues/95
  • Proof-of-concept test in MDK: crates/mdk-core/tests/multi_device.rs demonstrates the full flow including a scenario where the naive heuristic fails and the client_id approach succeeds.

Each device installation generates a random 32-byte client_id on first
launch and includes it in every Kind:443 KeyPackage event. This allows
inviters to group KeyPackages by client_id and select exactly one per
device, solving the multi-device discovery problem.

Changes:
- MIP-00: Add client_id to example event, field explanations, and a new
  'Multi-Device Support' section covering the discovery problem, the
  client_id tag spec, the selection algorithm, privacy considerations,
  and backward compatibility
- MIP-02: Add 'Multi-Device Considerations' section for Welcome delivery
  to multiple devices
- CHANGELOG: Document both additions
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 16, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@alltheseas
Copy link
Copy Markdown
Contributor

Critical Gaps

  1. client_id is a long-lived correlation handle — privacy impact is understated

The PR says it "reveals nothing" beyond device count, but that's not accurate. A persistent client_id that lives for the entire installation lifetime allows
passive observers on relays to:

  • Track per-device KP refresh frequency and timing patterns
  • Correlate activity windows to infer timezone/usage habits per device
  • Build a longitudinal device fingerprint — even if the value itself is random

The PR handwaves this as "already observable once devices join a group" but that's visible only to group members. The client_id on kind: 443 events is visible to
anyone querying the relay, before any group is joined. This is a meaningful privacy regression that deserves more than a paragraph.

  1. No device lifecycle management

The spec has zero guidance on:

  • Stale/dead devices: What happens when a phone is lost or wiped? Old KPs with that client_id persist on relays indefinitely. The inviter will create a Welcome
    for a device that no longer exists.
  • Reinstallation: A reinstall generates a fresh client_id, but old KPs with the old ID may still be on relays. The inviter now sees two "devices" — one a ghost.
    The ghost Welcome fails silently.
  • Maximum device count: No bounds. A buggy or malicious client could publish KPs with many distinct client_id values, causing inviters to create dozens of
    Welcomes.
  • Revocation/expiry: No mechanism to signal "this client_id is no longer active."

This is the biggest practical gap. Without lifecycle management, the client_id degrades to a best-effort heuristic that creates phantom leaf nodes.

  1. The backward compat story is actually harmful

"Each untagged KeyPackage SHOULD be treated as belonging to a unique, unknown device"

This means if a user has 5 old-style KPs without client_id, the inviter creates 5 separate Welcomes, potentially creating 5 leaf nodes for one device. The
alternative suggested ("select only the single most recent untagged KP") risks missing real devices. Neither option is good — this should be a clearer MUST with a
single strategy.

  1. No validation or trust model

The client_id is entirely self-reported. There's no discussion of:

  • Format validation: What if a client publishes a client_id that's not 64 hex chars? Silently ignore the tag? Reject the KP?
  • Sybil via client_id: A malicious client can publish KPs with N different client_id values to appear as N devices, forcing inviters to create N Welcomes and N
    leaf nodes.
  • Deduplication across client_id collision: Two installations generating the same random 32 bytes is astronomically unlikely but the spec should state the
    assumption.
  1. Alternative approaches not discussed

The PR doesn't explain why client_id was chosen over alternatives:

  • Group by MLS signing key / credential: Each device already has a unique signing key. The inviter could group KPs by the signing public key embedded in the
    KeyPackage itself — no new tag needed. This is already part of the MLS data.
  • NIP-78 device registration events: A dedicated "device list" event that maps client_id → relay hints, with explicit add/remove semantics.
  • Key transparency / device list signed by identity key: Would allow users to authoritatively declare their device set.

At minimum, a "Rationale" or "Alternatives Considered" section would strengthen the proposal.

  1. SHOULD vs MUST inconsistency

The client_id tag is "recommended" (SHOULD), but the selection algorithm in "KeyPackage Selection for Multi-Device Users" is also SHOULD. If clients don't
implement either, multi-device is still broken. If this is important enough to spec, it should be stronger — or the spec should explicitly state that
single-device implementations can ignore this entirely.

  1. Welcome delivery section in MIP-02 adds little new content

The MIP-02 addition says "create a separate Welcome for each device's KeyPackage" — but this is already how MLS works (one Welcome per KeyPackage). The
clarification isn't wrong, but it's restating MLS fundamentals rather than adding Marmot-specific guidance.


What's Good

  • The problem statement is clear and well-motivated
  • The solution is simple to implement (one new tag, straightforward algorithm)
  • The privacy section is honest about what's revealed, even if it undersells the impact
  • Base64/hex encoding consistency is maintained

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.

2 participants