Skip to content

[feature] social_connection invites have no stable id, and withdrawal is a read-then-delete #1162

Description

@Bekiboo

Description

Proposal, not a request. Came out of #1147 and two CodeRabbit findings on it. The wallet already works around both, so none of this is urgent. I'm raising it because the workarounds are in the wallet while the cause is in evault-core, and whoever owns the binding-document model is better placed than me to say whether either is worth changing.

A social_connection invite is two documents: the real one in the recipient's vault, and a single-signature mirror in the sender's. Nothing links them, and the sender can only learn the invite's fate by reading the recipient's vault. Two problems follow.

1. An invite has no stable id. The wallet matches a mirror to its remote document on data.relation_description, because there's nothing else to match on. Two pending invites to the same person with the same description (usually the empty one) are indistinguishable, so cancelling one can withdraw the other, and reconciliation can put the "confirmed" label on the wrong row. Neither is visible to the user, since the rows only differ by timestamp, so the cost today is about nil.

Putting an id in data doesn't work from a client. I tried both ways against a local eVault:

  • Sign over data including the extra field → Invalid owner signature. validateBindingDocumentData rebuilds the object from kind, name, parties, relation_description only (BindingDocumentService.ts:115-119) and hashes that.
  • Sign over the stripped data and send the extra field anyway → accepted, and the field is gone on read.

So it needs a schema change plus a plan for existing documents.

2. Withdrawing an invite is a read-then-delete. cancelSentSocialBinding checks the signature count, then calls deleteMetaEnvelope, which matches on id and eName and deletes unconditionally (typedefs.ts:475, db.service.ts:627). If the recipient counter-signs in between, the withdrawal deletes a completed binding. The window is one round trip and the recipient has to tap Accept inside it, so it's rare, but a second client-side read can't close it.

One more thing from the same review: BindingDocument and MetaEnvelope carry no server-assigned timestamp, so the wallet compares signature timestamps written by two different phones when it decides whether an envelope is a stale leftover or a new invite. Clock skew makes those two cases look the same. A server-side creation time would settle it.

Reference

Acceptance Criteria

Only if either is picked up:

  • An invite can be matched to its counterpart across two vaults without relying on relation_description.
  • A withdrawal can't delete a document that's been counter-signed since it was read.

Desired Output (may vary)

Sketches, not designs:

  • For (1), an optional id on social_connection that survives validation, with existing documents still parsing without it.
  • For (2), a delete that takes an expected state — a signature count or a document hash — and refuses instead of deleting when it no longer matches. The wallet would keep the local mirror on a refusal instead of orphaning it.

Activity

  1. assigned and unassigned on Sep 25, 2026
  2. changed the title [-][feature] Proposal: social_connection invites have no stable identity, and withdrawing one is a read-then-delete[/-] [+][feature] social_connection invites have no stable id, and withdrawal is a read-then-delete[/+] on Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions