Merge branch 'master' into badge-in-app-checkout

# Conflicts:
#	simplex-chat.cabal
#	src/Simplex/Chat/Store/Postgres/Migrations.hs
#	src/Simplex/Chat/Store/SQLite/Migrations.hs
#	src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt
#	src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql
This commit is contained in:
spacedandy-agents-simplex committed 2026-10-16 09:40:53 +00:00
commit e3324c6828
287 files changed
+11236 -2113

No files matched your search

+2 -1
View File
@@ -1,11 +1,12 @@
---
title: Download SimpleX apps
permalink: /downloads/index.html
revision: 09.09.2024
---
# Download SimpleX apps
Download the app for your platform: https://simplex.chat/downloads/
You can get the latest beta releases from [GitHub](https://github.com/simplex-chat/simplex-chat/releases).
If you cannot access GitHub, you can download SimpleX Chat apps from our mirror at [git.simplex.chat](https://git.simplex.chat/simplex-chat/simplex-chat/releases)
+1 -1
View File
@@ -49,6 +49,6 @@ This is a part-time remote contract. You must be in UTC +/- 8 hours timezone.
## How to join the team
To apply, please [install SimpleX Chat app](./DOWNLOADS.md) and send your achievements to this [SimpleX address](https://smp16.simplex.im/a#OGL3qf7utOrUERFoFOROgdQaAkj_znzoeACNKDAsFNA).
To apply, please [install SimpleX Chat app](https://simplex.chat/downloads/) and send your achievements to this [SimpleX address](https://smp16.simplex.im/a#OGL3qf7utOrUERFoFOROgdQaAkj_znzoeACNKDAsFNA).
**We do NOT review CVs at the initial stage, please only send the links to your achievements**.
+3 -3
View File
@@ -44,11 +44,11 @@ Creating a channel involves generating cryptographic material, creating the chan
When a relay receives an invitation to serve a channel, it validates the channel and creates its own relay link. This flow is currently part of channel creation; adding relays to an existing channel is planned but not yet implemented.
1. Owner sends `x.grp.relay.inv` to the relay's contact address. This message includes the relay's member ID and role, the owner's profile, and the channel's short link.
1. Owner sends `x.grp.relay.inv` to the relay's contact address. This message includes the relay's member ID and role, the owner's profile, the channel's short link, the channel's public group ID, and the owner's member key. The message is signed with the owner's member key over the public group ID and the owner's member ID.
2. Relay receives the invitation and creates a relay request record. A relay request worker processes it asynchronously.
2. Relay receives the invitation and creates a relay request record. From chat protocol version 22 of the owner, an invitation without a valid signature is ignored. The owner's badge is verified under the public group ID, the owner's member ID and the owner's member key from a signed invitation. A relay request worker processes it asynchronously.
3. The worker retrieves the channel's link data from the SMP server, extracts and validates the channel profile and owner authorization.
3. The worker retrieves the channel's link data from the SMP server, extracts and validates the channel profile and owner authorization. The request fails when the link's entity ID differs from the public group ID in the invitation, or when the owner's key in the link data differs from the owner's member key in the invitation.
4. The relay creates its own contact address link (the relay link) with the channel's entity ID in the immutable fixed data.
+3 -1
View File
@@ -232,7 +232,9 @@ Currently members can have one of four roles - `owner`, `admin`, `member` and `o
`x.grp.inv` message is sent to invite contact to the group via contact's direct connection and includes group member connection address. This message MUST only be sent by members with `admin` or `owner` role. Optional `groupLinkId` is included when this message is sent to contacts connected via the user's group link. This identifier is a random byte sequence, with no global or even local uniqueness - it is only used for the user's invitations to a given group to provide confirmation to the contact that the group invitation is for the same group the contact was connecting to via the group link, so that the invitation can be automatically accepted by the contact - the contact compares it with the group link id contained in the group link uri's data field.
`x.grp.acpt` message is sent as part of group member connection handshake, only to the inviting user.
`x.grp.inv` message can also be sent to a group member via the member connection, to invite the member to another group. In this case, the message MUST only be sent when direct messages are allowed for the sender in the group of the member connection, and receiving clients MUST ignore it otherwise.
`x.grp.acpt` message is sent as part of group member connection handshake, only to the inviting user. It optionally includes the invited member's group profile. The inviting user replies with `x.grp.mem.info` that includes the inviting member's group profile.
`x.grp.link.inv` message is sent as part of connection handshake to member joining via group link, and contains group profile and initial information about inviting and joining member.
+3
View File
@@ -568,6 +568,9 @@
"params": {
"properties": {
"memberId": {"ref": "base64url"}
},
"optionalProperties": {
"profile": {"ref": "profile"}
}
}
}
@@ -0,0 +1,204 @@
# Inviting group members to other groups
## Problem
A group admin can invite only a contact to a group.
To invite a member of group A to group B, the admin first creates a contact with this member:
1. Create a member contact (`APICreateMemberContact`).
2. Send the contact invitation in group A (`x.grp.direct.inv`).
3. Wait until the contact connection is established.
4. Invite the contact to group B (`APIAddMember`, `x.grp.inv`).
Steps 1-3 are possible only when direct messages are allowed in group A.
A direct chat is created with each invited member.
## Design
An admin of groups A and B invites a member of group A to group B.
The invitation is sent as `x.grp.inv` over the connection with this member in group A.
Only the invited member receives the invitation.
The invited member sees it in group A and joins group B in the same way as after an invitation from a contact.
### Protocol
`x.grp.inv` is accepted on group member connections, in addition to contact connections.
```
event x.grp.inv
params groupInvitation, unchanged
connection connection with the invited member in group A
forwarding excluded
group history excluded
```
Sender requirements:
```
group A preferences direct messages allowed for the user's role
role in group A author or above
role in group B admin or owner, and not lower than the invited role
group A group, not channel, different from group B
group B group, not channel
membership in A main profile
membership in B main profile
invited member current member with an active connection
invited member version 22 or later
```
The receiving client ignores the invitation when:
```
group A channel
group B channel
group A preferences direct messages not allowed for the sender's role
sender blocked
fromMember role below admin, or below the invited role
fromMember id equal to the invited member id
```
### Sending
New command, next to `APIAddMember`:
```haskell
APIAddMemberViaGroup
{ groupId :: GroupId,
viaGroupId :: GroupId,
groupMemberId :: GroupMemberId,
memberRole :: GroupMemberRole
}
```
```
groupId group B
viaGroupId group A
groupMemberId member of group A
memberRole role in group B
```
Syntax: `/_add #<groupId> #<viaGroupId> <groupMemberId> <memberRole>`
For a member outside group A, `SEGroupMemberNotFound` is returned.
New response, next to `CRSentGroupInvitation`:
```haskell
CRSentGroupInvitationViaGroup
{ user :: User,
groupInfo :: GroupInfo,
member :: GroupMember,
viaGroupInfo :: GroupInfo,
viaMember :: GroupMember
}
```
```
groupInfo group B
member record of the invited member in group B
viaGroupInfo group A
viaMember member of group A
```
In group B, the record of the invited member is linked to the member of group A.
Until the member joins, the same invitation is re-sent on a repeated command.
After the member joins, `CEGroupDuplicateMember` is returned.
After the member leaves, is removed or is rejected, a new invitation is sent.
The sent invitation is saved in group A as `CISndGroupInvitation`, visible to the sender only.
The name of the member in group A is stored in the item (`CIGroupInvitation.localDisplayName`), as the contact name in direct chats.
### Receiving
New event, next to `CEvtReceivedGroupInvitation`:
```haskell
CEvtReceivedGroupInvitationViaGroup
{ user :: User,
groupInfo :: GroupInfo,
viaGroupInfo :: GroupInfo,
viaMember :: GroupMember,
fromMemberRole :: GroupMemberRole,
memberRole :: GroupMemberRole
}
```
```
groupInfo group B
viaGroupInfo group A
viaMember sender, member of group A
fromMemberRole sender role in group B
memberRole invited role in group B
```
```
group B created with status "invited", or found by the invitation link
chat item CIRcvGroupInvitation in group A, main scope, from the sender
```
With automatic acceptance of group invitations enabled, the client joins group B and saves the item with status "accepted".
With an incognito membership in group A, the same incognito profile is used in group B.
In group B, copies of the group A profiles are stored for the sender and for the invited member.
Badge proofs bound to group A are dropped from the copies.
The copies are replaced by the profiles sent in the connection handshake (`x.grp.acpt`, `x.grp.mem.info`).
### Joining
The invited member joins group B with `APIJoinGroup`, or by tapping the invitation in group A.
Status of the invitation item:
```
pending received
accepted joined
rejected group B deleted before joining
```
With disappearing messages on in group A, the item disappears after the status changes, as in direct chats.
The connection with the host is created from the invitation link, independently of the inviter's connection.
So an invitation from a contact deleted before joining is accepted in the same way.
Before this change, joining failed with `SEContactNotFoundByMemberId`.
### Clients
```
group A existing group invitation card ("Tap to join"), with a label
chat list group B, added on CEvtReceivedGroupInvitationViaGroup
sending API only
```
Labels, on group invitation cards in group chats only:
```
received card For you only
sent card For <name> only name of the member in group A
```
### Bots API
```
APIAddMemberViaGroup new command
CRSentGroupInvitationViaGroup new response
CEvtReceivedGroupInvitationViaGroup new event
```
`APIAddMember`, `CRSentGroupInvitation` and `CEvtReceivedGroupInvitation`: unchanged.
### Version
```
chat version 22
```
Version 22 is shared with owner-signed relay invitations, in one constant `groupInvVersion`.
Version 22 is not in any release: v7.1.0-beta.6 uses version 21.
## Out of scope
- Role changes of members invited via a group before they join.
- "Open" action on accepted invitations.
- Invitations to members pending approval in group A.
- User interface for sending invitations.