Skip to content

feat(chat): open invite key bundles and store the invite grant - #1868

Merged
bmc08gt merged 6 commits into
code/cashfrom
feat/group-invites-key-bundle
Oct 29, 2026
Merged

bmc08gt merged 6 commits into
code/cashfrom
feat/group-invites-key-bundle

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator

A member who joined a private group with an invite link has no key envelope of their own yet. GetKeyEnvelope returns the invite's copy of the chat key instead, and the app had no handling for that, so the chat stayed undecryptable.

GroupKeyEnvelopes now opens that copy with InviteCipher.openInviteKeyBundle. It gets the creator's public key from the Resolver and falls back to the wrapped_by_public_key the server reports when the lookup fails, as the InviteKeyEnvelope comment in model.proto allows. Then:

  • Opened: it wraps the key for this device and calls SetKeyEnvelope, the same path a creator-wrapped envelope takes. A failed store still returns the key.
  • Rejected: it marks the chat in WrongKeyChats, a new in-memory store that the joiner UI will render as the wrong-key banner. Logout clears it.
  • Unknown envelope scheme: it fails as UntrustedEnvelope without the wrong-key mark.
  • Any other failure: nothing is cached, so opening the chat fetches the key again.

chat_metadata gains can_invite, from ViewerState.permissions.can_invite. It is migration 45→46, and the migration spec resets viewer_state_version so existing chats pick up the grant on their next GetChat, as 36→37 did for can_edit. invitedBy on the MemberJoined system message already came in with #1863, so this PR doesn't touch it.

Stacks on #1866, which is merged. Targets code/cash. The host and joiner screens follow in separate PRs.

@bmc08gt bmc08gt self-assigned this Oct 28, 2026
@github-actions github-actions Bot added type: feature New functionality area: auth Login, session, access keys, identity area: network gRPC, connectivity, API, exchange rates labels Oct 28, 2026
@bmc08gt
bmc08gt force-pushed the feat/group-invites-key-bundle branch from 2686aba to 27f1322 Compare October 28, 2026 17:31
@bmc08gt
bmc08gt added this pull request to stack #1871 October 28, 2026 17:32
Bind DefaultInviteCipher for injection and convert invite envelopes between the
transport models and the chat-cipher types. The cipher implements one scheme per
message and leaves the check to the caller, so an envelope in any other scheme
maps to null. FakeInviteCipher stands in for it in JVM tests, since libsodium
can't load there, and rejects a bundle at the same step the real one would.
A member who joined with an invite link gets GetKeyEnvelope's
invite_key_bundle until they store their own envelope, and GroupKeyEnvelopes
returned it as a failure, so that member never had a key. It now opens the
bundle with InviteCipher.openInviteKeyBundle, wraps the chat key for this
device and calls SetKeyEnvelope, the same way it handles an envelope the
creator wrapped.

The creator's key comes from the Resolver, falling back to the
wrapped_by_public_key the server reports. A bundle that fails a check, or uses
a scheme this build doesn't know, marks the chat in WrongKeyChats and returns
UntrustedEnvelope, so messages show as undecryptable instead of waiting for a
key. Other failures stay uncached, so opening the chat retries the fetch.
ViewerState.Permissions.canInvite arrived with flipcash2-client-protocol
0.22.0, but chat_metadata had no column for it, so a host lost the grant
whenever a chat loaded from the database and the invite entry points
couldn't be gated on it.

v46 adds chat_metadata.can_invite (default 0), written and cleared
alongside can_edit. Migration45To46 zeroes viewer_state_version for the
reason Migration36To37 gives: the next fetch reports the version the row
already holds, and without the re-arm the gate drops it and the grant
stays 0 on every chat that predates the upgrade.

Also adds a round-trip test for MemberJoined's invitedBy, which the
System JSON already carries.
An invite envelope in a scheme this build doesn't implement says nothing
about whether the key is right, so it now fails as UntrustedEnvelope the way
an unsupported KeyEnvelope does, and the chat isn't shown as wrong-key.

Adds tests for the fallback to wrapped_by_public_key when the creator's key
can't be looked up, ALREADY_SET after an invite bundle opens, and a rejected
bundle being fetched again on the next open.
WrongKeyChats is keyed by chat id alone, so a chat marked under one account
stayed marked for the next account to sign in on the device.
@bmc08gt
bmc08gt force-pushed the feat/group-invites-key-bundle branch from 27f1322 to 3712305 Compare October 29, 2026 04:05
@bmc08gt
bmc08gt merged commit b725f75 into code/cash Oct 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: auth Login, session, access keys, identity area: network gRPC, connectivity, API, exchange rates type: feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant