diff --git a/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md b/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md
index 9bd342a146..207d1b842b 100644
--- a/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md
+++ b/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md
@@ -95,7 +95,7 @@ The diagram below shows all the encryption layers used in private message routin
For private routing to work, both the forwardig and the destination relays should support the updated messaging protocol - it is supported from v5.8 of the messaging relays. It is already released to all relays preset in the app, and available as a self-hosted server. We updated [the guide](../docs/SERVER.md) about how to host your own messaging relays.
-Because many self-hosted relays did not upgrade yet, private routing is not enabled by default. To enable it, you can open *Network & servers* settings in the app and change the settings in *Private message routing* section. We recommend setting *Private routing* option to *Unprotected* (to use it only with unknown relays and when not connecting via Tor) and *Allow downgrade* to *Yes* (so messages can still be delivered to the messaging relays that didn't upgrade yet) or to *When IP hidden* (in which case the messages will fail to deliver to unknown relays that didn't upgrade yet unless you connect to them via Tor).
+Because many self-hosted relays did not upgrade yet, private routing is not enabled by default. To enable it, you can open *Network & servers* settings in the app and change the settings in *Private message routing* section. We recommend setting *Private routing* option to *Unprotected* (to use it only with unknown relays and when not connecting via Tor) and *Allow downgrade* to *Yes* (so messages can still be delivered to the messaging relays that didn't upgrade yet) or to *When IP hidden* (in which case the messages will fail to deliver to unknown relays that didn't upgrade yet unless you connect to them via Tor).\
See [F.A.Q. section](../docs/FAQ.md#does-simplex-protect-my-ip-address) for answers about private message routing.
Read more about the technical design of the private message routing in [this document](https://github.com/simplex-chat/simplexmq/blob/stable/rfcs/done/2023-09-12-second-relays.md) and in [the messaging protocol specification](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/simplex-messaging.md#proxying-sender-commands).
diff --git a/blog/20240814-simplex-chat-vision-funding-v6-private-routing-new-user-experience.md b/blog/20240814-simplex-chat-vision-funding-v6-private-routing-new-user-experience.md
index de9e33a87e..d6aba6ef62 100644
--- a/blog/20240814-simplex-chat-vision-funding-v6-private-routing-new-user-experience.md
+++ b/blog/20240814-simplex-chat-vision-funding-v6-private-routing-new-user-experience.md
@@ -60,7 +60,7 @@ We are very happy to announce that we now have funds to move faster – we raise
When Jack discovered SimpleX Chat last year, he [posted on Twitter](https://x.com/jack/status/1661681076983529479):
-> Better than Signal? Looks promising.
+> Better than Signal? Looks promising.\
> A few bugs and UX issues but great foundation. Love that it’s public domain.
And [on Nostr](https://primal.net/e/note1txz9xmmc456kwcg7zrsrtqrhn7as29ptuz0qulu452k8n85hsshqq6uh6q):
diff --git a/blog/20241218-oppose-digital-ids-they-break-law-lead-to-mass-scale-surveillance.md b/blog/20241218-oppose-digital-ids-they-break-law-lead-to-mass-scale-surveillance.md
index 8a8fffffb4..add075e40a 100644
--- a/blog/20241218-oppose-digital-ids-they-break-law-lead-to-mass-scale-surveillance.md
+++ b/blog/20241218-oppose-digital-ids-they-break-law-lead-to-mass-scale-surveillance.md
@@ -34,14 +34,14 @@ To make your voice heard, email your MP expressing your rejection of digital IDs
*I object to the introduction of digital IDs in pubs or any other public places for these reasons:*
-1. *It violates the European Convention of Human Rights, article 8: “Everyone has the right to respect for his private and family life” (https://fra.europa.eu/en/law-reference/european-convention-human-rights-article-8-0).*
+1. *It violates the European Convention of Human Rights, article 8: “Everyone has the right to respect for his private and family life” (https://fra.europa.eu/en/law-reference/european-convention-human-rights-article-8-0).*\
*Asking to present digital IDs when proof of identity is not legally required, even if it is optional, disrespects our privacy, and is therefore illegal.*
2. *It will not be an effective measure in reducing the violations of the law. People who want to circumvent it, will find a way.*
3. *It will increase crime, because combining a large amount of private information in a single system increases the risks of this information becoming available to criminals, who will exploit it for financial crimes and identity theft.*
*I kindly ask you to oppose this plan, both publicly and during any discussions in the UK Parliament.*
-*Sincerely yours,*
+*Sincerely yours,*\
*…*
2. [**Find the email address of your MP**](https://members.parliament.uk/members/Commons) and copy it to the email.
diff --git a/docs/FAQ.md b/docs/FAQ.md
index 21cb08417f..eb86d13c2e 100644
--- a/docs/FAQ.md
+++ b/docs/FAQ.md
@@ -82,7 +82,7 @@ When "Incognito Mode” is turned on, your currently chosen profile name and ima
It is quite a complex process, but fortunately all of this happens in the background, so it's simple to use.
-Whenever somebody connects to you via your address, they basically ask your client whether they want to establish connection. After that, you can either agree or disagree.
+Whenever somebody connects to you via your address, they basically ask your client whether they want to establish connection. After that, you can either agree or disagree.\
If interested, please read more: [Addresses and invitations](./guide/making-connections.md).
### How to configure and delete groups?
@@ -97,7 +97,7 @@ Yes! Currently, there are six emojis available. What's more, you can respond to
It's quite simple:
- one checkmark - message is delivered to the relay (the server).
-- two checkmarks - message is delivered to the recipient's device.
+- two checkmarks - message is delivered to the recipient's device.\
"sent" means accepted by the relay for delivery, "delivered" - stored on the recipient device.
Also see: [I do not see the second tick on the messages I sent](#i-do-not-see-the-second-tick-on-the-messages-i-sent)
@@ -178,12 +178,12 @@ becomes: `simplex:/i#E74vSxMwDnEx6DAvRCZmzBeZwwAseJUD/yVTHjaaH_EzL19DG7fvd46Mjry
### How to change servers to my own?
-Add your server in Settings -> Network & servers -> Your servers. If you have multiple profiles, you need to add your server in every profile.
+Add your server in Settings -> Network & servers -> Your servers. If you have multiple profiles, you need to add your server in every profile.\
After that, disable all preset servers. If you have multiple profiles, you must first add your server to all profiles for this option to be available.
To resolve SimpleX public names, if your server does not support it, you may leave preset servers enabled only for resolving the public names.
-Please note: configured servers only determine which servers will be used for the new connections (members/contacts). Previously created connections don't switch automatically. You can manually switch all existing contacts and small/important groups (each member needs to be switched), and then leave and re-join all large groups.
+Please note: configured servers only determine which servers will be used for the new connections (members/contacts). Previously created connections don't switch automatically. You can manually switch all existing contacts and small/important groups (each member needs to be switched), and then leave and re-join all large groups.\
You can switch contacts/members manually to another server by going to their profile and clicking on "Change receiving address", note that the current server must be online for the address change to work.
## Troubleshooting
@@ -420,7 +420,7 @@ SimpleX Chat Ltd is funded by private investors and venture capital. As an open-
Here are some reflections on VC funding being a necessity for a project at this scale, as well as sustainability and profitability for longtime operations: https://www.poberezkin.com/posts/2023-10-31-why-privacy-impossible-without-venture-funding.html
-And another perspective from a team member on the delicate balance of venture-backed and nonprofit structures, and the plans for the SimpleX network protocols to evolve under the stewardship of nonprofit entities in various jurisdictions, so that its continued evolution aligns more closely with the vision of community-driven, independent and transparent governance:
+And another perspective from a team member on the delicate balance of venture-backed and nonprofit structures, and the plans for the SimpleX network protocols to evolve under the stewardship of nonprofit entities in various jurisdictions, so that its continued evolution aligns more closely with the vision of community-driven, independent and transparent governance:\
[https://simplex.chat/blog/20240404-why-i-joined-simplex-chat-esraa-al-shafei.html](../blog/20240404-why-i-joined-simplex-chat-esraa-al-shafei.md).
### What will be the business model?
diff --git a/docs/contributing/CODE.md b/docs/contributing/CODE.md
index fcdbeb4081..7c67fb4a4f 100644
--- a/docs/contributing/CODE.md
+++ b/docs/contributing/CODE.md
@@ -64,6 +64,7 @@ Some files that use CPP language extension cannot be formatted as a whole, so in
- **Never move existing code or sections around** - add new content at appropriate locations without reorganizing existing structure.
- When adding new sections to documents, continue the existing numbering scheme.
- Minimize diff size - prefer small, targeted changes over reorganization.
+- When you change what users see or do in the apps, update the user guide (docs/guide) in the same pull request.
**Code analysis and review:**
- Trace data flows end-to-end: from origin, through storage/parameters, to consumption. Flag values that are discarded and reconstructed from partial data (e.g. extracted from a URI missing original fields) — this is usually a bug.
diff --git a/docs/guide/README.md b/docs/guide/README.md
index fe1546af0a..cb39d81102 100644
--- a/docs/guide/README.md
+++ b/docs/guide/README.md
@@ -3,20 +3,25 @@ title: Quick start
---
# SimpleX Chat User Guide
-The first messaging platform that has no user identifiers of any kind — 100% private by design.
+The first messaging network that has no user identifiers of any kind.
## Contents
- [Quick start](#quick-start) - scroll down this page
+- [Making connections](./making-connections.md)
- [Sending messages](./send-messages.md)
-- [Secret groups](./secret-groups.md)
+- [Groups](./secret-groups.md)
+- [Channels](./channels.md)
- [Channel webpage](./channel-webpage.md)
- [Chat profiles](./chat-profiles.md)
- [Registering a SimpleX name](./register-simplex-name.md)
- [Managing data](./managing-data.md)
+- [Linked devices](./linked-devices.md)
- [Audio & video calls](./audio-video-calls.md)
+- [Notifications](./notifications.md)
- [Privacy & security](./privacy-security.md)
- [App settings](./app-settings.md)
+- [Supporter badges](./supporter-badges.md)
## Quick start
@@ -24,61 +29,98 @@ The first messaging platform that has no user identifiers of any kind — 100% p
After downloading SimpleX Chat via the links at https://simplex.chat:
-- Open the app.
-- Create a new profile by inputting your display name and full name (optional).
-- Tap on "Create".
+- Open the app and tap "Get started". On desktop, click "Create your profile".
+- Create a new profile by inputting your profile name.
+- Tap on "Create profile".
-This profile is local to the device, it is not stored anywhere else
+This profile is stored on your device and shared only with your contacts.
+SimpleX servers cannot see it.
+
+On desktop, the app then asks you to set a database passphrase or to use a
+random one, see [Database passphrase](./managing-data.md#database-passphrase).
+You can also click "Link a mobile" instead of "Create your profile" to use the
+chat profiles of your phone from the computer, see [Linked
+devices](./linked-devices.md).
### Choose notifications mode
-After you have created the chat profile the app will ask you to choose notifications mode.
+After you have created the chat profile, the app shows "Your network". On iOS
+and Android, tap "Setup notifications" there to choose notifications mode.
-
+
Please read their description, as on iOS it is trade-off between convenience and privacy, with instant notifications sharing more meta-data with the servers, and on Android it is a trade-off between notification delays and battery life.
You can read more about [the differences between notifications work on Android and iOS](../../blog/20220404-simplex-chat-instant-notifications.md).
+You can also tap "Setup routers" to choose server operators. Then tap
+"Continue", and "Accept" on the "Network commitments" screen to start using the
+app.
+
+To change notifications mode later, see [Notifications](./notifications.md).
+
### Connect to developers
-When you just start using the app, you can tap "chat with the developers" to ask any questions or just to test the app - feel free to send any test messages.
+When you just start using the app, you can connect to "Ask SimpleX Team" to ask
+any questions or just to test the app - feel free to send any test messages.
-This button will disappear once you make the first connection. To connect to the developers later:
+Each new chat profile has this contact. To connect, tap the pencil button, tap
+"Ask SimpleX Team" in the list of your contacts and choose "Use current profile"
+or "Use new incognito profile". You can also connect to the developers via the
+app settings:
- [Open the app settings](./app-settings.md#opening-the-app-settings).
-- Tap on "Send questions and ideas".
+- Tap on "Help & support", then "Send questions and ideas".
### Join some user groups
There are links to join several [user groups](../../README.md#join-user-groups) on our GitHub page - join them to ask any questions and discuss ideas.
-You can also find some other user-managed groups via online search.
+You can also find groups and channels in [SimpleX
+Directory](https://simplex.chat/directory/), and some other user-managed groups
+via online search.
To join a group via a link:
- Get a link to a SimpleX user group, which you can get from your existing SimpleX conversations or by finding it online.
- Tap on a button in the lower right-hand of the screen.
-- Tap on "Connect via link / QR code" button.
+- Tap on "Scan / Paste link" (iOS), "Paste link / Scan" (Android) or "Paste
+ link" (desktop), and paste the link, or scan its QR code on iOS and Android.
+- Tap on "Open group", then "Join group".
### Connect to friends
- Tell your friends to download SimpleX Chat app via one of the links here: https://simplex.chat
-- Create a one-time link for each of you friends by tapping new chat button (Android: a floating pencil button in the bottom right corner, iOS: a pencil button in the top right corner)
-- Tap _Create one-time invitation link_.
+- Create a one-time link for each of your friends by tapping the pencil button
+ (in the bottom right corner, or in the top right corner on iOS when the
+ toolbar is at the top).
+- Tap "Create 1-time link".
- Share each link with a different friend, or alternatively let them scan a QR code.
-- You can optionally assign a different alias to each link, to know when they connect who is who, if they use incognito mode, for example.
-- To use your link or QR code, tell your friends to tap new chat button and then tap on "Connect via link / QR code".
-- They can either scan QR code or paste the link. If they opened the link in the browser, they need to tap "Open in mobile app" button on the page.
+- You can optionally set a different contact name for each link, to know when
+ they connect who is who, if they use incognito mode, for example: swipe the
+ pending connection in the chat list and tap "Name" (iOS), or long-press it
+ (Android) or right-click it (desktop) and choose "Set contact name".
+- To use your link or QR code, tell your friends to tap the pencil button and
+ then tap on "Scan / Paste link" (iOS), "Paste link / Scan" (Android) or "Paste
+ link" (desktop).
+- They can either scan QR code (iOS and Android) or paste the link, then tap
+ "Open new chat" and "Connect", see [Connect via a link or a
+ name](./making-connections.md#connect-via-a-link-or-a-name). If they opened
+ the link in the browser, they need to tap "Open SimpleX app" button on the
+ page.
-Watch the video on [the website](https://simplex.chat) that shows how it works.
+Watch the [video](https://youtu.be/2iIibhaoMhI) that shows how it works.
You can also create a [long-term contact address](./app-settings.md#your-simplex-contact-address).
### Explore the app
+Tap your profile image in the chat list toolbar to open the menu with your chat
+profiles, your SimpleX address, chat preferences and the app settings.
+
Most app settings are shared for all profiles, but some settings are specific to chat profile.
Read more in [App settings](./app-settings.md) page.
-Many conversation actions are available via long-press or swipe – try them!
+Many conversation actions are available via long-press or swipe (right-click on
+desktop) – try them!
diff --git a/docs/guide/app-settings.md b/docs/guide/app-settings.md
index 3a966e18e1..99e2caf7b6 100644
--- a/docs/guide/app-settings.md
+++ b/docs/guide/app-settings.md
@@ -8,18 +8,23 @@ title: App settings
To open app settings:
- Open the app.
-- Tap on your user profile image in the upper left-hand of the screen.
-- If you have more than one profile, tap the current profile again or choose Settings.
+- Tap on your user profile image in the lower left-hand of the screen (in the
+ upper left-hand, if the reachable toolbar is off in [Appearance](#appearance)).
+- Tap **Settings**.
## Your profile settings
-This section is labelled **"You"** in the app settings.
+These settings are in the menu that opens when you tap your user profile image.
+The menu also has **Use from desktop** (iOS and Android) or **Link a mobile**
+(desktop), see [Linked devices](./linked-devices.md).
### Your active profile
Tap on your avatar/name to update your current profile names and avatar.
-Display name cannot have any spaces and it is recommended to use latin characters and numbers to make it easier to type these names to the users who use [SimpleX Chat for the terminal](../CLI.md) (CLI).
+Display name can have spaces, but it is recommended to use latin characters and
+numbers to make it easier to type these names to the users who use [SimpleX Chat
+for the terminal](../CLI.md) (CLI).
**Please note**: When you save your profile, the update will be sent to all your contacts (excluding the contacts with whom your incognito profiles were shared). If you have a large number of contacts it can take several seconds.
@@ -31,13 +36,17 @@ This page allows adding and configuring your chat profiles. Please see [Your cha
This feature is unique to SimpleX Chat – it is independent from chat profiles.
-When "Incognito” is turned on, your current profile name and image are NOT shared with your new contacts. It allows connections with other people without any shared data – when you make new connections or join groups via a link a new random profile name will be generated for each contact or group.
+When you choose "Incognito" for a new connection, your current profile name and
+image are not shared with this contact or group. It allows connections with
+other people without any shared data – when you make new connections or join
+groups via a link a new random profile name will be generated for each contact
+or group.
-Please read more in [this post](../../blog/20220901-simplex-chat-v3.2-incognito-mode.md#incognito-mode).
+Please read more in [Incognito mode](./chat-profiles.md#incognito-mode).
### Your SimpleX contact address
-
+
This page allows you to create a long term address that can be used by other people to connect with you. Unlike 1-time invitation links, these addresses can be used many times, that makes them good to share online, e.g. on other social media platforms.
@@ -47,8 +56,6 @@ If you start receiving too many requests via this address it is always safe to r
See the comparison with [1-time invitation links](./making-connections.md#comparison-of-1-time-invitation-links-and-simplex-contact-addresses).
-Read more in [this post](../../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md#auto-accept-contact-requests).
-
### Chat preferences
This page allows to configure the chat preferences that would apply to all contacts - they can be changed separately for each contact:
@@ -56,52 +63,78 @@ This page allows to configure the chat preferences that would apply to all conta
- _disappearing messages_ - to allow disappearing messages in the conversations with you, only if your contacts allow them.
- _delete for everyone_ - to allow irreversible message deletion (by default messages are marked as deleted, not fully deleted). "Yes" setting would allow it only if they allow it for you, and "Always" - even if they don't allow.
- _voice messages_ - to allow sending voice messages.
+- _message reactions_ - to allow message reactions.
+- _audio/video calls_ - to allow calls.
-To set chat preferences in each contact, tap contact name on top of the conversation and then choose "Contact preferences".
+To set chat preferences in each contact, tap contact name in the chat toolbar
+and then choose "Contact preferences".
-Group owners can set similar preferences for their groups, when the group is created or later: tap group name on top of the conversation and then choose "Group preferences".
+Group owners can set similar preferences for their groups, when the group is
+created or later: tap group name in the chat toolbar and then choose "Group
+preferences".
## Your app settings
-This section is labelled **"Settings"** in the app settings.
+Tap **Settings** in the menu to open these settings, see [Opening the app
+settings](#opening-the-app-settings).
+
+
+
+**Migrate to another device** is described in [Managing
+data](./managing-data.md#migrate-to-another-device), and **Audio & video calls**
+in [Audio & video calls](./audio-video-calls.md).
### Notifications
-This page allows to configure the notifications mode: instant, periodic or only when the app is running. There are some differences in how these options work on iOS and Android. Please read more in [this post](../../blog/20220404-simplex-chat-instant-notifications.md) about how notifications work.
+This page (iOS and Android) allows to configure the notifications mode: instant,
+periodic or only when the app is running. On iOS, the third mode is **No push
+server**: the app checks messages in the background when iOS allows it. There
+are some differences in how these options work on iOS and Android, see
+[Notifications](./notifications.md). Please read more in [this
+post](../../blog/20220404-simplex-chat-instant-notifications.md) about how
+notifications work.
-You can also configure whether the message preview is shown when the message arrives.
+You can also configure whether the message preview is shown when the message
+arrives. To do it, tap **Your privacy**, then **More privacy** and **Show
+preview**.
-**Please note**: For instant and periodic notifications to work on Android, you need to disable power optimization when prompted by the app, or later via the settings. Also, some variants of Android system require additional settings, for example MIU system on Xiaomi phones requires to enable "Auto start" for the app for the notification service to work. Please read [Don't kill my app](https://dontkillmyapp.com/) guide for any settings that may be required on your device.
+**Please note**: For instant and periodic notifications to work on Android, you need to disable power optimization when prompted by the app, or later via the settings. Also, some variants of Android system require additional settings, for example MIU system on Xiaomi phones requires to enable "Autostart" for the app for the notification service to work. Please read [Don't kill my app](https://dontkillmyapp.com/) guide for any settings that may be required on your device.
-Also, currently instant notifications have the highest battery consumption - we are working to reduce it to be less or the same as for periodic notifications.
+Also, on Android instant notifications have the highest battery consumption.
### Network & servers
-This page allows to configure your own SMP relays and change other network settings.
-
-
+This page allows to configure server operators and your own servers, and to
+change other network settings.
#### SMP servers
-
-
-By default, the app has preset relays configured – you can change them to your own.
+By default, the app uses the servers of two preset operators, SimpleX Chat and
+Flux. You can disable them and add your own servers in **Your servers**. When
+more than one operator is enabled, the app uses different operators in each
+conversation to protect your privacy.
This page also allows testing the connection with the servers.
-Read more in [this post](../../blog/20221206-simplex-chat-v4.3-voice-messages.md#smp-servers-configuration-and-password).
+Read how to host your own servers in [this guide](../SERVER.md).
-#### Use SOCKS proxy (Android only)
+#### Use SOCKS proxy
This option makes the app connect via a SOCKS proxy that should be provided by some other app running on your device.
The most typical use for this is to run Orbot app that provides a SOCKS proxy to connect via Tor network, but it can be some other app and it can proxy connections via some other overlay network.
+The option is available on all platforms. On Android and desktop, it is in
+**Network & servers**, and the default port is 9050. On iOS, it is in **Advanced
+network settings**, where you enter the IP address and the port of the proxy.
+
#### Use .onion hosts
##### Android
-**Use .onion hosts** option is only available when **Use SOCKS proxy** is enabled. You can choose:
+On Android and desktop, **Use .onion hosts** option is in **SOCKS proxy
+settings**, and it is only available when **Use SOCKS proxy** is enabled. You
+can choose:
- _no_: never use .onion hosts. Choose this option if your SOCKS proxy does not connect via Tor network.
- _when available_ (default): when SOCKS proxy is enabled the app assumes that it provides connections via Tor network and use .onion host addresses when SMP relays include them in their configuration.
@@ -109,107 +142,154 @@ The most typical use for this is to run Orbot app that provides a SOCKS proxy to
##### iOS
-
-
-While iOS does not support SOCKS proxies, you can install Orbot app that works as VPN provider. You can choose:
+On iOS, this option is in **Advanced network settings**. You can install Orbot
+app that works as VPN provider. You can choose:
- _no_ (default): do not use .onion hosts. Choose this option if you do not use Orbot or if you use VPN that proxies connections via some other overlay network.
- _when available_: use .onion host addresses when SMP relays include them in their configuration. Orbot VPN must be enabled for this option to work.
- _required_: always use .onion hosts. Choose this option if you use Orbot VPN and you want to avoid connections without Tor. In this case, if the SMP relay address does not include .onion host, the connection will fail. If you use this option, you can enable "Disable Orbot for non-onion traffic" option in Orbot settings making the rest of your device traffic to not use Tor.
-**Please note** VPN on iOS can send some traffic to the usual network, if, for example, VPN app crashes. You can configure enable always-on VPN mode on managed iOS devices, but this is not applicable to most individual devices.
+**Please note** VPN on iOS can send some traffic to the usual network, if, for
+example, VPN app crashes. You can enable always-on VPN mode on managed iOS
+devices, but this is not applicable to most individual devices.
#### Transport isolation (BETA)
-This option is only available if you have enabled developer tools.
+This option sets how the app isolates your traffic. With **Chat profile**, **App
+session** (default) and **Server**, the app uses a separate TCP connection for
+each chat profile. On Android and desktop, **App session** also uses new SOCKS
+credentials every time you start the app, and **Server** uses them for each
+server. With **Connection**, the app uses a separate TCP connection for each
+contact and group member.
-Please read the details in [this post](../../blog/20230204-simplex-chat-v4-5-user-chat-profiles.md#transport-isolation).
+The option is in **Advanced network settings**. On iOS, it is available after
+you enable [developer options](#show-developer-options). On Android and desktop,
+the **Connection** mode is available with developer options.
#### Advanced network settings
-
+
-If your network is slow, and connections to the servers keep failing (you would see a spinner on your contacts), please increase TCP and protocol timeouts in this page.
+If your network is slow, and connections to the servers keep failing (the server
+status icon in the chat list toolbar shows it), please increase TCP and protocol
+timeouts in this page.
+
+This page also has the settings of private message routing: "To protect your IP
+address, private routing uses your SMP servers to deliver messages." By default,
+**Private routing** is set to always.
### Privacy and security
+This page is labelled **Your privacy** in the app settings.
+
#### SimpleX Lock
-SimpleX Lock when enabled requires to pass the device authentication when you open the app or use some security- or privacy-sensitive features.
+SimpleX Lock when enabled requires to pass the device authentication or to enter
+the app passcode when you open the app or use some security- or
+privacy-sensitive features. You choose it in **Lock mode** (iOS and Android);
+the desktop app uses the app passcode.
-You will be offered to enable it after you open the app a few times.
+You will be offered to enable it once, when you have more than 2 chats (iOS) or
+more than 3 chats (Android and desktop).
To enable it later:
- [Open the app settings](#opening-the-app-settings).
-- Tap on "Privacy & security".
-- Toggle on "SimpleX Lock" slider.
-- Confirm your phone credentials.
+- Tap on "Your privacy".
+- Tap on "SimpleX Lock" and toggle on "Enable lock".
+- Confirm your phone credentials, or set the app passcode.
-Done! Now you will be required to authenticate when you start or resume the app after 30 seconds in background.
+Done! Now you will be required to authenticate when you start or resume the app
+after 30 seconds in background. You can change this time in **Lock after**.
#### Protect app screen
-
-
-This option hides the app screen in recent apps - it is enabled by default. On Android it also prevents screenshots.
+This option hides the app screen in recent apps on iOS and Android - it is
+enabled by default on Android. On Android it also prevents screenshots.
#### Auto Accept Images
-Auto-accepting images may reduce privacy – your contacts will know you are online.
+With this option, the app receives images, small videos and voice messages
+without asking – it is enabled by default.
- [Open the app settings](#opening-the-app-settings).
-- Tap on "Privacy & security".
-- Toggle on "Auto-accept images".
+- Tap on "Your privacy".
+- Toggle "Auto-accept images".
#### Send Link Previews
-Enabling sending link previews may reduce privacy – your app will be loading link preview from the website.
+Enabling sending link previews may reduce privacy – your app will be loading
+link preview from the website. The first time you type a link, the app asks
+**Enable link previews?**
- [Open the app settings](#opening-the-app-settings).
-- Tap on "Privacy & security".
+- Tap on "Your privacy".
- Toggle on "Send link previews".
#### SimpleX Links
+This option is in [Developer tools](#developer-tools), in the section
+**Deprecated options**.
+
This option affects how the links to connect to other SimpleX users or to join groups are shown in the conversations. You can choose between:
- _description_ (default): only the link description and server host name are shown. The link won't be opened in the browser.
- _full link_: the full link is shown. The link will still not be opened in the browser.
-- _via browser_: the full link is shown, and it will be opened in the browser. In this case, if the link domain is different from simplex.chat, the link will be shown in red color, as it might be malicious.
-
-Read more about [SimpleX Links security](../../blog/20221206-simplex-chat-v4.3-voice-messages.md#privacy-and-security-of-simplex-invitation-links).
### Appearance
This page allows configuring:
- interface language
-- app icon
+- app icon (iOS and Android)
- accent color
+- light or dark mode, dark mode colors, wallpaper and other theme colors
+- reachable toolbars at the bottom of the screen
+- chat layout, message shape and profile image shape
+- font size (Android and desktop) and zoom (desktop)
+- **Close to tray** (desktop): the app keeps running in the system tray to
+ receive messages after you close its window.
### Database passphrase & export
-This page allows to change the database passphrase, export and import the database and configure the message retention period.
+This page is labelled **Chat data** in the app settings. It allows to change the
+database passphrase, export and import the database and configure the message
+retention period.
-Read more in [Managing Your Data](./managing-data.md) page.
+Read more in [Managing data](./managing-data.md) page.
## Help & feedback
+In the app settings, it is labelled **Help & support**.
+
This section has information on how to use the app and the links to connect to the team. Please use [Send questions and ideas](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion) to connect to us via the chat to ask any questions, make any suggestions and report any issues.
## Support SimpleX Chat
+Tap **Support SimpleX** in the app settings to get a supporter badge, see
+[Supporter badges](./supporter-badges.md).
+
+These links are in **Help & support**, in the section **Support the project**:
+
- contribute - the link to the information about how to contribute and to donate to the project.
- rate the app - rate and review it on the App Store or Play Store - your feedback helps a lot.
- star on GitHub - that also helps us grow very much.
+The app settings also have **Crowdfunding on Wefunder**, in the section **Own a
+part of SimpleX Chat**: it opens **Invest in SimpleX Chat**, with a link to the
+SimpleX Chat crowdfunding on Wefunder. It is shown in the iOS app and in the
+Android app from Google Play when the store country is the United States or is
+not known, and in the desktop app and other Android builds in every country.
+
Thank you for your support!
## Developer tools
This page has options that normally are only needed for app developers and can be used to debug the app in case something is not working.
+To open it, tap the app version at the end of **Advanced settings** in the app
+settings, then **Developer** (iOS) or **Developer tools** (Android and desktop).
+
### Chat console
Here you can see and use console command with the chat core. None of this information is sent over the network, this is an internal communication between different parts of the app.
@@ -220,8 +300,16 @@ Please be careful - some of the commands can disrupt app functioning, only use t
### Confirm database upgrades
+This option is shown when [Show developer options](#show-developer-options) is
+enabled.
+
This option is off by default - the app database is migrated to the new version without any confirmations. Starting from v4.6.2 these migrations are reversible - you can rollback to the previous version(s) of the app (not earlier than v4.6.1). If you want to be asked a confirmation every time database is upgraded you may enable this option - this is not recommended, as it happens almost on every app update, and should not cause any concerns.
### Show developer options
-This option currently enables [Transport isolation mode](#transport-isolation-beta) option and also enables showing database IDs in contacts, groups and group members to simplify debugging via console commands.
+This option currently enables [Transport isolation
+mode](#transport-isolation-beta) option (on Android and desktop, its
+**Connection** mode) and also enables showing database IDs in contacts, groups
+and group members to simplify debugging via console commands. It also shows the
+section **Developer options**, with [Confirm database
+upgrades](#confirm-database-upgrades) and other options.
diff --git a/docs/guide/audio-video-calls.md b/docs/guide/audio-video-calls.md
index 8fc2bf95b2..914b563cee 100644
--- a/docs/guide/audio-video-calls.md
+++ b/docs/guide/audio-video-calls.md
@@ -1,21 +1,29 @@
---
-title: Audio & video Calls
+title: Audio & video calls
---
# Audio and Video Calls
SimpleX Chat allows you to make end-to-end encrypted audio and video calls with your contacts via WebRTC. Note: Group calls are not supported at this time.
+A call is end-to-end encrypted when both devices support it, and the call
+screen shows its encryption status.
+
## Making and accepting the calls
### How to make an audio call
1. Tap on a contact.
-2. Tap on the phone icon to the right of your contact's name at the top of the screen.
+2. Tap on the phone icon to the right of your contact's name in the chat toolbar,
+ and choose **Audio call**. The chat toolbar is at the top of the screen on
+ iOS, and at the bottom on Android and desktop by default.
+
+The phone icon is shown when the contact is connected and the **Audio/video
+calls** preference allows calls with this contact. Calls are allowed by default.
### How to make a video call
1. Tap on a contact.
-2. Tap on the three vertical dots on the top right-hand corner of the screen to access more options.
+2. Tap on the phone icon to the right of your contact's name.
3. Choose **Video call**.
### Accepting the calls
@@ -24,7 +32,9 @@ When there is an incoming call you have three options:
- accept: to connect the call
- reject: to reject the call, _without_ notifying the caller.
-- ignore: to temporarily dismiss the call, but in a way that it can be accepted later, if the caller is still waiting, via the **Accept call** message in the conversation with this contact.
+- ignore: to temporarily dismiss the call, but in a way that it can be accepted
+ later, if the caller is still waiting, via the **Answer call** message in the
+ conversation with this contact.
There is no time limit that the call invitation can remain active - as long as the caller is still waiting, you can accept the call any time later.
@@ -35,7 +45,7 @@ The call can be accepted from the lock screen, both on Android (it needs to be e
SimpleX Chat shows an incoming call on your device's lock screen by default. However, you can change this behavior in the app settings menu.
1. Open the app settings menu.
-2. Tap **Audio and Video calls**.
+2. Tap **Audio & video calls**.
3. On the **Calls on lock screen** drop-down list, choose from the following three options:
- Disable - the call will show as a notification.
- Show - the call will show on the lock screen, you need to unlock the device and the app to accept it.
@@ -45,41 +55,56 @@ SimpleX Chat shows an incoming call on your device's lock screen by default. How
### Calls on lock screen on iOS
-
-
By default, SimpleX Chat uses native iOS call interface, where allowed, to show incoming calls on the lock screen. You can disable it:
1. Open the app settings menu.
-2. Tap **Audio and Video calls**.
+2. Tap **Audio & video calls**.
3. Toggle the **Use iOS call interface** switch off.
**Please note**: iOS call interface allows accepting the calls without unlocking the device and the app. If this is undesirable, please disable it – the calls will show as notifications in this case.
-Read more in [this post](../../blog/20230328-simplex-chat-v4-6-hidden-profiles.md#improved-audiovideo-calls).
+### During a call
+
+On iOS and Android, the buttons on the call screen turn the microphone and the
+camera on and off, switch the speaker, flip the camera and end the call. To use
+the app during a call, tap the back button on the call screen. Tap the green bar
+at the top of the app to return to the call.
+
+On desktop, the call opens in a tab of your default web browser. Closing the tab
+ends the call.
## Advanced call settings
### WebRTC ICE servers
-
-
SimpleX Chat uses a preset relay server to hide your IP address from your contacts by default, but it can also observe the duration of your calls. If you don't want that, you can configure and use your self-hosted WebRTC relay servers instead for further control of your calls.
1. Open the app settings menu.
-2. Tap **Audio and Video calls**.
+2. Tap **Audio & video calls**.
3. Tap **WebRTC ICE servers**.
4. Toggle the **Configure ICE servers** switch on.
5. Enter your ICE server addresses (one per line).
6. Tap **Save**.
-**Please note**: unlike messaging relays (SMP servers), the configuration of WebRTC ICE servers is stored on the current device, not in the chat database. if you transfer the chat database to another device you need to update this configuration.
+**Please note**: the configuration of WebRTC ICE servers is saved with the other
+app settings in the exported chat database, and it is restored when you import
+the database or migrate to another device.
### Always use relay
Audio and video calls on SimpleX Chat are routed via a TURN relay server by default. Optionally you can disable this and use peer-to-peer (P2P) instead, when it is supported by your network. However, your IP address will be known to your contacts.
1. Open the app settings menu.
-2. Tap **Audio and Video calls**.
+2. Tap **Audio & video calls**.
3. Toggle the **Always use relay** switch on to use a relay server or off for P2P.
-**Please note**: disabling this option allows P2P calls, but it does not prohibit the use of TURN relays – in case your network providers block P2P connections, the call will still use relays if the are available. To prohibit the use of relays you need to change WebRTC ICE server configuration to only include STUN servers, for example:
+**Please note**: disabling this option allows P2P calls, but it does not prohibit
+the use of TURN relays – in case your network providers block P2P connections,
+the call will still use relays if they are available. To prohibit the use of
+relays you need to change WebRTC ICE server configuration to only include STUN
+servers, for example:
+
+```
+stuns:stun.simplex.im:443
+stun:stun.simplex.im:443
+```
diff --git a/docs/guide/channel-webpage.md b/docs/guide/channel-webpage.md
index 8cb0f4fbe5..df6c1bb077 100644
--- a/docs/guide/channel-webpage.md
+++ b/docs/guide/channel-webpage.md
@@ -5,21 +5,31 @@ title: Channel webpage
A channel webpage shows a preview of your channel on the web: its name, description, recent messages and subscriber count. Visitors can see what the channel is about before they subscribe, and the page gives them a "Join" button along with links to download the app.
-You don't have to build the preview yourself. The chat relays that host your channel publish its content as a small file, and a ready-made script renders it on your page. All it takes is to copy the code the app generates and paste it into a web page you host.
+The chat relays of your channel publish its content as a small file, and a
+ready-made script renders it on your page. To set it up, copy the code the app
+generates and paste it into a web page you host.
## What you need
-A channel webpage can be set up by the **owner** of the channel, as long as the channel is hosted on chat relays that support webpages. If they don't, the app shows "Used chat relays do not support webpages." and no code is generated.
+A channel webpage can be set up by the **owner** of the channel, as long as the
+channel's chat relays support webpages. If they don't, the app shows "Used chat
+relays do not support webpages." and no code is generated.
-You'll also need somewhere to publish an HTML page. Your own site works, but any static hosting will do.
+You'll also need somewhere to publish an HTML page: your own site or any static
+hosting.
## Step 1. Open the channel webpage settings
-Open the channel and tap its name at the top to open the channel information. Scroll down to **Advanced options** and tap **Channel webpage**. This button is only shown to channel owners.
+Open the channel and tap its name in the chat toolbar to open the channel
+information. The chat toolbar is at the top on iOS, and at the bottom on Android
+and desktop by default. Scroll down to **Advanced options** and tap **Channel
+webpage**. This button is only shown to channel owners.
## Step 2. Allow embedding while you build the page
-Turn on **Allow anyone to embed** and tap **Save**.
+Turn on **Allow anyone to embed** and tap **Save and notify subscribers** (iOS)
+or **Save** (Android and desktop). On Android and desktop, the app closes the
+screen after **Save**: tap **Channel webpage** again for the next step.
With this on, the relay serves your channel preview to any page, so you can build and test from wherever the page lives without it being tied to one domain yet. Leave the webpage URL empty for now; nothing is shown to your subscribers until you set it.
@@ -77,7 +87,7 @@ Once the page works, go back to **Channel webpage** in the app:
- Under **Enter webpage URL**, type the address where the page is published, for example `https://example.com/my-channel`.
- Turn **Allow anyone to embed** off if you don't want other sites to be able to show your channel preview. Leave it on if you're happy for anyone to embed it.
-- Tap **Save**.
+- Tap **Save and notify subscribers** (iOS) or **Save** (Android and desktop).
The URL now appears as a link in your channel info that every subscriber can see. If you turned embedding off, the relay also restricts the preview to your own domain.
@@ -95,6 +105,7 @@ You can add optional `data-*` attributes to the `
+
+1. Tap the pencil button in the chat list, then tap **Create public channel**.
+2. Tap the picture to add a channel image, and enter the channel name.
+3. Tap **Create public channel**.
+
+**Configure relays** opens **Network & servers**, where you choose the chat
+relays that the app can use. If no chat relay is enabled, **Configure relays**
+is shown in orange, and the app asks you to enable at least one chat relay.
+
+The channel is created with your current chat profile, which is shared with the
+chat relays and subscribers. The app picks up to 3 of your enabled chat relays
+at random, preferring one of your own relays and relays of different server
+operators.
+
+On the **Creating channel** screen, the app shows how many relays are active,
+for example "2/3 relays active", and moves on when all of them are active. To
+move on earlier, tap **Continue** when at least one relay is active. To delete
+the new channel, tap **Cancel and delete channel**.
+
+Then the app shows the channel link and its QR code on the **Channel link**
+screen. Share the link now or later, and tap **Continue** to open the channel.
+On desktop, the app opens the channel and shows the channel link next to it.
+
+New channels have **Visible history** on and **Chat with admins** off. See
+[Channel preferences](#channel-preferences).
+
+## Subscribe to a channel
+
+1. Open the channel link: tap it in a chat, paste it into the search field of
+ the chat list, or scan its QR code. To use the channel's SimpleX name, type
+ it into the search field and tap **Connect to …**. See [Connect via a link or
+ a name](./making-connections.md#connect-via-a-link-or-a-name).
+2. The app shows the channel's name, image and number of subscribers. Tap **Open
+ channel**.
+3. The app opens the channel with its welcome message, if it has one. The chat
+ relays, the owner and moderators see the profile you join with. To join with
+ another chat profile or incognito, choose it in **Your profile** above the
+ button. See [Incognito mode](./chat-profiles.md#incognito-mode).
+4. Tap **Join channel**.
+
+The app connects to each of the channel's relays through messaging servers, and
+you are subscribed when at least one relay connects. Until all relays connect,
+the app shows the progress in a bar above the message field, for example "1/2
+relays connected".
+
+A channel card shared by the owner is marked "Channel link (from owner)". When
+you open such a card, the app shows "Link signature verified." in the channel
+preview.
+
+When a channel has no active relays, the app shows "Channel has no active
+relays. Please try to join later."
+
+As a subscriber, you read the channel, and "you are subscriber" is shown in the
+message field. You can react to messages when **Message reactions** is on, and
+use **Chat with admins** when the owner turns it on.
+
+## Post in a channel
+
+As the owner, you post as the channel, and **Broadcast** is shown in the empty
+message field. Subscribers see your posts with the channel's name and image,
+marked "channel".
+
+Contributors and moderators post with their own chat profiles, and subscribers
+see their names.
+
+Writing and sending messages is described in [Sending
+messages](./send-messages.md#sending-messages).
+
+The owner and moderators have **Delete from history** (iOS) or **From history**
+(Android and desktop) in place of **Delete for me**. It deletes the message on
+your device. For a message that you posted, the chat relays also delete it from
+the channel history that they keep: new subscribers do not receive it, and the
+channel webpage does not show it. Subscribers who received the message earlier
+keep it.
+
+## Channel link and name
+
+Anyone with the channel link can join the channel.
+
+- As the owner, tap **Channel link** in the chat info. The app shows the QR
+ code, **Share link** and **Share via chat**. Everyone who joins by the link
+ becomes a subscriber, and the link cannot be deleted.
+- Other members see the QR code, **Share link** and **Share via chat** in the
+ chat info.
+
+With **Share via chat**, you choose a chat, and the app adds a channel card to
+your message there. Recipients tap the card to open the channel.
+
+To let people find the channel by a name, the owner taps **Get SimpleX name
+(BETA)** in the chat info, below the channel link. See [Registering a SimpleX
+name](./register-simplex-name.md).
+
+To show a preview of the channel on a website, see [Channel
+webpage](./channel-webpage.md).
+
+## Chat relays
+
+Chat relays forward channel messages to subscribers. The SimpleX Chat server
+operator provides preset relays, and you can add your own.
+
+Tap **Chat relays** in the channel's chat info to see the relays and their
+status. The owner sees each relay's state in the channel, for example
+**active**. Other members see their connection to each relay, for example
+**connected** or **connecting**.
+
+The owner can change the relays:
+
+- To add relays, tap **Add relay**, select relays, and tap **Add** (iOS) or
+ **Add relays** (Android and desktop). You can select from your enabled chat
+ relays that are not in the channel yet.
+- To remove a relay, swipe it left (iOS), long-press it (Android) or right-click
+ it (desktop), and tap **Remove relay**. **Remove relay** is also in the
+ relay's info. For the last active relay, the app warns: "This is the last
+ active relay. Removing it will prevent message delivery to subscribers."
+
+When some relays are not active, the app shows their number in a bar above the
+message field, for example "2/3 relays active, 1 failed" to the owner or "1/2
+relays connected" to subscribers. Tap the bar to see each relay. When no relay
+works, the app shows "Add relays to restore message delivery." to the owner and
+"Waiting for channel owner to add relays." to other members in the opened bar,
+and "can't broadcast" in the message field of members who can post.
+
+The relays that the app can use are in **Network & servers**: in the **Chat
+relays** section of a server operator, and in **Your servers**. To add your own
+relay, open **Your servers**, tap **Add server**, then **Chat relay**, paste the
+relay address, tap **Test relay**, and save with **Save servers**. A relay is
+turned on or off with **Use for new channels**. See [Network &
+servers](./app-settings.md#smp-servers).
+
+## Subscribers and roles
+
+| Role | What the member can do |
+|---|---|
+| Subscriber | Reads posts. Reacts when **Message reactions** is on. Uses **Chat with admins** when it is on. |
+| Contributor | Also posts messages with their own name. |
+| Moderator | Also deletes messages of contributors, blocks subscribers and contributors for all, and answers subscribers in **Chats with members**. |
+| Owner | Posts as the channel. Edits the channel profile, welcome message and preferences. Manages relays and roles. Removes subscribers. Deletes the channel. |
+
+The owner and moderators see the members in **Subscribers** in the chat info.
+Other members see **Owners & contributors**.
+
+The owner can change a member's role to subscriber, contributor or moderator:
+
+1. In the chat info, tap **Subscribers**, then tap the member.
+2. Choose the new role in **Change role**.
+3. Tap **Change** to confirm. All subscribers are notified.
+
+A channel has one owner, the member who created it. The owner role cannot be
+given to other members. A channel can have up to 256 contributors and
+moderators.
+
+In the member's info, moderators and the owner can **Block for all**, and the
+owner can **Remove subscriber**.
+
+## Message signing
+
+When the owner turns on **Sign messages** in [Channel
+preferences](#channel-preferences), the apps of all members who post sign every
+message, and subscribers' apps verify the signatures. New subscribers see
+earlier messages as unsigned.
+
+A seal icon is shown next to the time of a signed and verified message. Its
+**Info** has the line "Signed & verified", or "Signed" for your own messages.
+
+While signing is on, a red seal icon is shown on a message without a signature.
+Such a message was sent before the channel started signing, or it may be fake.
+
+A message with an invalid signature, or an unsigned edit or deletion of a signed
+message, is rejected, and "message rejected: bad signature" is shown in the
+chat.
+
+## Channel preferences
+
+The owner changes channel preferences. Tap **Channel preferences** in the
+chat info, change them, and tap **Save and notify subscribers** (iOS) or **Save
+and notify channel subscribers** (Android and desktop). Other members see the
+preferences without the switches.
+
+| Preference | When it is on | New channels |
+|---|---|---|
+| **Sign messages** | Messages are signed: see [Message signing](#message-signing). | off |
+| **Disappearing messages** | New messages are deleted after the time set in **Delete after**. | off |
+| **Delete for everyone** | Messages deleted by their senders are deleted for all subscribers. | off |
+| **Message reactions** | All members, including subscribers, can react to messages. | on |
+| **Visible history** | Up to 100 last messages are sent to new subscribers. | on |
+| **Chat with admins** | Subscribers and contributors can chat with the owner and moderators. | off |
+
+Chats with admins in channels are not end-to-end encrypted, so turn them on only
+with chat relays you trust.
+
+The owner also edits the channel's name, image and short description with
+**Edit channel profile**, and the message for new subscribers with **Add welcome
+message** (**Welcome message** once it is added). Profile changes are sent to
+subscribers.
+
+## Leave or delete a channel
+
+- As a subscriber, contributor or moderator, tap **Leave channel** in the chat
+ info to leave. You stop receiving messages from the channel, and the chat
+ history stays on your device. After that, tap **Delete channel** to delete it
+ for you.
+- The owner can delete the channel for all subscribers with **Delete channel**.
+ This cannot be undone.
+
+See also [Leaving a group](./secret-groups.md#leaving-a-group).
diff --git a/docs/guide/chat-profiles.md b/docs/guide/chat-profiles.md
index d20790037a..5f405c76d8 100644
--- a/docs/guide/chat-profiles.md
+++ b/docs/guide/chat-profiles.md
@@ -1,79 +1,103 @@
---
title: Chat profiles
---
-# Your chat profiles
+# Chat profiles
## Creating additional chat profiles
-SimpleX Chat allows creating as many chat profiles as you like. Same as the first profile, they are only stored locally on your device.
-
-
+SimpleX Chat allows creating as many chat profiles as you like. Same as the first
+profile, they are stored on your device and shared only with your contacts. You
+can use different profiles, for example for family and work, or one with a
+pseudonym. All of them receive messages at the same time.
To create an additional SimpleX Chat profile:
-- [Open the app settings](./app-settings.md#opening-the-app-settings).
+- Tap your profile image in the chat list toolbar to open the profile menu.
- Select "Your chat profiles".
-- Unlock option via fingerprint or PIN.
-- Tap on "+ Add profile”.
-- Create a new profile by inputting your display name and full name (optional).
-- Tap on "Create".
+- Tap on "Add profile".
+- Unlock via fingerprint or PIN, if you have [SimpleX
+ Lock](./app-settings.md#simplex-lock) enabled.
+- Create a new profile by inputting your display name, and optionally a bio and
+ a profile image.
+- Tap on "Create profile".
## Hiding and muting chat profiles
v4.6 added the ability to mute and to hide chat profiles.
-
+
-These actions are available via long-press (Android) or swipe (iOS) on the profile in the list.
+These actions are available via long-press (Android), swipe right (iOS) or
+right-click (desktop) on the profile in the list.
-To unhide hidden profiles, enter the full password in the search bar.
+To show hidden profiles, enter the full password in the search bar of "Your chat
+profiles". Tap the profile to use it, or choose "Unhide" to unhide it.
## Switching between profiles
-
-
-- Tap on your user profile image in the upper right-hand of the screen.
+- Tap on your user profile image in the lower left-hand of the screen (upper
+ left, when the toolbar is at the top).
- Select which profile you want to use.
-You can also switch profile via Your chat profiles in the settings.
+You can also switch profile via "Your chat profiles" in the same menu.
## Incognito mode
-
-
This feature is unique to SimpleX Chat – it is independent from chat profiles.
-When "Incognito Mode” is turned on, your currently chosen profile name and image are hidden from your new contacts. It allows anonymous connections with other people without any shared data – when you make new connections or join groups via a link a new random profile name will be generated for each connection.
+When you choose "Incognito" for a new connection, your currently chosen profile
+name and image are hidden from this contact, group or channel. It allows
+anonymous connections with other people without any shared data – a new random
+profile name will be generated for each connection. When you share an incognito
+profile with somebody, this profile will also be used for the groups they invite
+you to.
-To toggle the incognito mode on/off:
+To choose incognito mode:
-- [Open the app settings](./app-settings.md#opening-the-app-settings).
-- Turn on/off incognito mode by tapping on the switch on "Incognito".
+- When you create a 1-time link, tap your profile under "Share profile" and
+ choose "Incognito".
+- When you connect via a link, tap your profile under "Your profile" in the new
+ chat and choose "Incognito", before you tap "Connect", "Join group" or "Join
+ channel". For some links, the app asks you to choose "Use current profile" or
+ "Use new incognito profile" instead.
+- When you create a group, turn on "Incognito".
-Please read more in [this post](../../blog/20220901-simplex-chat-v3.2-incognito-mode.md#incognito-mode).
+The app remembers your last choice for the next connection.
## Edit your profile
To edit your profile:
-- [Open the app settings](./app-settings.md#opening-the-app-settings).
+- Tap your profile image in the chat list toolbar to open the profile menu.
- Select your profile.
-- Tap on "Edit".
-- Input your desired profile name and/or update your full name.
+- Input your desired profile name and/or update your bio. You can also add or
+ edit a description.
- You can also set and change profile image.
-- Tap on "Save and notify contacts".
+- Tap on "Save (and notify contacts)" (iOS) or "Save and notify contacts"
+ (Android and desktop).
## Move your chat profiles to another device
-SimpleX Chat stores all user data only on client devices using a portable encrypted database format that can be exported and transferred to any supported device.
+SimpleX Chat stores all user data on client devices using a portable encrypted
+database format that can be exported and transferred to any supported device.
+
+You can move all profiles with "Migrate to another device" in the app settings:
+the app uploads the encrypted database, and the new device downloads it after
+you scan a QR code or paste a link, see [Migrate to another
+device](./managing-data.md#migrate-to-another-device). You can also export the
+database to a file and import it on the new device.
To export your SimpleX Chat data:
- [Open the app settings](./app-settings.md#opening-the-app-settings).
-- Select "Database passphrase & export".
+- Select "Chat data", then "Database passphrase & export".
- Stop chat by toggling "Chat is running" (tap "Stop” in the confirmation dialog).
- Confirm it with fingerprint or PIN, in case you have [SimpleX Lock](./app-settings.md#simplex-lock) enabled.
-- If you didn't set it before, [set a passphrase](./managing-data.md#database-passphrase) in "Database passphrase". Initially, the database is encrypted with a random passphrase that is stored in KeyChain (iOS) or with KeyStore (Android).
+- If you didn't set it before, [set a
+ passphrase](./managing-data.md#database-passphrase) in "Database passphrase".
+ Initially, the database is encrypted with a random passphrase that is stored
+ in Keychain (iOS), with Keystore (Android) or in the app settings as plaintext
+ (desktop).
- Tap on "Export database" - it won't allow exporting unless you have set a passphrase.
- Save the file on your device or share it via any available option.
@@ -81,9 +105,11 @@ To import your SimpleX Chat data in the app on another device:
- Transfer the database file to the new device.
- Install SimpleX Chat app.
-- Create chat profile with any name by following the steps in [Create your first chat profile](#create-yout-first-chat-profile) – you will replace it soon.
+- Create chat profile with any name by following the steps in [Create your first
+ chat profile](./README.md#create-your-first-chat-profile) – you will replace
+ it soon.
- Open the app settings
-- Select "Database passphrase & export".
+- Select "Chat data", then "Database passphrase & export".
- Stop chat by tapping the switch on "Chat is running".
- Tap "Import database".
- Select the .zip file of the exported chat data.
@@ -94,6 +120,6 @@ To import your SimpleX Chat data in the app on another device:
1. Currently you cannot move some of the profiles, only the whole database containing all profiles can be moved.
-2. You must NOT use the exported database on more than one device at a time, as it may disrupt some of the connections. You also must always use the latest version of the chat database - using the old one is also likely to disrupt your connections.
+2. You must not use the exported database on more than one device at a time, as it may disrupt some of the connections. You also must always use the latest version of the chat database - using the old one is also likely to disrupt your connections.
3. There is no way to recover lost passphrase - make sure to store it securely.
diff --git a/docs/guide/images/app-settings-advanced-network-settings.png b/docs/guide/images/app-settings-advanced-network-settings.png
new file mode 100644
index 0000000000..54865644b0
Binary files /dev/null and b/docs/guide/images/app-settings-advanced-network-settings.png differ
diff --git a/docs/guide/images/app-settings-settings.png b/docs/guide/images/app-settings-settings.png
new file mode 100644
index 0000000000..46d05c102d
Binary files /dev/null and b/docs/guide/images/app-settings-settings.png differ
diff --git a/docs/guide/images/audio-video-calls-settings.png b/docs/guide/images/audio-video-calls-settings.png
new file mode 100644
index 0000000000..9513999f31
Binary files /dev/null and b/docs/guide/images/audio-video-calls-settings.png differ
diff --git a/docs/guide/images/channels-create.png b/docs/guide/images/channels-create.png
new file mode 100644
index 0000000000..15b72c7e65
Binary files /dev/null and b/docs/guide/images/channels-create.png differ
diff --git a/docs/guide/images/chat-profiles-create-profile.png b/docs/guide/images/chat-profiles-create-profile.png
new file mode 100644
index 0000000000..349089f644
Binary files /dev/null and b/docs/guide/images/chat-profiles-create-profile.png differ
diff --git a/docs/guide/images/chat-profiles-hide-profile.png b/docs/guide/images/chat-profiles-hide-profile.png
new file mode 100644
index 0000000000..8f63e4015c
Binary files /dev/null and b/docs/guide/images/chat-profiles-hide-profile.png differ
diff --git a/docs/guide/images/groups-chat.png b/docs/guide/images/groups-chat.png
new file mode 100644
index 0000000000..ba3da26269
Binary files /dev/null and b/docs/guide/images/groups-chat.png differ
diff --git a/docs/guide/images/groups-create.png b/docs/guide/images/groups-create.png
new file mode 100644
index 0000000000..64ebbf2278
Binary files /dev/null and b/docs/guide/images/groups-create.png differ
diff --git a/docs/guide/images/groups-info.png b/docs/guide/images/groups-info.png
new file mode 100644
index 0000000000..16b21aac99
Binary files /dev/null and b/docs/guide/images/groups-info.png differ
diff --git a/docs/guide/images/groups-member-info.png b/docs/guide/images/groups-member-info.png
new file mode 100644
index 0000000000..ed7a584195
Binary files /dev/null and b/docs/guide/images/groups-member-info.png differ
diff --git a/docs/guide/images/groups-preferences.png b/docs/guide/images/groups-preferences.png
new file mode 100644
index 0000000000..578be46e1f
Binary files /dev/null and b/docs/guide/images/groups-preferences.png differ
diff --git a/docs/guide/images/groups-profile.png b/docs/guide/images/groups-profile.png
new file mode 100644
index 0000000000..ab8c555e8c
Binary files /dev/null and b/docs/guide/images/groups-profile.png differ
diff --git a/docs/guide/images/linked-devices-connect-to-desktop.png b/docs/guide/images/linked-devices-connect-to-desktop.png
new file mode 100644
index 0000000000..86f6f1237f
Binary files /dev/null and b/docs/guide/images/linked-devices-connect-to-desktop.png differ
diff --git a/docs/guide/images/making-connections-address.png b/docs/guide/images/making-connections-address.png
new file mode 100644
index 0000000000..ded2a812e8
Binary files /dev/null and b/docs/guide/images/making-connections-address.png differ
diff --git a/docs/guide/images/making-connections-contact-info.png b/docs/guide/images/making-connections-contact-info.png
new file mode 100644
index 0000000000..328e7c2474
Binary files /dev/null and b/docs/guide/images/making-connections-contact-info.png differ
diff --git a/docs/guide/images/making-connections-video-address.jpg b/docs/guide/images/making-connections-video-address.jpg
new file mode 100644
index 0000000000..2375e906d0
Binary files /dev/null and b/docs/guide/images/making-connections-video-address.jpg differ
diff --git a/docs/guide/images/making-connections-video-invite.jpg b/docs/guide/images/making-connections-video-invite.jpg
new file mode 100644
index 0000000000..c46acf716f
Binary files /dev/null and b/docs/guide/images/making-connections-video-invite.jpg differ
diff --git a/docs/guide/images/making-connections-video-join.jpg b/docs/guide/images/making-connections-video-join.jpg
new file mode 100644
index 0000000000..bcd44c5c72
Binary files /dev/null and b/docs/guide/images/making-connections-video-join.jpg differ
diff --git a/docs/guide/images/managing-data-chat-data.png b/docs/guide/images/managing-data-chat-data.png
new file mode 100644
index 0000000000..aab33d4aa1
Binary files /dev/null and b/docs/guide/images/managing-data-chat-data.png differ
diff --git a/docs/guide/images/managing-data-migrate-here.png b/docs/guide/images/managing-data-migrate-here.png
new file mode 100644
index 0000000000..16bf0c043f
Binary files /dev/null and b/docs/guide/images/managing-data-migrate-here.png differ
diff --git a/docs/guide/images/managing-data-passphrase.png b/docs/guide/images/managing-data-passphrase.png
new file mode 100644
index 0000000000..f6f6952e08
Binary files /dev/null and b/docs/guide/images/managing-data-passphrase.png differ
diff --git a/docs/guide/images/notifications-android-modes.png b/docs/guide/images/notifications-android-modes.png
new file mode 100644
index 0000000000..80bfa4bd3f
Binary files /dev/null and b/docs/guide/images/notifications-android-modes.png differ
diff --git a/docs/guide/images/notifications-ios-modes.png b/docs/guide/images/notifications-ios-modes.png
new file mode 100644
index 0000000000..ecab5b0de2
Binary files /dev/null and b/docs/guide/images/notifications-ios-modes.png differ
diff --git a/docs/guide/images/privacy-security-verify-code.png b/docs/guide/images/privacy-security-verify-code.png
new file mode 100644
index 0000000000..755f0eb9f0
Binary files /dev/null and b/docs/guide/images/privacy-security-verify-code.png differ
diff --git a/docs/guide/images/readme-get-started.png b/docs/guide/images/readme-get-started.png
new file mode 100644
index 0000000000..92e2c697e7
Binary files /dev/null and b/docs/guide/images/readme-get-started.png differ
diff --git a/docs/guide/images/readme-video-get-started.jpg b/docs/guide/images/readme-video-get-started.jpg
new file mode 100644
index 0000000000..b3a60b73a7
Binary files /dev/null and b/docs/guide/images/readme-video-get-started.jpg differ
diff --git a/docs/guide/images/send-messages-chat.png b/docs/guide/images/send-messages-chat.png
new file mode 100644
index 0000000000..de9991626e
Binary files /dev/null and b/docs/guide/images/send-messages-chat.png differ
diff --git a/docs/guide/images/send-messages-contact-preferences.png b/docs/guide/images/send-messages-contact-preferences.png
new file mode 100644
index 0000000000..84883a60fe
Binary files /dev/null and b/docs/guide/images/send-messages-contact-preferences.png differ
diff --git a/docs/guide/images/supporter-badges-redeem-code.png b/docs/guide/images/supporter-badges-redeem-code.png
new file mode 100644
index 0000000000..50f26e6bd9
Binary files /dev/null and b/docs/guide/images/supporter-badges-redeem-code.png differ
diff --git a/docs/guide/images/supporter-badges-support-simplex.png b/docs/guide/images/supporter-badges-support-simplex.png
new file mode 100644
index 0000000000..38fbc92b4a
Binary files /dev/null and b/docs/guide/images/supporter-badges-support-simplex.png differ
diff --git a/docs/guide/linked-devices.md b/docs/guide/linked-devices.md
new file mode 100644
index 0000000000..efecf0c28b
--- /dev/null
+++ b/docs/guide/linked-devices.md
@@ -0,0 +1,152 @@
+---
+title: Linked devices
+---
+# Linked devices
+
+You can use the chat profiles of the SimpleX app on your mobile (iOS or Android)
+from the desktop app. Your profiles, contacts and messages stay on the mobile.
+The desktop app shows them, and the mobile app performs the actions that you
+take on the desktop. The mobile connects to the desktop over the local network,
+without a server, and the connection is protected by quantum resistant
+encryption.
+
+To link the devices:
+
+- The mobile and the desktop must be connected to the same local network, and
+ the desktop firewall must allow the connection.
+- Both apps must have compatible versions. Otherwise the mobile shows **Not
+ compatible!** or **Incompatible version**.
+
+The desktop keeps its own chat profiles, if you created any, and you can switch
+between them and the profiles of the mobile.
+
+## Link a mobile
+
+On the desktop:
+
+1. Open the profile menu and click **Link a mobile** (**Linked mobiles** after
+ the first link).
+2. Optionally, change **This device name**. The mobile will see this name.
+3. Click **Link a mobile** under **Devices**. The desktop shows a QR code.
+
+On a new desktop, the first screen also offers **Link a mobile**, next to
+**Create your profile**, so you can link a mobile without creating a profile on
+the desktop. The desktop can first offer to set a [database
+passphrase](./managing-data.md#database-passphrase), and then it shows the QR
+code.
+
+On the mobile:
+
+4. Open the profile menu and tap **Use from desktop**. The app opens **Connect
+ to desktop**.
+5. Optionally, change **This device name**.
+6. Scan the QR code from the desktop, under **Scan QR code from desktop**.
+7. Compare the session codes: the mobile shows its code under **Verify code with
+ desktop**, and the desktop under **Verify code on mobile**. If the codes are
+ the same, tap **Confirm** on the mobile.
+
+The desktop then shows the chats of the mobile.
+
+
+
+The desktop waits 60 seconds for the mobile to connect, then closes the QR code.
+To get a new QR code, click **Link a mobile** again.
+
+## Session code verification
+
+Each connection between the mobile and the desktop has a session code, and both
+devices show it. When the codes are the same, the mobile and the desktop are in
+the same session.
+
+- When you connect to a new desktop, the mobile always asks you to confirm the
+ code.
+- When you connect to a desktop that you linked before, the mobile confirms the
+ code without asking. To be asked each time, turn on **Verify connections**: it
+ is off by default.
+
+While the devices are connected, the mobile shows the code under **Session
+code**. The desktop shows it under **Verify code on mobile** when you click the
+mobile in **Linked mobiles**.
+
+## Connect again
+
+After the first link, the mobile and the desktop remember each other.
+
+1. On the desktop, open **Linked mobiles** and click the mobile. The desktop
+ shows a QR code (**Scan from mobile**).
+2. On the mobile, open **Use from desktop**:
+ - If **Discover via local network** is on, the mobile looks for linked
+ desktops on the local network ("Waiting for desktop…"). When it finds the
+ desktop, it connects, or it shows **Connect** if **Connect automatically**
+ is off. To scan the QR code instead, tap **Scan QR code**.
+ - Otherwise, scan the QR code.
+
+## Local network discovery
+
+When the desktop shows the QR code for a linked mobile and **Discoverable via
+local network** is on, the desktop also announces itself on the local network.
+The announcement is encrypted for that mobile. A mobile with **Discover via
+local network** on receives it and connects without scanning.
+
+| Setting | Device | Default | What it does |
+|---|---|---|---|
+| **Discoverable via local network** | Desktop | On | The desktop announces itself to linked mobiles |
+| **Discover via local network** | Mobile | On (iOS), off (Android) | The mobile looks for linked desktops when you open **Use from desktop** |
+| **Connect automatically** | Mobile | On | The mobile connects to a found desktop without asking |
+| **Verify connections** | Mobile | Off | The mobile asks you to confirm the session code each time |
+
+On the desktop, **Discoverable via local network** is on the screen **Link a
+mobile** (**Linked mobiles**). On the mobile, the other settings are in **Use
+from desktop** > **Linked desktops** > **Linked desktop options**; **Linked
+desktops** is shown after you link a desktop, and **Connect automatically** is
+shown while **Discover via local network** is on.
+
+## Desktop address and port
+
+Below the QR code, the desktop shows its network address and the port that it
+uses for the connection.
+
+- If the computer has several network interfaces, choose the address from the
+ list.
+- The port is random by default (**Random**). To use a fixed port, enter a
+ number above 1023. If a firewall runs on the desktop, open this port in it so
+ that the mobile can connect.
+
+After you change the address or the port, click **Refresh** to show a new QR
+code. The address and the port that you choose are saved for that mobile.
+
+## While the devices are connected
+
+On the desktop:
+
+- The profile menu shows the name of the mobile and **This device**. Click
+ **This device** to use the profiles of the desktop, and the name of the mobile
+ to return to the profiles of the mobile.
+- Files that you send are copied to the mobile, which sends them. Files in the
+ chats of the mobile are loaded from the mobile when you open them.
+- To disconnect, click the Wi-Fi icon next to the name of the mobile in the
+ profile menu, or click the mobile in **Linked mobiles** and click
+ **Disconnect**.
+
+On the mobile:
+
+- The screen **Connected to desktop** shows the name and version of the desktop,
+ the session code and **Disconnect**.
+- On iOS, keep the app open while you use it from the desktop. The session ends
+ when the app goes to the background, and the screen of the iPhone stays on
+ while the session is active.
+- On Android, closing the screen asks **Disconnect desktop?**, because only one
+ device can work at a time.
+
+The database of the mobile, its passphrase and its network settings are managed
+on the mobile.
+
+## Remove a linked device
+
+- On the desktop, open **Linked mobiles** and right-click the mobile, then click
+ **Delete**. The files of that mobile that the desktop loaded are deleted too.
+ While you use the profiles of the mobile, the menu has **Disconnect** in place
+ of **Delete**.
+- On the mobile, open **Use from desktop** > **Linked desktops**. On iOS, swipe
+ the desktop to the left and confirm **Unlink**. On Android, tap and hold the
+ desktop, then tap **Delete**.
diff --git a/docs/guide/making-connections.md b/docs/guide/making-connections.md
index 2dd1f4bd77..fe7553983f 100644
--- a/docs/guide/making-connections.md
+++ b/docs/guide/making-connections.md
@@ -3,21 +3,39 @@ title: Making connections
---
# Connect to somebody in the chat
-_Work in progress_
+Because you have no identifier on the SimpleX network, nobody can contact you
+unless you share a 1-time link or your SimpleX address, as a QR code or a
+link, or a SimpleX name registered for your address. Members of groups you join
+can also send you contact requests, when direct messages are allowed in the
+group.
-Because you have no identifier on the SimpleX platform, nobody can contact you unless you share a one-time or temporary user address, such as a QR code or a link.
+Your SimpleX address is optional. You can change or delete it without losing any
+of your connections, for example when you receive spam contact requests.
-Even with the optional user address, while it can be used to send spam contact requests, you can change or completely delete it without losing any of your connections.
+Private Connection — connect using a 1-time link, or its QR code shown in person
+or in a video call.
-Private Connection — connect using an invitation link or QR code via video or in-person.
+Group Chat — join a group via its link or an invitation from a contact, or
+create your own group, see [Groups](./secret-groups.md).
-Group Chat — Users have the option to create a secret group, share their contact link [which can be deleted later on], or generate a one-time invitation link.
+Channel — join a channel via its link or its SimpleX name, see
+[Channels](./channels.md).
## Your SimpleX contact address
-You can [create an optional long term address](./app-settings.md#your-simplex-contact-address) for other people to connect with you. Unlike 1-time invitation links, these addresses can be used many times, that makes them good to share online, e.g. on social media platforms, or in email signatures. That helps more people discover SimpleX Chat, so please do it!
+You can [create an optional long term
+address](./app-settings.md#your-simplex-contact-address) for other people to
+connect with you. Unlike 1-time invitation links, these addresses can be used
+many times, that makes them good to share online, e.g. on social media
+platforms, or in email signatures.
-When people connect to you via this address, you will receive a connection request that you can accept or reject. You can configure an automatic acceptance of connection request and an automatic welcome message that will be sent to the new contacts. You can also share this address as part of your SimpleX profile, so group members can connect to you, and your contacts can share it with others - if this is something that you want.
+When people connect to you via this address, you will receive a connection
+request that you can accept or reject. You can configure an automatic acceptance
+of connection request and an automatic welcome message that will be sent to the
+new contacts. You can also share this address as part of your SimpleX profile,
+so group members can connect to you (in the groups that allow direct messages
+and SimpleX links), and your contacts can share it with others - if this is
+something that you want.
If you start receiving too many requests via this address it is always safe to remove it – all the connections you created via this address will remain active, as this address is not used to deliver the messages.
@@ -51,8 +69,8 @@ If you start receiving too many requests via this address it is always safe to r
+
By default, database passphrase is random, you have to set it manually to be able to export it.
+To set your own passphrase, tap **Database passphrase**, enter it in **New
+passphrase…** and **Confirm new passphrase…**, and tap **Update database
+passphrase**. To change a passphrase that you set before, also enter it in
+**Current passphrase…**.
+
+The app saves the passphrase on the device, in iOS Keychain, in Android Keystore,
+or in the app settings as plaintext on desktop. After you set your own
+passphrase, you can turn off **Save passphrase in Keychain** (iOS), **Save
+passphrase in Keystore** (Android) or **Save passphrase in settings** (desktop).
+The app then asks for the passphrase each time it starts, and notifications on
+iOS and Android are limited (see
+[Notifications](./notifications.md#notifications-and-the-database-passphrase)).
+On desktop, a passphrase that you set at the first start is not saved. A lost
+passphrase cannot be recovered.
+
### App data backup
-Here you can allow the system to back up app data, wherever it is configured. A database passphrase must be set to use this.
+The app data is not saved in iOS backups, and Android backup is turned off for
+the app. To keep a copy of your data, [export the database](#export-database)
+regularly: importing an old copy may disrupt your conversations with some
+contacts.
### Export database
-Here you can export the chat database to a file.
+Here you can export the chat database to a file. The file also contains your
+files and the app settings. The app stops the chat to export the database, and
+the chat stays stopped after the export. To start it again, turn on the toggle
+under **Run chat**.
### Import database
-Here you can import the chat database from a file.
+Here you can import the chat database from a file. Your current chat database is
+deleted and replaced with the imported one. When the chat starts again, the app
+asks for the passphrase of the imported database.
### Delete database
-Here you can delete your chat database. WARNING: Results in a loss of all your SimpleX Chat data! Unless you have just exported the chat database.
+Here you can delete your chat database. All your SimpleX Chat data will be lost,
+unless you have just exported the chat database.
## Delete files and media
-
+
-You can delete all sent and received files and media stored in the app via this option, without deleting the messages.
+You can delete all sent and received files and media stored in the app with
+**Delete all files** in **Chat data**, without deleting the messages. When you
+have several chat profiles, the button is **Delete files for all chat
+profiles**.
+
+## Migrate to another device
+
+You can move all your chat profiles, contacts, messages and files to a new
+device. The app encrypts them and uploads them in chunks to your XFTP file
+servers.
+
+1. On the old device, open [the app
+ settings](./app-settings.md#opening-the-app-settings) and tap **Migrate to
+ another device**. The app stops the chat. Set a database passphrase, or
+ confirm the one you set before, and tap **Archive and upload**. When the
+ upload completes, the app shows a QR code and a link.
+2. On the new device, install the app and tap **Get started** (iOS and Android)
+ or **Create your profile** (desktop). Tap **Migrate** (**Migrate from another
+ device** on desktop) at the top of the screen. Scan the QR code (iOS and
+ Android) or paste the link, then enter the database passphrase and tap **Open
+ chat**.
+3. When the new device shows **Chat migrated!**, tap **Finalize migration** on
+ the old device, then **Delete database from this device**.
+
+Do not use the same database on two devices. It breaks the decryption of
+messages from your connections.
diff --git a/docs/guide/notifications.md b/docs/guide/notifications.md
new file mode 100644
index 0000000000..b43cbb64c0
--- /dev/null
+++ b/docs/guide/notifications.md
@@ -0,0 +1,154 @@
+---
+title: Notifications
+---
+# Notifications
+
+The app shows notifications for new messages, contact requests, new contacts and
+calls. How it receives messages in the background depends on the platform. iOS
+does not allow apps to run a background service, so the iOS app can use the
+SimpleX Chat push server, which sends notifications through Apple's push
+service. The Android app receives messages itself, without a push server. The
+desktop app receives messages while it runs.
+
+When you create your first profile on iOS or Android, you can choose the
+notification mode with **Setup notifications** on the **Your network** screen,
+see [Choose notifications mode](./README.md#choose-notifications-mode). The
+default mode is **Instant**. You can change it later in the settings.
+
+## Notifications on iOS
+
+
+
+There are three modes:
+
+- **Instant**. The device token and end-to-end encrypted notifications are sent
+ to the push server. The push server does not see the message content, its
+ size or who sent it.
+- **Periodic**. The app checks for new messages every 20 minutes. Only the
+ device token is shared with the push server, and the server does not see how
+ many contacts you have or any message metadata.
+- **No push server**. The push server is not used. The app checks for messages
+ in the background when iOS allows it, which depends on how often you use the
+ app.
+
+The app marks **Instant** as recommended, **Periodic** as more private and **No
+push server** as the most private.
+
+To change the mode:
+
+1. Open **Settings** (see [Opening the app
+ settings](./app-settings.md#opening-the-app-settings)) and tap
+ **Notifications**.
+2. Tap **Send notifications** and choose a mode.
+3. Tap **Enable**, or **Turn off** for **No push server**.
+
+When a push server is used, the **Notifications** screen shows it in **Push
+server**. **Test notifications** checks the connection to the push server and
+the notification token of your device. If there is a problem, the app shows
+**Notifications error** with **Register**, which registers the token again.
+
+If notifications for SimpleX Chat are turned off in the iOS settings, the app
+shows **Notifications are disabled!** with **Open Settings**.
+
+## Notifications on Android
+
+
+
+The Android app receives messages itself, with a background service or with
+periodic checks. The modes have different names at the first start and in the
+settings:
+
+| At the first start | In **Notification service** | What it does |
+|---|---|---|
+| **Instant** (default) | **Always on** | A background service is always running, and notifications are shown as soon as messages arrive. It uses more battery. |
+| **Periodic** | **Starts periodically** | The app checks for new messages every 10 minutes, for up to 1 minute. It uses less battery, and you may miss calls or urgent messages. |
+| **When app is running** | **Runs when app is open** | The app receives messages while it is open. It uses the least battery. |
+
+To change the mode, open **Settings**, tap **Notifications**, then
+**Notification service**, and choose a mode.
+
+While the service runs, Android shows the notification **SimpleX Chat service**
+with **Receiving messages…**. Its **Hide** button opens the system settings of
+this notification.
+
+**Starts periodically**, and **Always on** on Android 12 and later, need the app
+to run without battery optimization. If this is not allowed, the app shows an
+alert. **Allow** opens the system dialog to allow it, and **Disable
+notifications** switches to **Runs when app is open**. If Android restricts the
+app in the background, the app asks you to set **App battery usage** to
+**Unrestricted** in the app settings, and **Open app settings** opens them.
+
+On Xiaomi, Redmi and POCO devices, the app asks you to enable **Autostart** in
+the system settings.
+
+On Android 13 and later, the app asks for permission to show notifications when
+the chat list opens. The notification service starts after notifications are
+allowed.
+
+## Notifications on desktop
+
+The desktop app shows system notifications while it runs. The **Notifications**
+row in the settings is on iOS and Android only. When the app window is focused,
+message notifications are shown for chats other than the open one, and incoming
+calls are shown in the app window.
+
+To keep receiving messages after you close the window, turn on **Close to tray**
+in [Appearance](./app-settings.md#appearance), and the app keeps running in the
+system tray. The first time you close the window, the app asks **Minimize to
+tray?** with **Close the app** and **Minimize to tray**. Messages are not
+received after the app is closed. The tray icon shows a dot when there are
+unread messages, and its tooltip shows how many. Its menu has **Show SimpleX**
+and **Quit SimpleX**. **Close to tray** is available when the system has a tray.
+
+## Notifications and the database passphrase
+
+If you remove the database passphrase from the device, the iOS app warns that
+instant push notifications will be hidden, and the Android app warns that
+notifications will be delivered only until the app stops. See [Database
+passphrase](./managing-data.md#database-passphrase).
+
+## Notification previews
+
+By default, notifications show the name of the contact or group and the message
+text. To change this:
+
+1. Open **Settings** and tap **Your privacy**, then **More privacy**.
+2. In the **Notifications** section, tap **Show preview**.
+3. Choose an option:
+ - **Message text**: the name and the message. Text formatted as secret is
+ replaced with "...".
+ - **Contact name**: the name and "new message".
+ - **Hidden**: "Contact hidden:" and "new message". On iOS, group messages
+ show "Group message:". Contact pictures are not shown.
+
+The option also applies to notifications of calls and contact requests. On iOS,
+you can set the lock screen preview in the system settings, and **Open
+Settings** opens them.
+
+## Mute chats and profiles
+
+When you mute a chat, its notifications are turned off. In groups and channels,
+you can keep notifications for mentions. Muted chats have a muted icon in the
+chat list.
+
+In a direct chat, **Mute** turns notifications off and **Unmute** turns them on.
+In groups and channels, the same menu item switches between three states:
+
+- **Mute**: notifications only for messages that mention you or reply to your
+ messages.
+- **Mute all**: no notifications.
+- **Unmute**: notifications for all messages.
+
+| | iOS | Android | Desktop |
+|---|---|---|---|
+| Chat list | Swipe the chat right | Long press the chat | Right-click the chat |
+| Open chat | The "…" menu in the chat toolbar | The "⋮" menu in the chat toolbar | The "⋮" menu in the chat toolbar |
+| Chat info | The mute button under the name | The mute button under the name | The mute button under the name |
+
+A muted chat profile shows no notifications while it is not your active profile.
+When it is the active profile, you receive its calls and notifications. Hidden
+profiles are always muted. To mute or unmute a profile, see [Hiding and muting
+chat profiles](./chat-profiles.md#hiding-and-muting-chat-profiles).
+
+The design of notifications on iOS and Android is explained in [this
+post](../../blog/20220404-simplex-chat-instant-notifications.md).
diff --git a/docs/guide/privacy-security.md b/docs/guide/privacy-security.md
index 849a17f65d..c46c4766a0 100644
--- a/docs/guide/privacy-security.md
+++ b/docs/guide/privacy-security.md
@@ -5,39 +5,68 @@ title: Privacy & security
SimpleX Chat default configuration aims to balance privacy, security and convenience. You may want to change the default options.
-This page lists all the features and options that affect privacy and security.
+This page lists the main features and options that affect privacy and security.
## Privacy and Security settings
-These settings are available in the [Privacy & Security settings](./app-settings.md#privacy-and-security).
+These settings are available in [Your
+privacy](./app-settings.md#privacy-and-security) settings.
+
+## End-to-end encryption
+
+Direct chats and groups are protected by end-to-end encryption. When a
+conversation starts, the app shows: "Messages, files and calls are protected by
+**end-to-end encryption** with perfect forward secrecy, repudiation and break-in
+recovery." In a direct chat with quantum resistant encryption, it shows
+**quantum resistant e2e encryption** instead.
+
+Messages in channels are not end-to-end encrypted: "Chat relays can see these
+messages." See [How channels work](./channels.md#how-channels-work).
## Security code verification
-
+
-While SimpleX Chat always establishes connection via link passed via an independent channel, so it is already more protected than other apps, there are scenarios when the invitation links can be substituted in transit (MITM attack). To protect against such attacks, you should verify the security code with your contacts:
+SimpleX Chat always establishes connection via link passed via an independent
+channel, so servers cannot substitute the encryption keys. In some scenarios,
+the invitation links can be substituted in transit (MITM attack). To protect
+against such attacks, you should verify the security code with your contacts:
- open the conversation with the contact
-- tap the contact name on top of the conversation
+- tap the contact name in the chat toolbar
- tap "Verify security code"
- ask your contact to do the same
- the connection is secure if you and your contact have the same security code
This can be validated by one of the following:
-- one of you can scan the security code from their device, if the codes match the contact will be marked as verified on the device that scanned the code.
+- one of you can scan the security code from their device (iOS and Android), if
+ the codes match the contact will be marked as verified on the device that
+ scanned the code.
- simply tap mark verified if you trust the confirmation from your contact that the code is verified.
-- you can also read the contact over the voice call.
+- you can also read the code over the voice call.
+
+You can verify the security code of a group member in the same way, in the
+member's info.
Read more in [this post](../../blog/20230103-simplex-chat-v4.4-disappearing-messages.md#connection-security-verification)
## Database passphrase
-When installed, the app generates a random passphrase for the chat database and stores it securely in KeyChain (iOS) or using KeyStore (Android, TPM module is used when available). You can set your own passphrase and also remove it from the device, in which case you will need to enter it every time you start the app, and notifications may be limited, in the [Database passphrase & export](./managing-data.md#database-passphrase) settings.
+When installed, the app generates a random passphrase for the chat database and
+stores it securely in Keychain (iOS) or using Keystore (Android, secure hardware
+is used when available). The desktop app offers to set your own passphrase when
+you create your first profile and, if you skip it, stores the random passphrase
+in the app settings as plaintext. You can set your own passphrase and also
+remove it from the device, in which case you will need to enter it every time
+you start the app, and notifications may be limited, in the [Database
+passphrase & export](./managing-data.md#database-passphrase) settings.
## Incognito mode
-This feature generates a random profile name for each new contact. Read more in [Incognito mode](./app-settings.md#incognito).
+This feature generates a random profile name for a new contact or group, when
+you choose it for the connection. Read more in [Incognito
+mode](./app-settings.md#incognito).
## Hidden profiles
@@ -45,16 +74,31 @@ This feature allows to hide some of your chat profiles with the password. Read m
## Network settings
-[Transport isolation (BETA)](./app-settings.md#transport-isolation-beta) allows to isolate you traffic with each contact in a different TCP connection (and Tor circuit).
+[Transport isolation (BETA)](./app-settings.md#transport-isolation-beta) allows
+to isolate the traffic of each chat profile (by default) or your traffic with
+each contact in a different TCP connection (and Tor circuit, on Android and
+desktop).
+
+[Private message routing](./app-settings.md#advanced-network-settings) is used
+by default: "To protect your IP address, private routing uses your SMP servers
+to deliver messages."
+
+**Protect IP address**, in **Your privacy** > **More privacy**, makes the app
+ask you to confirm downloads of files from unknown file servers, unless you use
+a SOCKS proxy or the .onion address of the server. It is on by default.
## Using Tor
-
+To connect to SMP relays (messaging servers) via Tor you need to install Orbot
+app or another app that provides a SOCKS proxy to Tor network.
-To connect to SMP relays (messaging servers) via Tor you need to install Orbot app.
+Android: use Orbot app as SOCKS proxy on port 9050 (default) and enable [Use SOCKS proxy](./app-settings.md#use-socks-proxy).
-Android: use Orbot app as SOCKS proxy on port 9050 (default) and enable [Use SOCKS proxy](./app-settings.md#use-socks-proxy-android-only).
+Desktop: use a SOCKS proxy to Tor network in the same way.
-iOS: use Orbot app as VPN provider and enable VPN.
+iOS: use Orbot app as VPN provider and enable VPN, or set a SOCKS proxy to Tor
+in [Advanced network settings](./app-settings.md#advanced-network-settings).
You can also change which server addresses are used with [Use .onion hosts option](./app-settings.md#use-onion-hosts).
+
+Calls connect without the SOCKS proxy.
diff --git a/docs/guide/register-simplex-name.md b/docs/guide/register-simplex-name.md
index 366661b7e5..84c72e66dd 100644
--- a/docs/guide/register-simplex-name.md
+++ b/docs/guide/register-simplex-name.md
@@ -7,11 +7,21 @@ title: Registering a SimpleX name
A SimpleX name lets people reach you by typing a short `@name` instead of pasting
a long link. You register the name on a website and add your SimpleX link to it,
then claim the name in the app, to connect it with your SimpleX address or
-channel name.
+channel.
+
+A name is optional. It is useful for those who want to be found, such as
+channels and businesses. When the app looks up a name, the server sees only a
+hash of the name and cannot link the lookup with the address.
> Names currently run on a testing network. The registration page is
> [testing-names.simplex.chat](https://testing-names.simplex.chat) and names end
> in `.testing`. The steps stay the same when the main names launch.
+>
+> In the app, **How to register a test name** on the SimpleX name screen of [Step
+> 4](#step-4-claim-the-name-in-the-app) opens
+> [simplex.domains/#testing](https://simplex.domains/#testing). Test names stop
+> working in the app when the SimpleX name sale starts, on 12 December 2026 at
+> 18:00 UTC. The app counts down to it under **SimpleX name sale starts in**.
## What you need
@@ -76,7 +86,8 @@ name before you.
## Step 3. Check what the app sees
-Copy your `#name.testing` (or `@name.testing`). Paste the name into the search bar, or simply type it.
+Copy your `#name.testing` (or `@name.testing`). Paste the name into the search
+field, or simply type it, and tap **Connect to …**.
The app reports that the name is registered but not yet added to your profile:
@@ -91,21 +102,33 @@ finishes the setup.
Set the name on the same profile the record points at.
- For your **contact address**, open your SimpleX address, tap **Get SimpleX
- name**, enter the name, and save.
-- For your **channel**, open the channel information, tap **Get SimpleX name** (under
- **Channel link**), enter the name, and save.
+ name (BETA)**, enter the full name, for example `@name.testing`, and save.
+- For your **channel**, open the channel information, tap **Get SimpleX name
+ (BETA)** (under **Channel link**), enter the full name, for example
+ `#name.testing`, and save.
+
+A name entered without a dot is saved with `.simplex`, so enter it with
+`.testing`.
This proves to people who connect to you that `#name.testing` (or `@name.testing`) is your name.
## Step 5. Connect by name
-On another device, copy `#name.testing` (or `@name.testing`). Paste the name into the search bar, or simply type it. This time the app connects, and the name
-shows a check mark next to it.
+On another device, copy `#name.testing` (or `@name.testing`). Paste the name
+into the search field, or simply type it, and tap **Connect to …**. This time
+the app connects, and the name shows a check mark next to it.
You can paste the name in your own device too - the app will warn you that it is your own name.
Anyone can now reach you by name, and their app confirms it is really you.
+When a contact or a channel has a SimpleX name, its chat info shows the name. A
+check mark next to the name means that the app verified it, and a red cross
+means that the verification failed. To verify a name, tap **Verify name** next
+to it. To verify names automatically when you open the chat info, turn on
+**Verify SimpleX names** in **Your privacy** > **More privacy**. It is off by
+default.
+
## If something doesn't work
| Message | Meaning | Fix |
@@ -114,7 +137,7 @@ Anyone can now reach you by name, and their app confirms it is really you.
| Name not found | No name is registered. | Check the spelling, or register it (step 2). |
| No valid link | The name has no contact or channel link. | Add a link to the name on the website, on its **Records** tab. |
| Error saving name | You tried to claim a name that has no matching link on the website. | Add your contact address or channel link to the name, then set the name again. |
-| None of your servers are set to resolve SimpleX names | No server in the app is set to resolve names. | In server settings, turn on **To resolve names** for a server. |
+| None of your servers are set to resolve SimpleX names | No server in the app is set to resolve names. | In **Network & servers**, open a server operator and turn on **To resolve names**. |
## See also
diff --git a/docs/guide/secret-groups.md b/docs/guide/secret-groups.md
index 5cdbcf86da..3712f665f2 100644
--- a/docs/guide/secret-groups.md
+++ b/docs/guide/secret-groups.md
@@ -1,69 +1,95 @@
---
-title: Secret groups
+title: Groups
---
-# Secret groups
+# Groups
-Secret groups are anonymous and private, they are designed to be hard to track by outsiders. Every message and file gets sent separately to every member in the group, which makes secret groups only fit for smaller groups.
+Groups are decentralized: the group profile and the member list are stored on
+members' devices, and servers have no information about the group. Every
+message gets sent separately to every member in the group, which suits smaller
+groups. Files are uploaded to file servers once, and each member can download
+them from there. For large audiences, use [channels](./channels.md).
## Creating a group
-
+
-- Tap on a button in the lower right-hand of the screen.
-- Tap on "Create secret group".
+- Tap on the pencil button in the lower right-hand of the chat list.
+- Tap on "Create group".
- Enter group display name.
-- Tap on "Create".
+- Tap on "Create group".
+
+Unless Incognito is on, the app then shows your contacts. Select the ones to
+invite and tap "Invite to group", or skip this step and [add
+members](#adding-members) later.
## Group preferences
-Here you can choose whether you want to allow disappearing messages, direct messages, deleting for everyone and voice messages.
+On the group page, in "Group preferences", group owners can choose whether to
+allow disappearing messages, direct messages, deleting for everyone, message
+reactions, voice messages, files and media, SimpleX links, and visible history
+for new members.
## Adding members
- Open a group.
-- Tap on + button in the upper right-hand of the screen.
+- Tap group name, then tap "Invite members".
- Select contacts you wish to invite.
- Tap on "Invite to group".
## Joining a group
-
-
When you receive the invitation to join the group you can accept it both from the conversation with your contact and from the list of conversations.
+To join a group via its link, open the link in the app, tap "Open group", then
+"Join group" (see [Join some user groups](./README.md#join-some-user-groups)).
+
## Leaving a group
-
-
-Tap and hold (Android) or swipe (iOS) the group in the list of the conversations to leave the group.
+Tap and hold (Android), swipe (iOS) or right-click (desktop) the group in the
+list of the conversations to leave the group.
You can also leave the group from the group page:
- Open a group conversation.
-- Tap group name on top.
+- Tap group name.
- Scroll to the bottom and tap "Leave group".
+After you leave, the group stays in the list of the conversations. To delete it
+from your device, tap "Delete group" on the group page. Owners can tap "Delete
+group" without leaving, to delete the group for all members.
+
## Member permissions
Open a group and tap on group's name to view members list, there you can tap on a member and change their role.
-- Observer: can only read messages (and delete or mark deleted the messages they sent before their role was changed).
+- Observer: can only read messages, add reactions and use "Chat with admins"
+ (and delete or mark deleted the messages they sent before their role was
+ changed).
- Member: can send messages and delete own messages.
-- Admin: Same as member, but can delete the messages of others and remove members from the group.
-- Owner: Same as admin, but can change group preferences.
+- Moderator: Same as member, but can delete the messages of others and block
+ members for all.
+- Admin: Same as moderator, but can also invite members, create a group link,
+ remove members from the group and change their roles.
+- Owner: Same as admin, but can also edit the group profile and the welcome
+ message, change group preferences and delete the group for all members.
+
+Moderators, admins and owners can act only on members with the same or a lower
+role.
## Link to join group
-
+
If you are a group owner or admin, you can create a link that can be used by anyone to join the group, making a group more public.
- Open a group conversation.
- Tap group name.
-- Tap on "Create group link.
+- Tap on "Create group link".
Once the link is created, you can also change the role assigned to members when they join.
## Welcome message
If you are a group owner you can create a welcome message that will be shown to all new members joining group.
+
+To create it, tap "Add welcome message" on the group page.
diff --git a/docs/guide/send-messages.md b/docs/guide/send-messages.md
index 8292bd5747..90e0395d06 100644
--- a/docs/guide/send-messages.md
+++ b/docs/guide/send-messages.md
@@ -11,48 +11,85 @@ You need to open a conversation to send messages.
### Sending text messages
-You can send, edit and quote messages in a conversation.
+You can send, edit and reply to messages in a conversation.
To send, simply type in the message in the entry box and tap "Send".
-To edit or quote a message, tap and hold the message and choose the action.
+On desktop, press Enter to send the message, and Shift+Enter to start a new
+line.
+
+To reply to a message, edit it, add a reaction or forward it, tap and hold the
+message (right-click on desktop) and choose the action. On iOS and Android, you
+can also swipe a message to the left to reply to it. You can edit a message for
+24 hours after you send it.
+
+To save a message for yourself, forward it to "Private notes".
+
+In a group, type @ and choose a member to mention them.
+
+You can format messages with markdown. The formats are listed in the [app
+settings](./app-settings.md#opening-the-app-settings), in "Help & support" >
+"How to use it".
### Sending images and files
- Tap 📎 (paperclip) button on the left of the message entry box.
-- Choose between "Use Camera", "From Gallery" and "Choose file".
+- Choose between "Take picture", "Choose from library" and "Choose file" on
+ iOS, "Camera", "Image", "Video" and "File" on Android, or "Image", "Video" and
+ "File" on desktop.
- Once you have chosen what you wish to send, tap on "Send" button.
On iOS you can also paste images from the clipboard.
+On iOS and Android, you can also share images, videos and files to a chat from
+other apps, with their share button.
+
+On desktop, you can also paste images and files, or drag them into the chat.
+
+Files can be up to 1 GB, or larger with a [supporter
+badge](./supporter-badges.md).
+
### Sending voice messages
-
+
- Tap 🎤 (microphone) button on the right of the message entry box.
-- Allow SimpleX Chat to record the voice if prompted. You will have to tap on the microphone button once again if this happens.
+- Allow SimpleX Chat to record the voice if prompted. On Android, you will have
+ to tap on the microphone button once again if this happens.
- Speak, once you've finished the recording, tap on the square stop icon on the right.
- You may listen to the recording before sending it, and re-record if necessary.
- Tap on "Send" button to send the recorded voice message.
You can also tap and hold the microphone button to record a voice message. In this case the recording will stop once you release the button. You still need to tap "Send" to send the message.
-**Please note**: if the microphone button is gray, that means voice messages are disabled in this conversation. Ask your contact or group owner to enable them (they are enabled by default).
+A voice message can be up to 5 minutes long.
+
+**Please note**: if the microphone button is gray, that means voice messages are
+disabled in this conversation. Tap it to see why. Ask your contact or group
+owner to enable them (they are enabled by default).
### How to use disappearing messages
-
+
- Tap on name of the conversation.
-- Toggle the slider next to "Disappearing messages".
-- Choose how long until messages are deleted after being sent in "Delete after".
+- Tap "Contact preferences".
+- Under "Disappearing messages", make sure that the "You allow" slider is on (it
+ is on by default).
+- Choose how long until messages are deleted in "Delete after". On your device,
+ the time counts from when the message is sent, and on your contact's device,
+ from when it is read.
+- Tap "Save and notify contact".
-**Please note**: Your contact must also enable disappearing messages. In a group, only group owner can enable disappearing messages.
+**Please note**: Your contact must also allow disappearing messages (they are
+allowed by default). In a group, only group owner can enable disappearing
+messages.
+
+To send one disappearing message, type it, then tap and hold the "Send" button
+(right-click on desktop), choose "Disappearing message" and the time.
### Sending "live" messages
-
-
You can use "live" messages for your recipients to see the messages as you type them, without having to tap "Send" button until it's completed. It can be useful in these scenarios:
1. Real-time conversation, like talking in person or in a discussion club. You can retract what you wrote, but your recipients would see it. Also people don’t need to guess what you’re typing. This allows to avoid interrupting the flow of conversation and still send longer messages.
@@ -61,42 +98,46 @@ You can use "live" messages for your recipients to see the messages as you type
To send a "live" message:
- Tap ⚡️ (lightning) button on the right of the message entry box.
-- When you do it first time, you would see and alert explaining the feature. Tap "OK", and tap it again.
+- When you do it first time, you would see an alert explaining the feature. Tap
+ "Send".
- Now you can start typing the messages, the app will be periodically sending the updates to your recipients, as you type (every few seconds, and only complete words).
+- Tap ✓ (check mark) on the right of the message entry box to finish the
+ message.
### Sending stickers and animated messages
-
-
While there are no stickers and GIFs in the app itself, you can send them using third-party (such as [GIPHY](https://apps.apple.com/us/app/giphy-the-gif-search-engine/id974748812) on iOS) or native (Android 13+) keyboards.
### Message drafts
-
-
Messages are kept in the chat until they are sent, even if you leave the chat.
For privacy and simplicity, we decided to allow only one message draft and keep it only until the app is stopped.
-Read more in [this post](../../blog/20230204-simplex-chat-v4-5-user-chat-profiles.md#message-draft).
-
## Deleting messages
-
-
-To delete a message, tap and hold the message and choose "Delete".
+To delete a message, tap and hold the message (right-click on desktop) and
+choose "Delete".
If you sent this message yourself within the last 24 hours, you will be offered two options:
-- `delete for everyone` - this will either mark the message as deleted for you and the recipient(s) (this is the default) or fully delete the message on your and recipients' devices (if you have enabled "Delete for everyone" in the conversation preferences)
+- `delete for everyone` - this will either mark the message as deleted for you
+ and the recipient(s) (this is the default) or fully delete the message on your
+ and recipients' devices (if "Delete for everyone" is enabled in the
+ conversation preferences)
- `delete for me` - this will delete the message only locally.
Please read about [reasons for not allowing full deletion by default](../../blog/20221206-simplex-chat-v4.3-voice-messages.md#irreversible-message-deletion). TL;DR: data sovereignty.
## Moderating messages in the groups
-Group admins can delete member messages that are inappropriate for the groups.
+Group moderators, admins and owners can delete member messages that are
+inappropriate for the groups, when the member's role is the same as theirs or
+lower.
-To remove a message of another member, tap and hold the message and choose "Moderate".
+To remove a message of another member, tap and hold the message (right-click on
+desktop) and choose "Moderate".
-Please read about [group moderation](../../blog/20230328-simplex-chat-v4-6-hidden-profiles.md#community-moderation).
+Members can report a message to the group moderators. To report it, tap and hold
+the message (right-click on desktop) and choose "Report". Moderators see the
+reports on the group page, in "Member reports".
diff --git a/docs/guide/supporter-badges.md b/docs/guide/supporter-badges.md
new file mode 100644
index 0000000000..1ac7190941
--- /dev/null
+++ b/docs/guide/supporter-badges.md
@@ -0,0 +1,182 @@
+---
+title: Supporter badges
+---
+# Supporter badges
+
+A supporter badge, bought to support SimpleX Chat, is shown next to your name on
+your chat profile. With a badge, you can send larger files, from 2 to 5 GB, that
+stay available for 7 to 21 days.
+
+A badge is added to one chat profile. It is shown to your contacts, and in the
+groups and channels where you use this profile. It is not shown with an
+incognito profile.
+
+
+
+## Badge types
+
+| Badge | Benefits |
+|---|---|
+| **Supporter** | Files up to 2 GB, stored for 7 days. |
+| **Legend** | Files up to 5 GB, stored for 21 days. |
+
+Without a badge, you can send files up to 1 GB. The app of the person who
+receives your file checks your badge before it accepts a larger file.
+
+In incognito chats, you can send files up to 1 GB.
+
+## Where the app offers a badge
+
+- **Settings**, then **Support SimpleX**. With a badge, this row is **Your
+ badge** or **Your legend badge**.
+- The **Support SimpleX** banner in the chat list, shown when you have more than
+ 3 chats and no badge. After you tap it once, you can dismiss it.
+- The **Large file!** alert, when you have no badge and choose a file larger
+ than 1 GB and up to 5 GB, outside incognito chats. It has the button **Support
+ SimpleX**.
+- The alert that opens when you tap another person's supporter or legend badge,
+ when you have no badge yourself.
+
+On the **Support SimpleX** screen are **How badges protect your privacy** and
+the ways to get a badge on your platform.
+
+## Get a badge
+
+| iOS | Android from Google Play | Android from F-Droid or GitHub, and desktop |
+|---|---|---|
+| **Choose your badge**: the badge page in your browser. **Redeem badge code**: a badge from a code. | **Choose your badge**: a purchase in the app with Google Play. **Restore purchases**: "Moves a paid badge to this profile." **Redeem badge code**: a badge from a code. In the United States, **Buy in browser** is offered as well, and **Redeem badge code** is on the **Choose your badge** screen. | **Buy in browser**: the badge page in your browser. **Redeem badge code**: a badge from a code. |
+
+### Buy on the badge page
+
+Tap **Choose your badge** (iOS) or **Buy in browser** to open the badge page,
+badges.simplex.chat. On the page, choose a **Supporter** or **Legend** badge and
+the number of months, and pay by card, Bitcoin or Monero.
+
+- When the app is registered to open links from the page, the page shows
+ **Return to SimpleX** after the payment. Tap it to open the app, which asks
+ **Add badge to your profile?** and names the profile. Tap **Add badge**. Tap
+ **Show code** on the page to see the badge code.
+- Otherwise, the page shows the badge code, **Copy code**, and a QR code to scan
+ with your phone. The code is saved only in that browser. Redeem it as
+ described below.
+
+The iOS and Android apps are registered to open these links. When the desktop
+app is not registered to open them, it opens **Redeem code** next to the
+browser.
+
+A profile has one badge at a time. If it already has a badge, you can add the
+new badge to another profile, or to this profile after the current badge ends.
+
+A badge bought on the badge page is not renewed. It ends when the prepaid months
+run out.
+
+### Buy in the app with Google Play
+
+In the Android app from Google Play:
+
+1. Open **Settings**, then **Support SimpleX**, and tap **Choose your badge**.
+2. Choose **Supporter** or **Legend**, and tap **Continue**.
+3. Choose **1 month**, **Monthly** or **Annual**, and tap **Continue**.
+ **Monthly** and **Annual** are subscriptions that renew until you cancel
+ them.
+4. Check your order and tap **Pay**. Complete the payment in Google Play.
+
+While the badge is being added, the app shows **Issuing your badge**. When the
+payment needs approval, the app shows **Waiting for approval**, and nothing is
+charged until it is approved.
+
+### Redeem a badge code
+
+1. Open **Settings**, then **Support SimpleX**, and tap **Redeem badge code**.
+2. Tap **Paste**, type the code, or scan its QR code. Badge codes look like
+ SB-XXXXX-XXXXX-XXXXX-XXXXX. Scanning is available on iOS and Android.
+3. Tap **Redeem**.
+
+The badge is added to your current profile.
+
+| Message | Meaning |
+|---|---|
+| This profile already has a badge. | A profile holds one badge at a time. Use the code with another profile, or after this badge ends. |
+| This code was not recognized. | Check the code. |
+| This code has already been used. | The code was redeemed before. |
+| This code has expired. | The code can no longer be redeemed. |
+| The code was accepted, but the badge it grants has already ended. | The paid period of the code is over. |
+| Too many attempts. Please try again later. | Wait before you try again. |
+
+## Your badge
+
+With a badge, open **Settings**, then **Your badge** (or **Your legend badge**).
+On the **Your badge** screen are:
+
+- The badge, its type, and "shown on your profile".
+- **Benefits**: the file size and storage time.
+- **Ends** with the date, for a badge without renewal, or **Subscription** with
+ its state.
+- **How badges protect your privacy**.
+- **Error**, when the badge could not be renewed, with **Contact SimpleX team**.
+
+| Subscription state | What the app shows |
+|---|---|
+| Renewing | **Renews** with the date, and the price when it is known. |
+| Cancelled | **Ends** with the date until which the badge stays. |
+| Payment failed | **Payment problem**, with a request to update your payment method. |
+| Ended | **Ended**. The badge will be removed. |
+
+Tap **Manage subscription** to open your subscriptions in Google Play.
+
+The app updates the badge every month while paid months remain, with no action
+from you.
+
+## Badge alerts
+
+The chat list shows a banner about your badge in these cases. Tap the banner to
+open **Your badge**, or **Support SimpleX** when the badge has ended.
+
+| Banner | When |
+|---|---|
+| **Your badge expired** | A badge without renewal has ended. |
+| **Your subscription ended** | The subscription has ended. |
+| **Badge payment problem** | The store could not charge your payment method. |
+| **Badge renewal failed** | The badge could not be renewed. |
+| **Subscription cancelled** | The subscription was cancelled, and the badge stays until its end date. |
+| **Your badge renews soon** | 3 days before an annual subscription is renewed. |
+
+To close a banner, tap its close button and choose **Remind me later**, to see
+it again after one day, or **Dismiss**.
+
+## How badges protect your privacy
+
+A badge is an anonymous credential stored in your profile on your device.
+SimpleX Chat has no user accounts, so the badge is not attached to an account.
+The credential itself is not sent to anyone.
+
+To show a contact or a server that you have a badge, the app generates a new
+proof each time. A proof contains the badge type and the expiry date, rounded to
+a week. Two proofs cannot be linked to each other or to the purchase.
+
+The app sends a new proof with your profile each time it sends the profile: to a
+new contact, in your SimpleX address and 1-time links, to the groups and
+channels you join, to your contacts when a badge is added, and with your profile
+updates. A file larger than 1 GB is sent with a proof as well.
+
+The expiry date is the same for all badges whose paid month ends in the same
+week: the end of the following Monday. The apps of other people accept a badge
+for 7 days after this date. After that, the badge is shown dimmed, and one month
+later it is no longer shown.
+
+The design is described in the [supporter badges
+whitepaper](../protocol/badges-overview.md).
+
+## Badges of other people
+
+A badge is shown after the person's name. Tap it in the contact info, in the
+member info, or in the profile shown at the start of the chat to see what it
+means:
+
+- "[name] supports SimpleX Chat." for a supporter or legend badge;
+- "[name] supported SimpleX Chat. The badge expired on [date]." for a recently
+ expired badge.
+
+When the app cannot verify a badge, it shows a warning sign in its place.
+Tapping it shows **Unverified badge**, or **Badge cannot be verified** when the
+badge is signed with a key that this version of the app does not recognize.
diff --git a/scripts/docs/frame-guide-images.py b/scripts/docs/frame-guide-images.py
new file mode 100644
index 0000000000..eacaf6f855
--- /dev/null
+++ b/scripts/docs/frame-guide-images.py
@@ -0,0 +1,599 @@
+#!/usr/bin/env python3
+import argparse
+import base64
+import bz2
+import os
+import plistlib
+import struct
+import sys
+import tempfile
+import urllib.request
+import zlib
+
+BEZEL_DMG_URL = "https://devimages-cdn.apple.com/design/resources/download/Bezel-iPhone-17.dmg"
+BEZEL_PNG_PATH = "PNG/iPhone 17 Pro/iPhone 17 Pro - Silver - Portrait.png"
+BEZEL_SCALE = 3
+
+PIXEL_FRAME = "pixel_7"
+PIXEL_FRAME_URL = "https://android.googlesource.com/platform/tools/adt/idea/+/ab04bfbfaaedfcb539b3768c2e8ef1368ea947a4/artwork/resources/device-art-resources/{}/{}?format=TEXT"
+PIXEL_SCREEN_LEFT = 59
+PIXEL_SCREEN_TOP = 58
+PIXEL_SCALE = 2
+
+REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+REFERENCES = os.path.join(REPO, "apps/ios/SimpleX Snapshots/References")
+GUIDE_IMAGES = os.path.join(REPO, "docs/guide/images")
+
+FRAMED = {
+ "app-settings-advanced-network-settings.png": "AdvancedNetworkSettings",
+ "app-settings-settings.png": "SettingsView",
+ "audio-video-calls-settings.png": "CallSettings",
+ "channels-create.png": "AddChannelView",
+ "chat-profiles-create-profile.png": "CreateProfile",
+ "chat-profiles-hide-profile.png": "ProfilePrivacyView",
+ "groups-chat.png": "ChatView_Group",
+ "groups-create.png": "AddGroupView",
+ "groups-info.png": "GroupChatInfoView",
+ "groups-member-info.png": "GroupMemberInfoView",
+ "groups-preferences.png": "GroupPreferencesView",
+ "groups-profile.png": "GroupProfileView",
+ "linked-devices-connect-to-desktop.png": "ConnectDesktopView",
+ "making-connections-address.png": "UserAddressCreated",
+ "making-connections-contact-info.png": "ChatInfoView",
+ "managing-data-chat-data.png": "DatabaseView",
+ "managing-data-migrate-here.png": "MigrateToDevice",
+ "managing-data-passphrase.png": "DatabaseEncryptionView",
+ "notifications-ios-modes.png": "NotificationsModeView",
+ "privacy-security-verify-code.png": "VerifyCodeView",
+ "readme-get-started.png": "SimpleXInfo",
+ "send-messages-chat.png": "ChatView",
+ "send-messages-contact-preferences.png": "ContactPreferencesView",
+ "supporter-badges-redeem-code.png": "BadgesRedeemCodeView",
+ "supporter-badges-support-simplex.png": "BadgesSupportSimplexView",
+}
+
+PIXEL_FRAMED = {
+ "notifications-android-modes.png": "notifications-mode",
+}
+
+CODE_LENGTH_ORDER = (17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
+
+DISTANCE_MAP = (
+ (0, 1), (1, 0), (1, 1), (-1, 1), (0, 2), (2, 0), (1, 2),
+ (-1, 2), (2, 1), (-2, 1), (2, 2), (-2, 2), (0, 3), (3, 0),
+ (1, 3), (-1, 3), (3, 1), (-3, 1), (2, 3), (-2, 3), (3, 2),
+ (-3, 2), (0, 4), (4, 0), (1, 4), (-1, 4), (4, 1), (-4, 1),
+ (3, 3), (-3, 3), (2, 4), (-2, 4), (4, 2), (-4, 2), (0, 5),
+ (3, 4), (-3, 4), (4, 3), (-4, 3), (5, 0), (1, 5), (-1, 5),
+ (5, 1), (-5, 1), (2, 5), (-2, 5), (5, 2), (-5, 2), (4, 4),
+ (-4, 4), (3, 5), (-3, 5), (5, 3), (-5, 3), (0, 6), (6, 0),
+ (1, 6), (-1, 6), (6, 1), (-6, 1), (2, 6), (-2, 6), (6, 2),
+ (-6, 2), (4, 5), (-4, 5), (5, 4), (-5, 4), (3, 6), (-3, 6),
+ (6, 3), (-6, 3), (0, 7), (7, 0), (1, 7), (-1, 7), (5, 5),
+ (-5, 5), (7, 1), (-7, 1), (4, 6), (-4, 6), (6, 4), (-6, 4),
+ (2, 7), (-2, 7), (7, 2), (-7, 2), (3, 7), (-3, 7), (7, 3),
+ (-7, 3), (5, 6), (-5, 6), (6, 5), (-6, 5), (8, 0), (4, 7),
+ (-4, 7), (7, 4), (-7, 4), (8, 1), (8, 2), (6, 6), (-6, 6),
+ (8, 3), (5, 7), (-5, 7), (7, 5), (-7, 5), (8, 4), (6, 7),
+ (-6, 7), (7, 6), (-7, 6), (8, 5), (7, 7), (-7, 7), (8, 6),
+ (8, 7),
+)
+
+
+def main():
+ parser = argparse.ArgumentParser(description="Places the app screens used by the user guide into device frames: iOS renders into Apple's iPhone 17 Pro bezel, Android emulator screenshots into Google's Pixel 7 frame.")
+ parser.add_argument("--cache", default=os.path.join(tempfile.gettempdir(), "simplex-guide-bezel"), help="directory for the downloaded frames, outside the repository")
+ parser.add_argument("--references", default=REFERENCES, help="directory of the snapshot references")
+ parser.add_argument("--android-screens", help="directory of the android-smoke-test artifact of an android-emu run, taken with the emulator's Pixel 7 display overlays; Android screens are framed only when it is given")
+ args = parser.parse_args()
+ bezel_w, bezel_h, bezel = downscale(*read_png(bezel_png(args.cache)), BEZEL_SCALE)
+ left, top, screen_w, screen_h = screen_area(bezel_w, bezel_h, bezel)
+ for image, view in FRAMED.items():
+ w, h, render = read_png(os.path.join(args.references, f"{view}-light-large.png"))
+ if (w, h) != (screen_w, screen_h):
+ sys.exit(f"{view}: render is {w}x{h}, the bezel screen is {screen_w}x{screen_h}")
+ write_png(os.path.join(GUIDE_IMAGES, image), bezel_w, bezel_h, composite_over(place(render, w, h, bezel_w, bezel_h, left, top), bezel))
+ print(f"{image}: {view}")
+ if args.android_screens:
+ (back_w, back_h, back), (mask_w, mask_h, mask) = (read_webp(pixel_layer(args.cache, layer)) for layer in ("back.webp", "mask.webp"))
+ for image, shot in PIXEL_FRAMED.items():
+ screens = [name for name in os.listdir(args.android_screens) if name.endswith(f"-{shot}.png")]
+ if len(screens) != 1:
+ sys.exit(f"{shot}: {len(screens)} screenshots in {args.android_screens}")
+ w, h, screen = read_png(os.path.join(args.android_screens, screens[0]))
+ if (w, h) != (mask_w, mask_h):
+ sys.exit(f"{shot}: screenshot is {w}x{h}, the Pixel 7 screen is {mask_w}x{mask_h}")
+ framed = composite_over(back, place(composite_over(screen, mask), w, h, back_w, back_h, PIXEL_SCREEN_LEFT, PIXEL_SCREEN_TOP))
+ write_png(os.path.join(GUIDE_IMAGES, image), *downscale(back_w, back_h, framed, PIXEL_SCALE))
+ print(f"{image}: {screens[0]}")
+
+
+def bezel_png(cache):
+ png = os.path.join(cache, os.path.basename(BEZEL_PNG_PATH))
+ if not os.path.exists(png):
+ os.makedirs(cache, exist_ok=True)
+ dmg = os.path.join(cache, os.path.basename(BEZEL_DMG_URL))
+ if not os.path.exists(dmg):
+ urllib.request.urlretrieve(BEZEL_DMG_URL, dmg)
+ with open(png, "wb") as f:
+ f.write(hfs_file(udif_image(dmg), BEZEL_PNG_PATH))
+ return png
+
+
+def pixel_layer(cache, layer):
+ path = os.path.join(cache, f"{PIXEL_FRAME}-{layer}")
+ if not os.path.exists(path):
+ os.makedirs(cache, exist_ok=True)
+ with urllib.request.urlopen(PIXEL_FRAME_URL.format(PIXEL_FRAME, layer)) as response:
+ data = base64.b64decode(response.read())
+ with open(path, "wb") as f:
+ f.write(data)
+ return path
+
+
+def udif_image(dmg):
+ with open(dmg, "rb") as f:
+ f.seek(-512, 2)
+ koly = f.read(512)
+ data_fork = struct.unpack(">Q", koly[0x18:0x20])[0]
+ xml_offset, xml_length = struct.unpack(">QQ", koly[0xD8:0xE8])
+ f.seek(xml_offset)
+ blocks = plistlib.loads(f.read(xml_length))["resource-fork"]["blkx"]
+ image = bytearray()
+ for block in blocks:
+ mish = block["Data"]
+ first_sector = struct.unpack(">Q", mish[8:16])[0]
+ data_offset = struct.unpack(">Q", mish[24:32])[0]
+ chunk_count = struct.unpack(">I", mish[0xC8:0xCC])[0]
+ for i in range(chunk_count):
+ kind, _, sector, sectors, offset, length = struct.unpack(">IIQQQQ", mish[0xCC + 40 * i:0xCC + 40 * (i + 1)])
+ if kind in (0, 2, 0x7FFFFFFE, 0xFFFFFFFF):
+ continue
+ f.seek(data_fork + data_offset + offset)
+ chunk = f.read(length)
+ data = {1: lambda c: c, 0x80000005: zlib.decompress, 0x80000006: bz2.decompress}[kind](chunk)
+ position = (first_sector + sector) * 512
+ if len(image) < position + len(data):
+ image.extend(bytes(position + len(data) - len(image)))
+ image[position:position + len(data)] = data
+ return image
+
+
+def hfs_file(image, wanted_path):
+ partition = next(
+ struct.unpack(">I", image[block * 512 + 8:block * 512 + 12])[0] * 512
+ for block in range(1, struct.unpack(">I", image[512 + 4:512 + 8])[0] + 1)
+ if image[block * 512 + 48:block * 512 + 57] == b"Apple_HFS"
+ )
+ header = image[partition + 1024:partition + 1536]
+ block_size = struct.unpack(">I", header[40:44])[0]
+
+ def fork(record):
+ size = struct.unpack(">Q", record[0:8])[0]
+ extents = [struct.unpack(">II", record[16 + 8 * i:24 + 8 * i]) for i in range(8)]
+ data = b"".join(image[partition + start * block_size:partition + (start + count) * block_size] for start, count in extents if count)
+ return data[:size]
+
+ catalog = fork(header[272:352])
+ node_size = struct.unpack(">H", catalog[32:34])[0]
+ node = struct.unpack(">I", catalog[24:28])[0]
+ folders = {}
+ files = {}
+ while node:
+ data = catalog[node * node_size:(node + 1) * node_size]
+ next_node = struct.unpack(">I", data[0:4])[0]
+ for i in range(struct.unpack(">H", data[10:12])[0]):
+ offset = struct.unpack(">H", data[node_size - 2 * (i + 1):node_size - 2 * i])[0]
+ key_length, parent, name_length = struct.unpack(">HIH", data[offset:offset + 8])
+ name = data[offset + 8:offset + 8 + 2 * name_length].decode("utf-16-be")
+ record = data[offset + 2 + key_length:]
+ kind = struct.unpack(">h", record[0:2])[0]
+ if kind == 1:
+ folders[struct.unpack(">I", record[8:12])[0]] = (parent, name)
+ elif kind == 2:
+ files[(parent, name)] = record[88:168]
+ node = next_node
+
+ def path(parent, name):
+ parts = [name]
+ while parent in folders and folders[parent][0] != 1:
+ parent, folder = folders[parent]
+ parts.append(folder)
+ return "/".join(reversed(parts))
+
+ return next(fork(record) for (parent, name), record in files.items() if path(parent, name) == wanted_path)
+
+
+def downscale(w, h, rgba, factor):
+ out_w, out_h = w // factor, h // factor
+ out = bytearray(out_w * out_h * 4)
+ for y in range(out_h):
+ for x in range(out_w):
+ r = g = b = a = 0
+ for dy in range(factor):
+ row = ((y * factor + dy) * w + x * factor) * 4
+ for dx in range(factor):
+ p = row + dx * 4
+ alpha = rgba[p + 3]
+ r += rgba[p] * alpha
+ g += rgba[p + 1] * alpha
+ b += rgba[p + 2] * alpha
+ a += alpha
+ p = (y * out_w + x) * 4
+ if a:
+ out[p:p + 4] = bytes((round(r / a), round(g / a), round(b / a), round(a / (factor * factor))))
+ return out_w, out_h, out
+
+
+def screen_area(w, h, rgba):
+ start = (h // 2) * w + w // 2
+ seen = {start}
+ stack = [start]
+ while stack:
+ p = stack.pop()
+ x = p % w
+ for q in (p - 1 if x > 0 else -1, p + 1 if x < w - 1 else -1, p - w, p + w):
+ if 0 <= q < w * h and q not in seen and rgba[q * 4 + 3] < 255:
+ seen.add(q)
+ stack.append(q)
+ xs = [p % w for p in seen]
+ ys = [p // w for p in seen]
+ return min(xs), min(ys), max(xs) - min(xs) + 1, max(ys) - min(ys) + 1
+
+
+def place(rgba, w, h, canvas_w, canvas_h, left, top):
+ canvas = bytearray(canvas_w * canvas_h * 4)
+ for y in range(h):
+ start = ((top + y) * canvas_w + left) * 4
+ canvas[start:start + w * 4] = rgba[y * w * 4:(y + 1) * w * 4]
+ return canvas
+
+
+def composite_over(below, above):
+ out = bytearray(len(below))
+ for p in range(0, len(below), 4):
+ top_alpha = above[p + 3]
+ bottom_alpha = below[p + 3] * (255 - top_alpha) // 255
+ alpha = top_alpha + bottom_alpha
+ if alpha:
+ for c in range(3):
+ out[p + c] = round((above[p + c] * top_alpha + below[p + c] * bottom_alpha) / alpha)
+ out[p + 3] = alpha
+ return out
+
+
+def read_png(path):
+ data = open(path, "rb").read()
+ position = 8
+ compressed = bytearray()
+ while position < len(data):
+ length = struct.unpack(">I", data[position:position + 4])[0]
+ kind = data[position + 4:position + 8]
+ body = data[position + 8:position + 8 + length]
+ if kind == b"IHDR":
+ w, h, depth, color, _, _, interlace = struct.unpack(">IIBBBBB", body)
+ if depth != 8 or interlace or color not in (2, 6):
+ sys.exit(f"{path}: unsupported PNG format")
+ elif kind == b"IDAT":
+ compressed += body
+ position += 12 + length
+ channels = 4 if color == 6 else 3
+ raw = zlib.decompress(bytes(compressed))
+ stride = w * channels
+ pixels = bytearray(h * stride)
+ previous = bytearray(stride)
+ for y in range(h):
+ line = unfilter(raw[y * (stride + 1)], bytearray(raw[y * (stride + 1) + 1:(y + 1) * (stride + 1)]), previous, channels)
+ pixels[y * stride:(y + 1) * stride] = line
+ previous = line
+ if channels == 4:
+ return w, h, pixels
+ rgba = bytearray(w * h * 4)
+ for c in range(3):
+ rgba[c::4] = pixels[c::3]
+ rgba[3::4] = b"\xff" * (w * h)
+ return w, h, rgba
+
+
+def unfilter(kind, line, previous, channels):
+ for x in range(len(line)):
+ left = line[x - channels] if x >= channels else 0
+ up = previous[x]
+ up_left = previous[x - channels] if x >= channels else 0
+ if kind == 1:
+ line[x] = (line[x] + left) & 255
+ elif kind == 2:
+ line[x] = (line[x] + up) & 255
+ elif kind == 3:
+ line[x] = (line[x] + ((left + up) >> 1)) & 255
+ elif kind == 4:
+ estimate = left + up - up_left
+ nearest = min((abs(estimate - left), 0, left), (abs(estimate - up), 1, up), (abs(estimate - up_left), 2, up_left))[2]
+ line[x] = (line[x] + nearest) & 255
+ return line
+
+
+def write_png(path, w, h, rgba):
+ stride = w * 4
+ rows = bytearray()
+ for y in range(h):
+ line = rgba[y * stride:(y + 1) * stride]
+ rows += b"\x01" + bytes(line[:4]) + bytes((line[x] - line[x - 4]) & 255 for x in range(4, stride))
+
+ def chunk(kind, body):
+ return struct.pack(">I", len(body)) + kind + body + struct.pack(">I", zlib.crc32(kind + body))
+
+ with open(path, "wb") as f:
+ f.write(b"\x89PNG\r\n\x1a\n")
+ f.write(chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 6, 0, 0, 0)))
+ f.write(chunk(b"IDAT", zlib.compress(bytes(rows), 9)))
+ f.write(chunk(b"IEND", b""))
+
+
+def read_webp(path):
+ data = open(path, "rb").read()
+ if data[0:4] != b"RIFF" or data[8:12] != b"WEBP":
+ sys.exit(f"{path}: not a WebP image")
+ position = 12
+ while data[position:position + 4] != b"VP8L":
+ if position >= len(data):
+ sys.exit(f"{path}: not a lossless WebP image")
+ position += 8 + (struct.unpack("{w * h}I", *pixels)
+ rgba = bytearray(w * h * 4)
+ for c in range(4):
+ rgba[c::4] = argb[(c + 1) % 4::4]
+ return w, h, rgba
+
+
+class Bits:
+ def __init__(self, data):
+ self.data = data
+ self.position = 0
+
+ def read(self, n):
+ value = 0
+ for i in range(n):
+ value |= (self.data[self.position >> 3] >> (self.position & 7) & 1) << i
+ self.position += 1
+ return value
+
+
+def subsample(size, bits):
+ return (size + (1 << bits) - 1) >> bits
+
+
+def read_image(bits, w, h, argb_image):
+ cache_bits = 0
+ if bits.read(1):
+ cache_bits = bits.read(4)
+ if not 1 <= cache_bits <= 11:
+ sys.exit(f"invalid color cache size {cache_bits}")
+ cache_size = 1 << cache_bits if cache_bits else 0
+ prefix_bits = 0
+ groups_image = None
+ if argb_image and bits.read(1):
+ prefix_bits = bits.read(3) + 2
+ groups_image = [p >> 8 & 0xFFFF for p in read_image(bits, subsample(w, prefix_bits), subsample(h, prefix_bits), False)]
+ groups = [[read_code(bits, size) for size in (280 + cache_size, 256, 256, 256, 40)] for _ in range(max(groups_image) + 1 if groups_image else 1)]
+ groups_width = subsample(w, prefix_bits)
+ cache = [0] * cache_size
+ pixels = [0] * (w * h)
+ i = cached = 0
+ while i < len(pixels):
+ codes = groups[groups_image[(i // w >> prefix_bits) * groups_width + (i % w >> prefix_bits)]] if groups_image else groups[0]
+ symbol = decode(bits, codes[0])
+ if symbol < 256:
+ red, blue, alpha = (decode(bits, code) for code in codes[1:4])
+ pixels[i] = alpha << 24 | red << 16 | symbol << 8 | blue
+ i += 1
+ elif symbol < 280:
+ length = prefix_value(bits, symbol - 256)
+ code = prefix_value(bits, decode(bits, codes[4]))
+ if code > 120:
+ offset = code - 120
+ else:
+ dx, dy = DISTANCE_MAP[code - 1]
+ offset = max(dx + dy * w, 1)
+ if offset > i or i + length > len(pixels):
+ sys.exit("invalid backward reference")
+ for _ in range(length):
+ pixels[i] = pixels[i - offset]
+ i += 1
+ else:
+ pixels[i] = cache[symbol - 280]
+ i += 1
+ while cached < i and cache_size:
+ cache[(0x1E35A7BD * pixels[cached] & 0xFFFFFFFF) >> 32 - cache_bits] = pixels[cached]
+ cached += 1
+ return pixels
+
+
+def read_code(bits, alphabet):
+ lengths = [0] * alphabet
+ if bits.read(1):
+ count = bits.read(1) + 1
+ lengths[bits.read(8 if bits.read(1) else 1)] = 1
+ if count == 2:
+ lengths[bits.read(8)] = 1
+ return prefix_code(lengths)
+ code_length_lengths = [0] * 19
+ for i in range(bits.read(4) + 4):
+ code_length_lengths[CODE_LENGTH_ORDER[i]] = bits.read(3)
+ code_length_code = prefix_code(code_length_lengths)
+ max_symbol = bits.read(2 + 2 * bits.read(3)) + 2 if bits.read(1) else alphabet
+ if max_symbol > alphabet:
+ sys.exit("invalid prefix code size")
+ symbol = 0
+ previous = 8
+ while symbol < alphabet and max_symbol:
+ max_symbol -= 1
+ length = decode(bits, code_length_code)
+ if length < 16:
+ lengths[symbol] = length
+ symbol += 1
+ if length:
+ previous = length
+ else:
+ extra, offset = ((2, 3), (3, 3), (7, 11))[length - 16]
+ repeat = bits.read(extra) + offset
+ if symbol + repeat > alphabet:
+ sys.exit("invalid prefix code lengths")
+ lengths[symbol:symbol + repeat] = [previous if length == 16 else 0] * repeat
+ symbol += repeat
+ return prefix_code(lengths)
+
+
+def prefix_code(lengths):
+ used = [symbol for symbol, length in enumerate(lengths) if length]
+ if len(used) == 1:
+ return used[0]
+ counts = [0] * 16
+ for symbol in used:
+ counts[lengths[symbol]] += 1
+ return counts, sorted(used, key=lambda symbol: (lengths[symbol], symbol))
+
+
+def decode(bits, code):
+ if isinstance(code, int):
+ return code
+ counts, symbols = code
+ value = first = index = 0
+ for length in range(1, 16):
+ value |= bits.read(1)
+ if value - first < counts[length]:
+ return symbols[index + value - first]
+ index += counts[length]
+ first = (first + counts[length]) << 1
+ value <<= 1
+ sys.exit("invalid prefix code")
+
+
+def prefix_value(bits, code):
+ if code < 4:
+ return code + 1
+ extra = (code - 2) >> 1
+ return ((2 + (code & 1)) << extra) + bits.read(extra) + 1
+
+
+def add_pixels(a, b):
+ return (a & 0xFF00FF00) + (b & 0xFF00FF00) & 0xFF00FF00 | (a & 0x00FF00FF) + (b & 0x00FF00FF) & 0x00FF00FF
+
+
+def average2(a, b):
+ return (((a ^ b) & 0xFEFEFEFE) >> 1) + (a & b)
+
+
+def per_channel(f, *pixels):
+ return sum(f(*(p >> shift & 255 for p in pixels)) << shift for shift in (0, 8, 16, 24))
+
+
+def clamp(c):
+ return min(max(c, 0), 255)
+
+
+def select(left, top, top_left):
+ to_left = sum(abs((top >> shift & 255) - (top_left >> shift & 255)) for shift in (0, 8, 16, 24))
+ to_top = sum(abs((left >> shift & 255) - (top_left >> shift & 255)) for shift in (0, 8, 16, 24))
+ return left if to_left < to_top else top
+
+
+PREDICTORS = (
+ lambda left, top, top_right, top_left: 0xFF000000,
+ lambda left, top, top_right, top_left: left,
+ lambda left, top, top_right, top_left: top,
+ lambda left, top, top_right, top_left: top_right,
+ lambda left, top, top_right, top_left: top_left,
+ lambda left, top, top_right, top_left: average2(average2(left, top_right), top),
+ lambda left, top, top_right, top_left: average2(left, top_left),
+ lambda left, top, top_right, top_left: average2(left, top),
+ lambda left, top, top_right, top_left: average2(top_left, top),
+ lambda left, top, top_right, top_left: average2(top, top_right),
+ lambda left, top, top_right, top_left: average2(average2(left, top_left), average2(top, top_right)),
+ lambda left, top, top_right, top_left: select(left, top, top_left),
+ lambda left, top, top_right, top_left: per_channel(lambda a, b, c: clamp(a + b - c), left, top, top_left),
+ lambda left, top, top_right, top_left: per_channel(lambda a, b: clamp(a + int((a - b) / 2)), average2(left, top), top_left),
+ lambda left, top, top_right, top_left: 0xFF000000,
+ lambda left, top, top_right, top_left: 0xFF000000,
+)
+
+
+def inverse_predictor(pixels, w, h, size_bits, modes):
+ blocks_width = subsample(w, size_bits)
+ for i in range(len(pixels)):
+ y, x = divmod(i, w)
+ if y == 0:
+ prediction = pixels[i - 1] if x else 0xFF000000
+ elif x == 0:
+ prediction = pixels[i - w]
+ else:
+ mode = modes[(y >> size_bits) * blocks_width + (x >> size_bits)] >> 8 & 15
+ prediction = PREDICTORS[mode](pixels[i - 1], pixels[i - w], pixels[i - w + 1], pixels[i - w - 1])
+ pixels[i] = add_pixels(pixels[i], prediction)
+ return pixels
+
+
+def color_delta(t, c):
+ return (t - (t >> 7 << 8)) * (c - (c >> 7 << 8)) >> 5
+
+
+def inverse_color(pixels, w, h, size_bits, elements):
+ blocks_width = subsample(w, size_bits)
+ for i, p in enumerate(pixels):
+ y, x = divmod(i, w)
+ element = elements[(y >> size_bits) * blocks_width + (x >> size_bits)]
+ green = p >> 8 & 255
+ red = (p >> 16 & 255) + color_delta(element & 255, green) & 255
+ blue = (p & 255) + color_delta(element >> 8 & 255, green) + color_delta(element >> 16 & 255, red) & 255
+ pixels[i] = p & 0xFF00FF00 | red << 16 | blue
+ return pixels
+
+
+def inverse_subtract_green(pixels, w, h, size_bits, data):
+ for i, p in enumerate(pixels):
+ green = p >> 8 & 255
+ pixels[i] = p & 0xFF00FF00 | ((p >> 16) + green & 255) << 16 | (p + green & 255)
+ return pixels
+
+
+def inverse_color_indexing(pixels, w, h, width_bits, table):
+ packed_width = subsample(w, width_bits)
+ index_bits = 8 >> width_bits
+ out = [0] * (w * h)
+ for y in range(h):
+ for x in range(w):
+ index = (pixels[y * packed_width + (x >> width_bits)] >> 8 + (x & (1 << width_bits) - 1) * index_bits) & (1 << index_bits) - 1
+ out[y * w + x] = table[index] if index < len(table) else 0
+ return out
+
+
+if __name__ == "__main__":
+ main()
diff --git a/website/.eleventy.js b/website/.eleventy.js
index 5659db8d42..85f1b0de54 100644
--- a/website/.eleventy.js
+++ b/website/.eleventy.js
@@ -568,7 +568,7 @@ module.exports = function (ty) {
const markdownLib = markdownIt({
html: true,
- breaks: true,
+ breaks: false,
linkify: true,
replaceLink: replaceLink
}).use(markdownItAnchor, {
diff --git a/website/langs/ar.json b/website/langs/ar.json
index 7b3b1eb43f..8418091902 100644
--- a/website/langs/ar.json
+++ b/website/langs/ar.json
@@ -211,11 +211,8 @@
"protocol-3-text": "بروتوكولات P2P",
"simplex-unique-overlay-card-4-p-2": "تستخدم شبكة بروتوكول مفتوح وتوفرSDK لإنشاء روبوتات دردشة، مما يسمح بتنفيذ الخدمات التي يمكن للمستخدمين التفاعل معها عبر تطبيقات SimpleX Chat — التي تتطلع حقًا لمعرفة خدمات SimpleX التي ستبنيها.",
"guide-dropdown-1": "بداية سريعة",
- "guide-dropdown-2": "إرسال الرسائل",
- "guide-dropdown-3": "مجموعات سرية",
"guide-dropdown-4": "ملفات تعريف الدردشة",
"guide-dropdown-5": "إدارة البيانات",
- "guide-dropdown-6": "مكالمات الصوت والفيديو",
"guide-dropdown-7": "الخصوصية و الأمان",
"guide-dropdown-8": "إعدادات التطبيق",
"guide-dropdown-9": "إجراء اتصالات",
diff --git a/website/langs/az.json b/website/langs/az.json
index 68efab8b81..99f17f42e8 100644
--- a/website/langs/az.json
+++ b/website/langs/az.json
@@ -125,7 +125,6 @@
"why-p3": "",
"simplex-unique-3-title": "",
"file-empty": "",
- "guide-dropdown-3": "",
"no-resilient": "",
"hide-info": "",
"privacy-matters-overlay-card-3-p-4": "",
@@ -182,7 +181,6 @@
"simplex-unique-overlay-card-2-p-2": "",
"simplex-unique-4-overlay-1-title": "",
"file-sec-3": "",
- "guide-dropdown-2": "",
"simplex-network-overlay-card-1-li-5": "",
"simplex-private-2-title": "",
"hero-overlay-card-1-p-4": "",
@@ -311,7 +309,6 @@
"simplex-private-card-3-point-3": "",
"simplex-private-card-10-point-1": "",
"file-available": "",
- "guide-dropdown-6": "",
"file-noscript": "",
"more-info": "",
"no-decentralized": "",
diff --git a/website/langs/bg.json b/website/langs/bg.json
index 654e612596..6312e016c8 100644
--- a/website/langs/bg.json
+++ b/website/langs/bg.json
@@ -168,7 +168,6 @@
"why-p3": "",
"simplex-unique-3-title": "",
"file-empty": "",
- "guide-dropdown-3": "",
"no-resilient": "",
"hide-info": "",
"privacy-matters-overlay-card-3-p-4": "",
@@ -216,7 +215,6 @@
"simplex-unique-overlay-card-2-p-2": "",
"simplex-unique-4-overlay-1-title": "",
"file-sec-3": "",
- "guide-dropdown-2": "",
"simplex-network-overlay-card-1-li-5": "",
"hero-overlay-card-1-p-4": "",
"about-and-contact-us": "",
@@ -324,7 +322,6 @@
"simplex-private-card-3-point-3": "",
"simplex-private-card-10-point-1": "",
"file-available": "",
- "guide-dropdown-6": "",
"file-noscript": "",
"more-info": "",
"no-decentralized": "",
diff --git a/website/langs/cs.json b/website/langs/cs.json
index 50c471296e..765d26d527 100644
--- a/website/langs/cs.json
+++ b/website/langs/cs.json
@@ -211,11 +211,8 @@
"simplex-network-1-desc": "Všechny zprávy se odesílají přes servery, což zajišťuje lepší soukromí metadat a spolehlivé asynchronní doručování zpráv, přičemž se zamezuje mnoha",
"simplex-private-card-1-point-1": "Double-ratchet protokol —