Compare commits
No commits in common. "stable" and "v0.4.2" have entirely different histories.
1
.github/CODEOWNERS
vendored
|
@ -1 +0,0 @@
|
|||
* @epoberezkin @jr-simplex
|
1
.github/FUNDING.yml
vendored
|
@ -1,2 +1 @@
|
|||
github: simplex-chat
|
||||
open_collective: simplex-chat
|
||||
|
|
68
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
@ -1,68 +0,0 @@
|
|||
name: Bug
|
||||
description: File a bug report/issue
|
||||
title: "[Bug]: "
|
||||
labels: ["bug", "triage"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Multiple selections are possible.
|
||||
multiple: true
|
||||
options:
|
||||
- Linux
|
||||
- Mac
|
||||
- Windows
|
||||
- Android
|
||||
- iOS
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: OS version
|
||||
description: Specify the OS version
|
||||
placeholder: ex. Android 12, Ubuntu 20.04
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: App version
|
||||
description: Specify the SimpleX version
|
||||
placeholder: ex. 4.3.2
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: A concise description of what you're experiencing.
|
||||
placeholder: Bug happened!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A concise description of what you expected to happen.
|
||||
placeholder: No bug should happen!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps To Reproduce
|
||||
description: Steps to reproduce the behavior.
|
||||
placeholder: |
|
||||
1. Go to ...
|
||||
3. Click on ...
|
||||
4. See error...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1 +0,0 @@
|
|||
blank_issues_enabled: true
|
40
.github/ISSUE_TEMPLATE/feature.yml
vendored
|
@ -1,40 +0,0 @@
|
|||
name: Feature
|
||||
description: Suggest your feature
|
||||
title: "[Feature]: "
|
||||
labels: ["enhancement", "triage"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Multiple selections are possible. If selected input is "all", this considered to be a general feature.
|
||||
multiple: true
|
||||
options:
|
||||
- Linux
|
||||
- Mac
|
||||
- Windows
|
||||
- Android
|
||||
- iOS
|
||||
- all
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: App version
|
||||
description: Specify the SimpleX version
|
||||
placeholder: ex. 4.3.2
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Feature
|
||||
description: Describe the feature you would like to see added
|
||||
placeholder: SimpleX Chat should make me coffee!
|
||||
validations:
|
||||
required: true
|
16
.github/ISSUE_TEMPLATE/question.yml
vendored
|
@ -1,16 +0,0 @@
|
|||
name: Question
|
||||
description: Ask your question
|
||||
title: "[Q]: "
|
||||
labels: ["question", "triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Generally, we encourage you to ask questions in our [official group](https://simplex.chat/invitation/#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3Dsimplex:/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D), but you can do it anyway :)
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Question
|
||||
description: Please ask your question in plain english.
|
||||
placeholder: Is SimpleX - chat?
|
||||
validations:
|
||||
required: true
|
47
.github/actions/prepare-build/action.yml
vendored
|
@ -1,47 +0,0 @@
|
|||
name: "Prebuilt steps for build"
|
||||
description: "Reusable steps for multiple jobs"
|
||||
inputs:
|
||||
java_ver:
|
||||
required: true
|
||||
description: "Java version to install"
|
||||
ghc_ver:
|
||||
required: true
|
||||
description: "GHC version to install"
|
||||
github_ref:
|
||||
required: true
|
||||
description: "Git reference"
|
||||
os:
|
||||
required: true
|
||||
description: "Target OS"
|
||||
cache_path:
|
||||
required: false
|
||||
default: "~/.cabal/store"
|
||||
description: "Cache path"
|
||||
cabal_ver:
|
||||
required: false
|
||||
default: 3.10.1.0
|
||||
description: "GHC version to install"
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Setup Haskell
|
||||
uses: simplex-chat/setup-haskell-action@v2
|
||||
with:
|
||||
ghc-version: ${{ inputs.ghc_ver }}
|
||||
cabal-version: ${{ inputs.cabal_ver }}
|
||||
|
||||
- name: Setup Java
|
||||
if: startsWith(inputs.github_ref, 'refs/tags/v')
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'corretto'
|
||||
java-version: ${{ inputs.java_ver }}
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Restore cached build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ inputs.cache_path }}
|
||||
dist-newstyle
|
||||
key: ${{ inputs.os }}-ghc${{ inputs.ghc_ver }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}
|
39
.github/actions/prepare-release/action.yml
vendored
|
@ -1,39 +0,0 @@
|
|||
name: "Upload binary and update hash"
|
||||
description: "Reusable steps for multiple jobs"
|
||||
inputs:
|
||||
bin_path:
|
||||
required: true
|
||||
description: "Path to binary to upload"
|
||||
bin_name:
|
||||
required: true
|
||||
description: "Name of uploaded binary"
|
||||
bin_hash:
|
||||
required: true
|
||||
description: "Message with SHA to include in release"
|
||||
github_ref:
|
||||
required: true
|
||||
description: "Github reference"
|
||||
github_token:
|
||||
required: true
|
||||
description: "Github token"
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Upload file with specific name
|
||||
if: startsWith(inputs.github_ref, 'refs/tags/v')
|
||||
uses: simplex-chat/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ inputs.github_token }}
|
||||
file: ${{ inputs.bin_path }}
|
||||
asset_name: ${{ inputs.bin_name }}
|
||||
tag: ${{ inputs.github_ref }}
|
||||
|
||||
- name: Add hash to release notes
|
||||
if: startsWith(inputs.github_ref, 'refs/tags/v')
|
||||
uses: simplex-chat/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.github_token }}
|
||||
with:
|
||||
append_body: true
|
||||
body: |
|
||||
${{ inputs.bin_hash }}
|
44
.github/actions/swap/action.yml
vendored
|
@ -1,44 +0,0 @@
|
|||
name: 'Set Swap Space'
|
||||
description: 'Add moar swap'
|
||||
branding:
|
||||
icon: 'crop'
|
||||
color: 'orange'
|
||||
inputs:
|
||||
swap-size-gb:
|
||||
description: 'Swap space to create, in Gigabytes.'
|
||||
required: false
|
||||
default: '10'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Swap space report before modification
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Memory and swap:"
|
||||
free -h
|
||||
echo
|
||||
swapon --show
|
||||
echo
|
||||
- name: Set Swap
|
||||
shell: bash
|
||||
run: |
|
||||
export SWAP_FILE=$(swapon --show=NAME | tail -n 1)
|
||||
echo "Swap file: $SWAP_FILE"
|
||||
if [ -z "$SWAP_FILE" ]; then
|
||||
SWAP_FILE=/opt/swapfile
|
||||
else
|
||||
sudo swapoff $SWAP_FILE
|
||||
sudo rm $SWAP_FILE
|
||||
fi
|
||||
sudo fallocate -l ${{ inputs.swap-size-gb }}G $SWAP_FILE
|
||||
sudo chmod 600 $SWAP_FILE
|
||||
sudo mkswap $SWAP_FILE
|
||||
sudo swapon $SWAP_FILE
|
||||
- name: Swap space report after modification
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Memory and swap:"
|
||||
free -h
|
||||
echo
|
||||
swapon --show
|
||||
echo
|
4
.github/changelog_conf.json
vendored
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"template": "Commits:\n${{UNCATEGORIZED}}",
|
||||
"pr_template": "- ${{TITLE}}"
|
||||
"template": "${{UNCATEGORIZED}}",
|
||||
"pr_template": "- ${{TITLE}}\n"
|
||||
}
|
||||
|
|
531
.github/workflows/build.yml
vendored
|
@ -4,76 +4,22 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable
|
||||
- v4
|
||||
tags:
|
||||
- "v*"
|
||||
- "!*-fdroid"
|
||||
- "!*-armv7a"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "apps/ios"
|
||||
- "apps/multiplatform"
|
||||
- "blog"
|
||||
- "docs"
|
||||
- "fastlane"
|
||||
- "images"
|
||||
- "packages"
|
||||
- "website"
|
||||
- "README.md"
|
||||
- "PRIVACY.md"
|
||||
|
||||
# This workflow uses custom actions (prepare-build and prepare-release) defined in:
|
||||
#
|
||||
# .github/actions/
|
||||
# ├── prepare-build
|
||||
# │ └── action.yml
|
||||
# └── prepare-release
|
||||
# └── action.yml
|
||||
|
||||
# Important!
|
||||
# Do not use always(), it makes build unskippable.
|
||||
# See: https://github.com/actions/runner/issues/1846#issuecomment-1246102753
|
||||
|
||||
jobs:
|
||||
|
||||
# =============================
|
||||
# Global variables
|
||||
# =============================
|
||||
|
||||
# That is the only and less hacky way to setup global variables
|
||||
# to use in strategy matrix (env:/YAML anchors doesn't work).
|
||||
# See: https://github.com/orgs/community/discussions/56787#discussioncomment-6041789
|
||||
# https://github.com/actions/runner/issues/1182
|
||||
# https://stackoverflow.com/a/77549656
|
||||
|
||||
variables:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
GHC_VER: 9.6.3
|
||||
JAVA_VER: 17
|
||||
steps:
|
||||
- name: Dummy job when we have just simple variables
|
||||
if: false
|
||||
run: echo
|
||||
|
||||
# =============================
|
||||
# Create release
|
||||
# =============================
|
||||
|
||||
# Create release, but only if it's triggered by tag push.
|
||||
# On pull requests/commits push, this job will always complete.
|
||||
|
||||
maybe-release:
|
||||
prepare-release:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone project
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build changelog
|
||||
id: build_changelog
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: simplex-chat/release-changelog-builder-action@v5
|
||||
uses: mikepenz/release-changelog-builder-action@v1
|
||||
with:
|
||||
configuration: .github/changelog_conf.json
|
||||
failOnError: true
|
||||
|
@ -83,450 +29,73 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: simplex-chat/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body: ${{ steps.build_changelog.outputs.changelog }}
|
||||
prerelease: true
|
||||
files: |
|
||||
LICENSE
|
||||
fail_on_unmatched_files: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# =========================
|
||||
# Linux Build
|
||||
# =========================
|
||||
|
||||
build-linux:
|
||||
name: "ubuntu-${{ matrix.os }} (CLI,Desktop), GHC: ${{ matrix.ghc }}"
|
||||
needs: [maybe-release, variables]
|
||||
runs-on: ubuntu-${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: 22.04
|
||||
ghc: "8.10.7"
|
||||
should_run: ${{ !(github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }}
|
||||
- os: 22.04
|
||||
ghc: ${{ needs.variables.outputs.GHC_VER }}
|
||||
cli_asset_name: simplex-chat-ubuntu-22_04-x86-64
|
||||
desktop_asset_name: simplex-desktop-ubuntu-22_04-x86_64.deb
|
||||
should_run: true
|
||||
- os: 24.04
|
||||
ghc: ${{ needs.variables.outputs.GHC_VER }}
|
||||
cli_asset_name: simplex-chat-ubuntu-24_04-x86-64
|
||||
desktop_asset_name: simplex-desktop-ubuntu-24_04-x86_64.deb
|
||||
should_run: true
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
if: matrix.should_run == true
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup swap
|
||||
if: matrix.ghc == '8.10.7' && matrix.should_run == true
|
||||
uses: ./.github/actions/swap
|
||||
with:
|
||||
swap-size-gb: 30
|
||||
|
||||
# Otherwise we run out of disk space with Docker build
|
||||
- name: Free disk space
|
||||
if: matrix.should_run == true
|
||||
shell: bash
|
||||
run: ./scripts/ci/linux_util_free_space.sh
|
||||
|
||||
- name: Restore cached build
|
||||
if: matrix.should_run == true
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cabal/store
|
||||
dist-newstyle
|
||||
key: ubuntu-${{ matrix.os }}-ghc${{ matrix.ghc }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
if: matrix.should_run == true
|
||||
uses: simplex-chat/docker-setup-buildx-action@v3
|
||||
|
||||
- name: Build and cache Docker image
|
||||
if: matrix.should_run == true
|
||||
uses: simplex-chat/docker-build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
load: true
|
||||
file: Dockerfile.build
|
||||
tags: build/${{ matrix.os }}:latest
|
||||
build-args: |
|
||||
TAG=${{ matrix.os }}
|
||||
GHC=${{ matrix.ghc }}
|
||||
|
||||
# Docker needs these flags for AppImage build:
|
||||
# --device /dev/fuse
|
||||
# --cap-add SYS_ADMIN
|
||||
# --security-opt apparmor:unconfined
|
||||
- name: Start container
|
||||
if: matrix.should_run == true
|
||||
shell: bash
|
||||
run: |
|
||||
docker run -t -d \
|
||||
--device /dev/fuse \
|
||||
--cap-add SYS_ADMIN \
|
||||
--security-opt apparmor:unconfined \
|
||||
--name builder \
|
||||
-v ~/.cabal:/root/.cabal \
|
||||
-v /home/runner/work/_temp:/home/runner/work/_temp \
|
||||
-v ${{ github.workspace }}:/project \
|
||||
build/${{ matrix.os }}:latest
|
||||
|
||||
- name: Prepare cabal.project.local
|
||||
if: matrix.should_run == true
|
||||
shell: bash
|
||||
run: |
|
||||
echo "ignore-project: False" >> cabal.project.local
|
||||
echo "package direct-sqlcipher" >> cabal.project.local
|
||||
echo " flags: +openssl" >> cabal.project.local
|
||||
|
||||
# chmod/git commands are used to workaround permission issues when cache is restored
|
||||
- name: Build CLI
|
||||
if: matrix.should_run == true
|
||||
shell: docker exec -t builder sh -eu {0}
|
||||
run: |
|
||||
chmod -R 777 dist-newstyle ~/.cabal && git config --global --add safe.directory '*'
|
||||
cabal clean
|
||||
cabal update
|
||||
cabal build -j --enable-tests
|
||||
mkdir -p /out
|
||||
for i in simplex-chat simplex-chat-test; do
|
||||
bin=$(find /project/dist-newstyle -name "$i" -type f -executable)
|
||||
chmod +x "$bin"
|
||||
mv "$bin" /out/
|
||||
done
|
||||
strip /out/simplex-chat
|
||||
|
||||
- name: Copy tests from container
|
||||
if: matrix.should_run == true
|
||||
shell: bash
|
||||
run: |
|
||||
docker cp builder:/out/simplex-chat-test .
|
||||
|
||||
- name: Copy CLI from container and prepare it
|
||||
id: linux_cli_prepare
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
|
||||
shell: bash
|
||||
run: |
|
||||
docker cp builder:/out/simplex-chat ./${{ matrix.cli_asset_name }}
|
||||
path="${{ github.workspace }}/${{ matrix.cli_asset_name }}"
|
||||
echo "bin_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "bin_hash=$(echo SHA2-256\(${{ matrix.cli_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload CLI
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
bin_path: ${{ steps.linux_cli_prepare.outputs.bin_path }}
|
||||
bin_name: ${{ matrix.cli_asset_name }}
|
||||
bin_hash: ${{ steps.linux_cli_prepare.outputs.bin_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build Desktop
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
|
||||
shell: docker exec -t builder sh -eu {0}
|
||||
run: |
|
||||
scripts/desktop/build-lib-linux.sh
|
||||
cd apps/multiplatform
|
||||
./gradlew packageDeb
|
||||
|
||||
- name: Prepare Desktop
|
||||
id: linux_desktop_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
|
||||
shell: bash
|
||||
run: |
|
||||
path=$(echo ${{ github.workspace }}/apps/multiplatform/release/main/deb/simplex_*_amd64.deb )
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "package_hash=$(echo SHA2-256\(${{ matrix.desktop_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Desktop
|
||||
uses: ./.github/actions/prepare-release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
|
||||
with:
|
||||
bin_path: ${{ steps.linux_desktop_build.outputs.package_path }}
|
||||
bin_name: ${{ matrix.desktop_asset_name }}
|
||||
bin_hash: ${{ steps.linux_desktop_build.outputs.package_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build AppImage
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == '22.04' && matrix.should_run == true
|
||||
shell: docker exec -t builder sh -eu {0}
|
||||
run: |
|
||||
scripts/desktop/make-appimage-linux.sh
|
||||
|
||||
- name: Prepare AppImage
|
||||
id: linux_appimage_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == '22.04' && matrix.should_run == true
|
||||
shell: bash
|
||||
run: |
|
||||
path=$(echo ${{ github.workspace }}/apps/multiplatform/release/main/*imple*.AppImage)
|
||||
echo "appimage_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "appimage_hash=$(echo SHA2-256\(simplex-desktop-x86_64.AppImage\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload AppImage
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == '22.04' && matrix.should_run == true
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
bin_path: ${{ steps.linux_appimage_build.outputs.appimage_path }}
|
||||
bin_name: "simplex-desktop-x86_64.AppImage"
|
||||
bin_hash: ${{ steps.linux_appimage_build.outputs.appimage_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Fix permissions for cache
|
||||
if: matrix.should_run == true
|
||||
shell: bash
|
||||
run: |
|
||||
sudo chmod -R 777 dist-newstyle ~/.cabal
|
||||
sudo chown -R $(id -u):$(id -g) dist-newstyle ~/.cabal
|
||||
|
||||
- name: Run tests
|
||||
if: matrix.should_run == true
|
||||
timeout-minutes: 120
|
||||
shell: bash
|
||||
run: |
|
||||
i=1
|
||||
attempts=1
|
||||
${{ (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} && attempts=3
|
||||
while [ "$i" -le "$attempts" ]; do
|
||||
if ./simplex-chat-test; then
|
||||
break
|
||||
else
|
||||
echo "Attempt $i failed, retrying..."
|
||||
i=$((i + 1))
|
||||
sleep 1
|
||||
fi
|
||||
done
|
||||
if [ "$i" -gt "$attempts" ]; then
|
||||
echo "All "$attempts" attempts failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# =========================
|
||||
# MacOS Build
|
||||
# =========================
|
||||
|
||||
build-macos:
|
||||
name: "${{ matrix.os }} (CLI,Desktop), GHC: ${{ matrix.ghc }}"
|
||||
needs: [maybe-release, variables]
|
||||
build:
|
||||
name: build-${{ matrix.os }}
|
||||
if: always()
|
||||
needs: prepare-release
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
cache_path: ~/.stack
|
||||
stack_args: "--test"
|
||||
artifact_rel_path: /bin/simplex-chat
|
||||
asset_name: simplex-chat-ubuntu-20_04-x86-64
|
||||
- os: ubuntu-18.04
|
||||
cache_path: ~/.stack
|
||||
stack_args: "--test"
|
||||
artifact_rel_path: /bin/simplex-chat
|
||||
asset_name: simplex-chat-ubuntu-18_04-x86-64
|
||||
- os: macos-latest
|
||||
ghc: ${{ needs.variables.outputs.GHC_VER }}
|
||||
cli_asset_name: simplex-chat-macos-aarch64
|
||||
desktop_asset_name: simplex-desktop-macos-aarch64.dmg
|
||||
openssl_dir: "/opt/homebrew/opt"
|
||||
- os: macos-13
|
||||
ghc: ${{ needs.variables.outputs.GHC_VER }}
|
||||
cli_asset_name: simplex-chat-macos-x86-64
|
||||
desktop_asset_name: simplex-desktop-macos-x86_64.dmg
|
||||
openssl_dir: "/usr/local/opt"
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare build
|
||||
uses: ./.github/actions/prepare-build
|
||||
with:
|
||||
java_ver: ${{ needs.variables.outputs.JAVA_VER }}
|
||||
ghc_ver: ${{ matrix.ghc }}
|
||||
os: ${{ matrix.os }}
|
||||
github_ref: ${{ github.ref }}
|
||||
|
||||
- name: Install OpenSSL
|
||||
run: brew install openssl@3.0
|
||||
|
||||
- name: Prepare cabal.project.local
|
||||
shell: bash
|
||||
run: |
|
||||
echo "ignore-project: False" >> cabal.project.local
|
||||
echo "package simplexmq" >> cabal.project.local
|
||||
echo " extra-include-dirs: ${{ matrix.opnessl_dir }}/openssl@3.0/include" >> cabal.project.local
|
||||
echo " extra-lib-dirs: ${{ matrix.openssl_dir}}/openssl@3.0/lib" >> cabal.project.local
|
||||
echo "" >> cabal.project.local
|
||||
echo "package direct-sqlcipher" >> cabal.project.local
|
||||
echo " extra-include-dirs: ${{ matrix.openssl_dir }}/openssl@3.0/include" >> cabal.project.local
|
||||
echo " extra-lib-dirs: ${{ matrix.openssl_dir }}/openssl@3.0/lib" >> cabal.project.local
|
||||
echo " flags: +openssl" >> cabal.project.local
|
||||
|
||||
- name: Build CLI
|
||||
id: mac_cli_build
|
||||
shell: bash
|
||||
run: |
|
||||
cabal build -j --enable-tests
|
||||
path=$(cabal list-bin simplex-chat)
|
||||
echo "bin_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "bin_hash=$(echo SHA2-256\(${{ matrix.cli_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload CLI
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
bin_path: ${{ steps.mac_cli_build.outputs.bin_path }}
|
||||
bin_name: ${{ matrix.cli_asset_name }}
|
||||
bin_hash: ${{ steps.mac_cli_build.outputs.bin_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build Desktop
|
||||
id: mac_desktop_build
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
shell: bash
|
||||
env:
|
||||
APPLE_SIMPLEX_SIGNING_KEYCHAIN: ${{ secrets.APPLE_SIMPLEX_SIGNING_KEYCHAIN }}
|
||||
APPLE_SIMPLEX_NOTARIZATION_APPLE_ID: ${{ secrets.APPLE_SIMPLEX_NOTARIZATION_APPLE_ID }}
|
||||
APPLE_SIMPLEX_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_SIMPLEX_NOTARIZATION_PASSWORD }}
|
||||
run: |
|
||||
scripts/ci/build-desktop-mac.sh
|
||||
path=$(echo $PWD/apps/multiplatform/release/main/dmg/SimpleX-*.dmg)
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "package_hash=$(echo SHA2-256\(${{ matrix.desktop_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Desktop
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
bin_path: ${{ steps.mac_desktop_build.outputs.package_path }}
|
||||
bin_name: ${{ matrix.desktop_asset_name }}
|
||||
bin_hash: ${{ steps.mac_desktop_build.outputs.package_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run tests
|
||||
timeout-minutes: 120
|
||||
shell: bash
|
||||
run: |
|
||||
i=1
|
||||
attempts=1
|
||||
${{ (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} && attempts=3
|
||||
while [ "$i" -le "$attempts" ]; do
|
||||
if cabal test --test-show-details=direct; then
|
||||
break
|
||||
else
|
||||
echo "Attempt $i failed, retrying..."
|
||||
i=$((i + 1))
|
||||
sleep 1
|
||||
fi
|
||||
done
|
||||
if [ "$i" -gt "$attempts" ]; then
|
||||
echo "All "$attempts" attempts failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# =========================
|
||||
# Windows Build
|
||||
# =========================
|
||||
|
||||
build-windows:
|
||||
name: "${{ matrix.os }} (CLI,Desktop), GHC: ${{ matrix.ghc }}"
|
||||
needs: [maybe-release, variables]
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
cache_path: ~/.stack
|
||||
stack_args: "--test"
|
||||
artifact_rel_path: /bin/simplex-chat
|
||||
asset_name: simplex-chat-macos-x86-64
|
||||
# TODO enable tests for windows once fixed (remove stack_args altogether)
|
||||
- os: windows-latest
|
||||
ghc: ${{ needs.variables.outputs.GHC_VER }}
|
||||
cli_asset_name: simplex-chat-windows-x86-64
|
||||
desktop_asset_name: simplex-desktop-windows-x86_64.msi
|
||||
cache_path: C:/sr
|
||||
stack_args: ""
|
||||
artifact_rel_path: /bin/simplex-chat.exe
|
||||
asset_name: simplex-chat-windows-x86-64
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
- name: Clone project
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Prepare build
|
||||
uses: ./.github/actions/prepare-build
|
||||
- name: Setup Stack
|
||||
uses: haskell/actions/setup@v1
|
||||
with:
|
||||
java_ver: ${{ needs.variables.outputs.JAVA_VER }}
|
||||
ghc_ver: ${{ matrix.ghc }}
|
||||
os: ${{ matrix.os }}
|
||||
cache_path: "C:/cabal"
|
||||
github_ref: ${{ github.ref }}
|
||||
ghc-version: '8.8.4'
|
||||
enable-stack: true
|
||||
stack-version: 'latest'
|
||||
|
||||
- name: Configure pagefile (Windows)
|
||||
uses: simplex-chat/configure-pagefile-action@v1.4
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
minimum-size: 16GB
|
||||
maximum-size: 16GB
|
||||
disk-root: "C:"
|
||||
path: ${{ matrix.cache_path }}
|
||||
key: ${{ matrix.os }}-${{ hashFiles('stack.yaml') }}
|
||||
|
||||
- name: 'Setup MSYS2'
|
||||
uses: simplex-chat/setup-msys2@v2
|
||||
with:
|
||||
msystem: ucrt64
|
||||
update: true
|
||||
install: >-
|
||||
git
|
||||
perl
|
||||
make
|
||||
pacboy: >-
|
||||
toolchain:p
|
||||
cmake:p
|
||||
|
||||
# rm -rf dist-newstyle/src/direct-sq* is here because of the bug in cabal's dependency which prevents second build from finishing
|
||||
- name: Build CLI
|
||||
id: windows_cli_build
|
||||
shell: msys2 {0}
|
||||
- name: Build & test
|
||||
id: build_test
|
||||
run: |
|
||||
export PATH=$PATH:/c/ghcup/bin:$(echo /c/tools/ghc-*/bin || echo)
|
||||
scripts/desktop/prepare-openssl-windows.sh
|
||||
openssl_windows_style_path=$(echo `pwd`/dist-newstyle/openssl-3.0.15 | sed 's#/\([a-zA-Z]\)#\1:#' | sed 's#/#\\#g')
|
||||
rm cabal.project.local 2>/dev/null || true
|
||||
echo "ignore-project: False" >> cabal.project.local
|
||||
echo "package direct-sqlcipher" >> cabal.project.local
|
||||
echo " flags: +openssl" >> cabal.project.local
|
||||
echo " extra-include-dirs: $openssl_windows_style_path\include" >> cabal.project.local
|
||||
echo " extra-lib-dirs: $openssl_windows_style_path" >> cabal.project.local
|
||||
stack build ${{ matrix.stack_args }}
|
||||
echo "::set-output name=LOCAL_INSTALL_ROOT::$(stack path --local-install-root)"
|
||||
|
||||
rm -rf dist-newstyle/src/direct-sq*
|
||||
sed -i "s/, unix /--, unix /" simplex-chat.cabal
|
||||
cabal build -j --enable-tests
|
||||
rm -rf dist-newstyle/src/direct-sq*
|
||||
path=$(cabal list-bin simplex-chat | tail -n 1)
|
||||
echo "bin_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "bin_hash=$(echo SHA2-256\(${{ matrix.cli_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload CLI
|
||||
- name: Upload binaries to release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: ./.github/actions/prepare-release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
bin_path: ${{ steps.windows_cli_build.outputs.bin_path }}
|
||||
bin_name: ${{ matrix.cli_asset_name }}
|
||||
bin_hash: ${{ steps.windows_cli_build.outputs.bin_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build Desktop
|
||||
id: windows_desktop_build
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
export PATH=$PATH:/c/ghcup/bin:$(echo /c/tools/ghc-*/bin || echo)
|
||||
scripts/desktop/build-lib-windows.sh
|
||||
cd apps/multiplatform
|
||||
./gradlew packageMsi
|
||||
rm -rf dist-newstyle/src/direct-sq*
|
||||
path=$(echo $PWD/release/main/msi/*imple*.msi | sed 's#/\([a-z]\)#\1:#' | sed 's#/#\\#g')
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "package_hash=$(echo SHA2-256\(${{ matrix.desktop_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Desktop
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: ./.github/actions/prepare-release
|
||||
with:
|
||||
bin_path: ${{ steps.windows_desktop_build.outputs.package_path }}
|
||||
bin_name: ${{ matrix.desktop_asset_name }}
|
||||
bin_hash: ${{ steps.windows_desktop_build.outputs.package_hash }}
|
||||
github_ref: ${{ github.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.build_test.outputs.LOCAL_INSTALL_ROOT }}${{ matrix.artifact_rel_path }}
|
||||
asset_name: ${{ matrix.asset_name }}
|
||||
tag: ${{ github.ref }}
|
||||
|
|
42
.github/workflows/cla.yml
vendored
|
@ -1,42 +0,0 @@
|
|||
name: "CLA Assistant"
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_target:
|
||||
types: [opened, closed, synchronize]
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
|
||||
jobs:
|
||||
CLAssistant:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "CLA Assistant"
|
||||
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
|
||||
# Beta Release
|
||||
uses: cla-assistant/github-action@v2.3.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# the below token should have repo scope and must be manually added by you in the repository's secret
|
||||
PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||
with:
|
||||
path-to-signatures: 'signatures/v1.1/cla.json'
|
||||
path-to-document: 'https://github.com/simplex-chat/cla/blob/master/CLA.md'
|
||||
# branch should not be protected
|
||||
remote-organization-name: simplex-chat
|
||||
remote-repository-name: cla
|
||||
branch: 'master'
|
||||
# allowlist: user1,bot*
|
||||
|
||||
#below are the optional inputs - If the optional inputs are not given, then default values will be taken
|
||||
#create-file-commit-message: 'For example: Creating file for storing CLA Signatures'
|
||||
#signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo'
|
||||
#custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign'
|
||||
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
|
||||
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
|
||||
#lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
|
||||
#use-dco-flag: true - If you are using DCO instead of CLA
|
45
.github/workflows/reproduce-schedule.yml
vendored
|
@ -1,45 +0,0 @@
|
|||
name: Reproduce latest release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # every day at 02:00 night
|
||||
|
||||
jobs:
|
||||
reproduce:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Get latest release
|
||||
shell: bash
|
||||
run: |
|
||||
curl --proto '=https' \
|
||||
--tlsv1.2 \
|
||||
-sSf -L \
|
||||
'https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest' \
|
||||
2>/dev/null | \
|
||||
grep -i "tag_name" | \
|
||||
awk -F \" '{print "TAG="$4}' >> $GITHUB_ENV
|
||||
|
||||
- name: Execute reproduce script
|
||||
run: |
|
||||
${GITHUB_WORKSPACE}/scripts/reproduce-builds.sh "$TAG"
|
||||
|
||||
- name: Check if build has been reproduced
|
||||
env:
|
||||
url: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_URL }}
|
||||
user: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_USER }}
|
||||
pass: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_PASS }}
|
||||
run: |
|
||||
if [ -f "${GITHUB_WORKSPACE}/$TAG/_sha256sums" ]; then
|
||||
exit 0
|
||||
else
|
||||
curl --proto '=https' --tlsv1.2 -sSf \
|
||||
-u "${user}:${pass}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"title": "👾 GitHub: Runner", "description": "⛔️ '"$TAG"' did not reproduce."}' \
|
||||
"$url"
|
||||
exit 1
|
||||
fi
|
39
.github/workflows/web.yml
vendored
|
@ -1,39 +0,0 @@
|
|||
name: Build Eleventy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- website/**
|
||||
- images/**
|
||||
- blog/**
|
||||
- docs/**
|
||||
- .github/workflows/web.yml
|
||||
- PRIVACY.md
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies & build
|
||||
run: |
|
||||
./website/web.sh
|
||||
|
||||
- name: Deploy
|
||||
uses: simplex-chat/actions-gh-pages@v3
|
||||
with:
|
||||
publish_dir: ./website/_site
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
52
.gitignore
vendored
|
@ -5,6 +5,12 @@
|
|||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
|
@ -34,48 +40,12 @@ cabal.project.local
|
|||
cabal.project.local~
|
||||
.HTF/
|
||||
.ghc.environment.*
|
||||
*.cabal
|
||||
stack.yaml.lock
|
||||
|
||||
# Chat database
|
||||
# Idris
|
||||
*.ibc
|
||||
|
||||
# chat database
|
||||
*.db
|
||||
*.db.bak
|
||||
|
||||
# Temporary test files
|
||||
tests/tmp
|
||||
tests/tmp*
|
||||
logs/
|
||||
|
||||
*.devcontainer
|
||||
# for website
|
||||
website/node_modules/
|
||||
website/src/blog/
|
||||
website/src/docs/
|
||||
website/translations.json
|
||||
website/src/img/images/
|
||||
website/src/images/
|
||||
website/src/js/lottie.min.js
|
||||
website/src/privacy.md
|
||||
# Generated files
|
||||
website/package/generated*
|
||||
|
||||
# Ignore build tool output, e.g. code coverage
|
||||
website/.nyc_output/
|
||||
website/coverage/
|
||||
result
|
||||
|
||||
# Ignore API documentation
|
||||
website/api-docs/
|
||||
|
||||
# Ignore folders from source code editors
|
||||
website/.vscode
|
||||
website/.idea
|
||||
|
||||
# Ignore eleventy output when doing manual tests
|
||||
website/_site/
|
||||
|
||||
website/package-lock.json
|
||||
|
||||
# Ignore test files
|
||||
website/.cache
|
||||
website/test/stubs-layout-cache/_includes/*.js
|
||||
apps/android/app/release
|
||||
|
|
43
Dockerfile
|
@ -1,41 +1,10 @@
|
|||
ARG TAG=22.04
|
||||
|
||||
FROM ubuntu:${TAG} AS build
|
||||
|
||||
### Build stage
|
||||
|
||||
# Install curl and git and simplex-chat dependencies
|
||||
RUN apt-get update && apt-get install -y curl git build-essential libgmp3-dev zlib1g-dev llvm-12 llvm-12-dev libnuma-dev libssl-dev
|
||||
|
||||
# Specify bootstrap Haskell versions
|
||||
ENV BOOTSTRAP_HASKELL_GHC_VERSION=9.6.3
|
||||
ENV BOOTSTRAP_HASKELL_CABAL_VERSION=3.10.1.0
|
||||
|
||||
# Install ghcup
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 sh
|
||||
|
||||
# Adjust PATH
|
||||
ENV PATH="/root/.cabal/bin:/root/.ghcup/bin:$PATH"
|
||||
|
||||
# Set both as default
|
||||
RUN ghcup set ghc "${BOOTSTRAP_HASKELL_GHC_VERSION}" && \
|
||||
ghcup set cabal "${BOOTSTRAP_HASKELL_CABAL_VERSION}"
|
||||
|
||||
FROM haskell:8.10.4 AS build-stage
|
||||
# if you encounter "version `GLIBC_2.28' not found" error when running
|
||||
# chat client executable, build with the following base image instead:
|
||||
# FROM haskell:8.10.4-stretch AS build-stage
|
||||
COPY . /project
|
||||
WORKDIR /project
|
||||
RUN stack install
|
||||
|
||||
# Adjust build
|
||||
RUN cp ./scripts/cabal.project.local.linux ./cabal.project.local
|
||||
|
||||
# Compile simplex-chat
|
||||
RUN cabal update
|
||||
RUN cabal build exe:simplex-chat --constraint 'simplexmq +client_library' --constraint 'simplex-chat +client_library'
|
||||
|
||||
# Strip the binary from debug symbols to reduce size
|
||||
RUN bin=$(find /project/dist-newstyle -name "simplex-chat" -type f -executable) && \
|
||||
mv "$bin" ./ && \
|
||||
strip ./simplex-chat
|
||||
|
||||
# Copy compiled app from build stage
|
||||
FROM scratch AS export-stage
|
||||
COPY --from=build /project/simplex-chat /
|
||||
COPY --from=build-stage /root/.local/bin/simplex-chat /
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
# syntax=docker/dockerfile:1.7.0-labs
|
||||
ARG TAG=24.04
|
||||
FROM ubuntu:${TAG} AS build
|
||||
|
||||
### Build stage
|
||||
|
||||
ARG GHC=9.6.3
|
||||
ARG CABAL=3.10.1.0
|
||||
ARG JAVA=17
|
||||
|
||||
ENV TZ=Etc/UTC \
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install curl, git and and simplex-chat dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y curl \
|
||||
libpq-dev \
|
||||
git \
|
||||
sqlite3 \
|
||||
libsqlite3-dev \
|
||||
build-essential \
|
||||
libgmp3-dev \
|
||||
zlib1g-dev \
|
||||
llvm \
|
||||
cmake \
|
||||
llvm-dev \
|
||||
libnuma-dev \
|
||||
libssl-dev \
|
||||
desktop-file-utils \
|
||||
patchelf \
|
||||
ca-certificates \
|
||||
zip \
|
||||
wget \
|
||||
fuse3 \
|
||||
file \
|
||||
appstream \
|
||||
gpg \
|
||||
unzip &&\
|
||||
ln -s /bin/fusermount /bin/fusermount3 || :
|
||||
|
||||
# Install Java Coretto
|
||||
# Required, because official Java in Ubuntu
|
||||
# depends on libjpeg.so.8 and liblcms2.so.2 which are NOT copied into final
|
||||
# /usr/lib/runtime/lib directory and I do not have time to figure out gradle.kotlin
|
||||
# to fix this :(
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf 'https://apt.corretto.aws/corretto.key' | gpg --dearmor -o /usr/share/keyrings/corretto-keyring.gpg &&\
|
||||
echo "deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main" > /etc/apt/sources.list.d/corretto.list &&\
|
||||
apt update &&\
|
||||
apt install -y java-${JAVA}-amazon-corretto-jdk
|
||||
|
||||
# Specify bootstrap Haskell versions
|
||||
ENV BOOTSTRAP_HASKELL_GHC_VERSION=${GHC}
|
||||
ENV BOOTSTRAP_HASKELL_CABAL_VERSION=${CABAL}
|
||||
|
||||
# Do not install Stack
|
||||
ENV BOOTSTRAP_HASKELL_INSTALL_NO_STACK=true
|
||||
ENV BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK=true
|
||||
|
||||
# Install ghcup
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 sh
|
||||
|
||||
# Adjust PATH
|
||||
ENV PATH="/root/.cabal/bin:/root/.ghcup/bin:$PATH"
|
||||
|
||||
# Set both as default
|
||||
RUN ghcup set ghc "${GHC}" && \
|
||||
ghcup set cabal "${CABAL}"
|
||||
|
||||
#=====================
|
||||
# Install Android SDK
|
||||
#=====================
|
||||
ARG SDK_VERSION=13114758
|
||||
|
||||
ENV SDK_VERSION=$SDK_VERSION \
|
||||
ANDROID_HOME=/root
|
||||
|
||||
RUN curl -L -o tools.zip "https://dl.google.com/android/repository/commandlinetools-linux-${SDK_VERSION}_latest.zip" && \
|
||||
unzip tools.zip && rm tools.zip && \
|
||||
mv cmdline-tools tools && mkdir "$ANDROID_HOME/cmdline-tools" && mv tools "$ANDROID_HOME/cmdline-tools/" && \
|
||||
ln -s "$ANDROID_HOME/cmdline-tools/tools" "$ANDROID_HOME/cmdline-tools/latest"
|
||||
|
||||
ENV PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/cmdline-tools/tools/bin"
|
||||
|
||||
# https://askubuntu.com/questions/885658/android-sdk-repositories-cfg-could-not-be-loaded
|
||||
RUN mkdir -p ~/.android ~/.gradle && \
|
||||
touch ~/.android/repositories.cfg && \
|
||||
echo 'org.gradle.console=plain' > ~/.gradle/gradle.properties &&\
|
||||
yes | sdkmanager --licenses >/dev/null
|
||||
|
||||
ENV PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools
|
||||
|
||||
WORKDIR /project
|
246
PRIVACY.md
|
@ -1,246 +0,0 @@
|
|||
---
|
||||
layout: layouts/privacy.html
|
||||
permalink: /privacy/index.html
|
||||
---
|
||||
|
||||
# SimpleX Chat Operators Privacy Policy and Conditions of Use
|
||||
|
||||
## Summary
|
||||
|
||||
[Introduction](#introduction) and [General principles](#general-principles) cover SimpleX Chat network design, the network operators, and the principles of privacy and security provided by SimpleX network.
|
||||
|
||||
[Privacy policy](#privacy-policy) covers:
|
||||
- data stored only on your device - [your profiles](#user-profiles), delivered [messages and files](#messages-and-files). You can transfer this information to another device, and you are responsible for its preservation - if you delete the app it will be lost.
|
||||
- [private message delivery](#private-message-delivery) that protects your IP address and connection graph from the destination servers.
|
||||
- [undelivered messages and files](#storage-of-messages-and-files-on-the-servers) stored on the servers.
|
||||
- [how users connect](#connections-with-other-users) without any user profile identifiers.
|
||||
- [iOS push notifications](#ios-push-notifications) privacy limitations.
|
||||
- [user support](#user-support), [SimpleX directory](#simplex-directory) and [any other data](#another-information-stored-on-the-servers) that may be stored on the servers.
|
||||
- [preset server operators](#preset-server-operators) and the [information they may share](#information-preset-server-operators-may-share).
|
||||
- [source code license](#source-code-license) and [updates to this document](#updates).
|
||||
|
||||
[Conditions of Use](#conditions-of-use-of-software-and-infrastructure) are the conditions you need to accept to use SimpleX Chat applications and the relay servers of preset operators. Their purpose is to protect the users and preset server operators.
|
||||
|
||||
*Please note*: this summary and any links in this document are provided for information only - they are not a part of the Privacy Policy and Conditions of Use.
|
||||
|
||||
## Introduction
|
||||
|
||||
SimpleX Chat (also referred to as SimpleX) is the first communication network based on a new protocol stack that builds on the same ideas of complete openness and decentralization as email and web, with the focus on providing security and privacy of communications, and without compromising on usability.
|
||||
|
||||
SimpleX messaging protocol is the first protocol that has no user profile IDs of any kind, not even random numbers, cryptographic keys or hashes that identify the users. SimpleX apps allow their users to send messages and files via relay server infrastructure. Relay server owners and operators do not have any access to your messages, thanks to double-ratchet end-to-end encryption algorithm (also known as Signal algorithm - do not confuse with Signal protocols or platform) and additional encryption layers, and they also have no access to your profile and contacts - as they do not host user accounts.
|
||||
|
||||
Double ratchet algorithm has such important properties as [forward secrecy](/docs/GLOSSARY.md#forward-secrecy), sender [repudiation](/docs/GLOSSARY.md#) and break-in recovery (also known as [post-compromise security](/docs/GLOSSARY.md#post-compromise-security)).
|
||||
|
||||
If you believe that any part of this document is not aligned with SimpleX network mission or values, please raise it via [email](mailto:chat@simplex.chat) or [chat](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).
|
||||
|
||||
## Privacy Policy
|
||||
|
||||
### General principles
|
||||
|
||||
SimpleX network software uses the best industry practices for security and encryption to provide client and server software for secure [end-to-end encrypted](/docs/GLOSSARY.md#end-to-end-encryption) messaging via private connections. This encryption is protected from being compromised by the relays servers, even if they are modified or compromised, via [man-in-the-middle attack](/docs/GLOSSARY.md#man-in-the-middle-attack).
|
||||
|
||||
SimpleX software is built on top of SimpleX messaging and application protocols, based on a new message routing protocol allowing to establish private connections without having identifiers assigned to its users - it does not use emails, phone numbers, usernames, identity keys or any other user profile identifiers to pass messages between the user applications.
|
||||
|
||||
SimpleX software is similar in its design approach to email clients and browsers - it allows you to have full control of your data and freely choose the relay server operators, in the same way you choose which website or email provider to use, or use your own relay servers, simply by changing the configuration of the client software. The only current restriction to that is Apple push notifications - at the moment they can only be delivered via the servers operated by SimpleX Chat Ltd, as explained below. We are exploring the solutions to deliver push notifications to iOS devices via other providers or users' own servers.
|
||||
|
||||
SimpleX network operators are not communication service provider, and provide public relays "as is", as experimental, without any guarantees of availability or data retention. The operators of the relay servers preset in the app ("Preset Server Operators"), including SimpleX Chat Ltd, are committed to maintain a high level of availability, reliability and security. SimpleX client apps can have multiple preset relay server operators that you can opt-in or opt-out of using. You are and will continue to be able to use any other operators or your own servers.
|
||||
|
||||
SimpleX network design is based on the principles of users and data sovereignty, and device and operator portability.
|
||||
|
||||
The implementation security assessment of SimpleX cryptography and networking was done in October 2022 by [Trail of Bits](https://www.trailofbits.com/about), and most fixes were released in v4.2 – see [the announcement](/blog/20221108-simplex-chat-v4.2-security-audit-new-website.md).
|
||||
|
||||
The cryptographic review of SimpleX protocols design was done in July 2024 by Trail of Bits – see [the announcement](/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md).
|
||||
|
||||
### Your information
|
||||
|
||||
#### User profiles
|
||||
|
||||
Servers used by SimpleX Chat apps do not create, store or identify user chat profiles. The profiles you can create in the app are local to your device, and can be removed at any time via the app.
|
||||
|
||||
When you create the local profile, no records are created on any of the relay servers, and infrastructure operators, whether preset in the app or any other, have no access to any part of your information, and even to the fact that you created a profile - it is a local record stored only on your device. That means that if you delete the app, and have no backup, you will permanently lose all your data and the private connections you created with other software users.
|
||||
|
||||
You can transfer the profile to another device by creating a backup of the app data and restoring it on the new device, but you cannot use more than one device with the copy of the same profile at the same time - it will disrupt any active conversations on either or both devices, as a security property of end-to-end encryption.
|
||||
|
||||
#### Messages and Files
|
||||
|
||||
SimpleX relay servers cannot decrypt or otherwise access the content or even the size of your messages and files you send or receive. Each message is padded to a fixed size of 16kb. Each file is sent in chunks of 64kb, 256kb, 1mb or 4mb via all or some of the configured file relay servers. Both messages and files are sent end-to-end encrypted, and the servers do not have technical means to compromise this encryption, because part of the [key exchange](/docs/GLOSSARY.md#key-exchange) happens out-of-band.
|
||||
|
||||
Your message history is stored only on your own device and the devices of your contacts. While the recipients' devices are offline, messaging relay servers temporarily store end-to-end encrypted messages – you can configure which relay servers are used to receive the messages from the new contacts, and you can manually change them for the existing contacts too.
|
||||
|
||||
#### Private message delivery
|
||||
|
||||
You do not have control over which servers are used to send messages to your contacts - these servers are chosen by your contacts. To send messages your client by default uses configured servers to forward messages to the destination servers, thus protecting your IP address from the servers chosen by your contacts.
|
||||
|
||||
In case you use preset servers of more than one operator, the app will prefer to use a server of an operator different from the operator of the destination server to forward messages, preventing destination server to correlate messages as belonging to one client.
|
||||
|
||||
You can additionally use VPN or some overlay network (e.g., Tor) to hide your IP address from the servers chosen by you.
|
||||
|
||||
*Please note*: the clients allow changing configuration to connect to the destination servers directly. It is not recommended - if you make such change, your IP address will be visible to the destination servers.
|
||||
|
||||
#### Storage of messages and files on the servers
|
||||
|
||||
The messages are removed from the relay servers as soon as all messages of the file they were stored in are delivered and saving new messages switches to another file, as long as these servers use unmodified published code. Undelivered messages are also marked as delivered after the time that is configured in the messaging servers you use (21 days for preset messaging servers).
|
||||
|
||||
The files are stored on file relay servers for the time configured in the relay servers you use (48 hours for preset file servers).
|
||||
|
||||
The encrypted messages can be stored for some time after they are delivered or expired (because servers use append-only logs for message storage). This time varies, and may be longer in connections with fewer messages, but it is usually limited to 1 month, including any backup storage.
|
||||
|
||||
#### Connections with other users
|
||||
|
||||
When you create a connection with another user, two messaging queues (you can think about them as mailboxes) are created on messaging relay servers (chosen by you and your contact each), that can be the preset servers or the servers that you and your contact configured in the app. SimpleX messaging protocol uses separate queues for direct and response messages, and the apps prefer to create these queues on two different relay servers, or, if available, the relays of two different operators, for increased privacy, in case you have more than one relay server configured in the app, which is the default.
|
||||
|
||||
Preset and unmodified SimpleX relay servers do not store information about which queues are linked to your profile on the device, and they do not collect any information that would allow infrastructure owners and operators to establish that these queues are related to your device or your profile - the access to each queue is authorized by two anonymous unique cryptographic keys, different for each queue, and separate for sender and recipient of the messages.
|
||||
|
||||
#### Connection links privacy
|
||||
|
||||
When you create a connection with another user, the app generates a link/QR code that can be shared with the user to establish the connection via any channel (email, any other messenger, or a video call). This link is safe to share via insecure channels, as long as you can identify the recipient and also trust that this channel did not replace this link (to mitigate the latter risk you can validate the security code via the app).
|
||||
|
||||
While the connection "links" contain SimpleX Chat Ltd domain name `simplex.chat`, this site is never accessed by the app, and is only used for these purposes:
|
||||
- to direct the new users to the app download instructions,
|
||||
- to show connection QR code that can be scanned via the app,
|
||||
- to "namespace" these links,
|
||||
- to open links directly in the installed app when it is clicked outside of the app.
|
||||
|
||||
You can always safely replace the initial part of the link `https://simplex.chat/` either with `simplex:/` (which is a URI scheme provisionally registered with IANA) or with any other domain name where you can self-host the app download instructions and show the connection QR code (but in case it is your domain, it will not open in the app). Also, while the page renders QR code, all the information needed to render it is only available to the browser, as the part of the "link" after `#` symbol is not sent to the website server.
|
||||
|
||||
#### iOS Push Notifications
|
||||
|
||||
This section applies only to the notification servers operated by SimpleX Chat Ltd.
|
||||
|
||||
When you choose to use instant push notifications in SimpleX iOS app, because the design of push notifications requires storing the device token on notification server, the notifications server can observe how many messaging queues your device has notifications enabled for, and approximately how many messages are sent to each queue.
|
||||
|
||||
Preset notification server cannot observe the actual addresses of these queues, as a separate address is used to subscribe to the notifications. It also cannot observe who sends messages to you. Apple push notifications servers can only observe how many notifications are sent to you, but not from how many contacts, or from which messaging relays, as notifications are delivered to your device end-to-end encrypted by one of the preset notification servers - these notifications only contain end-to-end encrypted metadata, not even encrypted message content, and they look completely random to Apple push notification servers.
|
||||
|
||||
You can read more about the design of iOS push notifications [here](./blog/20220404-simplex-chat-instant-notifications.md#our-ios-approach-has-one-trade-off).
|
||||
|
||||
#### Another information stored on the servers
|
||||
|
||||
Additional technical information can be stored on the network servers, including randomly generated authentication tokens, keys, push tokens, and other material that is necessary to transmit messages. SimpleX network design limits this additional technical information to the minimum required to operate the software and servers. To prevent server overloading or attacks, the servers can temporarily store data that can link to particular users or devices, including IP addresses, geographic location, or information related to the transport sessions. This information is not stored for the absolute majority of the app users, even for those who use the servers very actively.
|
||||
|
||||
#### SimpleX Directory
|
||||
|
||||
This section applies only to the experimental group directory operated by SimpleX Chat Ltd.
|
||||
|
||||
[SimpleX Directory](/docs/DIRECTORY.md) stores: your search requests, the messages and the members profiles in the registered groups. You can connect to SimpleX Directory via [this address](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).
|
||||
|
||||
#### Public groups and content channels
|
||||
|
||||
You may participate in a public group and receive content from a public channel (Group). In case you send messages or comments to the Group, you grant a license:
|
||||
- to all recipients:
|
||||
- to share your messages with the new Group members and outside of the group, e.g. via quoting (replying), forwarding and copy-pasting your message. When your message is deleted or marked as deleted, the copies of your message will not be deleted.
|
||||
- to retain a copy of your messages according to the Group settings (e.g., the Group may allow irreversible message deletion from the recipient devices for a limited period of time, or it may only allow to edit and mark messages as deleted on recipient devices). Deleting message from the recipient devices or marking message as deleted revokes the license to share the message.
|
||||
- to Group owners: to share your messages with the new Group members as history of the Group. Currently, the Group history shared with the new members is limited to 100 messages.
|
||||
|
||||
Group owners may use chat relays or automated bots (Chat Relays) to re-broadcast member messages to all members, for efficiency. The Chat Relays may be operated by the group owners, by preset operators or by 3rd parties. The Chat Relays have access to and will retain messages in line with Group settings, for technical functioning of the Group. Neither you nor group owners grant any content license to Chat Relay operators.
|
||||
|
||||
#### User Support
|
||||
|
||||
The app includes support contact operated by SimpleX Chat Ltd. If you contact support, any personal data you share is kept only for the purposes of researching the issue and contacting you about your case. We recommend contacting support [via chat](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) when it is possible, and avoid sharing any personal information.
|
||||
|
||||
### Preset Server Operators
|
||||
|
||||
Preset server operators will not share the information on their servers with each other, other than aggregate usage statistics.
|
||||
|
||||
Preset server operators must not provide general access to their servers or the data on their servers to each other.
|
||||
|
||||
Preset server operators will provide non-administrative access to control port of preset servers to SimpleX Chat Ltd, for the purposes of removing illegal content identified in publicly accessible resources (contact and group addresses, and downloadable files). This control port access only allows deleting known links and files, and accessing aggregate server-wide statistics, but does NOT allow enumerating any information on the servers or accessing statistics related to specific users.
|
||||
|
||||
### Information Preset Server Operators May Share
|
||||
|
||||
The preset server operators use third parties. While they do not have access and cannot share any user data, these third parties may access the encrypted user messages (but NOT the actual unencrypted message content or size) as it is stored or transmitted via the servers. Hosting and network providers can also store IP addresses and other transport information as part of their logs.
|
||||
|
||||
SimpleX Chat Ltd uses a third party for email services - if you ask for support via email, your and SimpleX Chat Ltd email providers may access these emails according to their privacy policies and terms. When the request is sensitive, please contact us via SimpleX Chat apps or using encrypted email using PGP key published at [openpgp.org](https://keys.openpgp.org/search?q=chat%40simplex.chat).
|
||||
|
||||
The cases when the preset server operators may share the data temporarily stored on the servers:
|
||||
|
||||
- To meet any applicable law, or enforceable governmental request or court order.
|
||||
- To enforce applicable terms, including investigation of potential violations.
|
||||
- To detect, prevent, or otherwise address fraud, security, or technical issues.
|
||||
- To protect against harm to the rights, property, or safety of software users, operators of preset servers, or the public as required or permitted by law.
|
||||
|
||||
By the time of updating this document, the preset server operators were not served with any enforceable requests and did not provide any information from the servers to any third parties. If the preset server operators are ever requested to provide such access or information, they will follow the due legal process to limit any information shared with the third parties to the minimally required by law.
|
||||
|
||||
Preset server operators will publish information they are legally allowed to share about such requests in the [Transparency reports](./docs/TRANSPARENCY.md).
|
||||
|
||||
### Source code license
|
||||
|
||||
As this software is fully open-source and provided under AGPLv3 license, all infrastructure owners and operators, and the developers of the client and server applications who use the SimpleX Chat source code, are required to publish any changes to this software under the same AGPLv3 license - including any modifications to the servers.
|
||||
|
||||
In addition to the AGPLv3 license terms, the preset relay server operators are committed to the software users that these servers will always be compiled from the [published open-source code](https://github.com/simplex-chat/simplexmq), without any modifications.
|
||||
|
||||
### Updates
|
||||
|
||||
This Privacy Policy applies to SimpleX Chat Ltd and all other preset server operators you use in the app.
|
||||
|
||||
This Privacy Policy may be updated as needed so that it is current, accurate, and as clear as possible. When it is updated, you will have to review and accept the changed policy within 30 days of such changes to continue using preset relay servers. Even if you fail to accept the changed policy, your continued use of SimpleX Chat software applications and preset relay servers confirms your acceptance of the updated Privacy Policy.
|
||||
|
||||
Please also read The Conditions of Use of Software and Infrastructure below.
|
||||
|
||||
If you have questions about this Privacy Policy please contact SimpleX Chat Ltd via [email](mailto:chat@simplex.chat) or [chat](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).
|
||||
|
||||
## Conditions of Use of Software and Infrastructure
|
||||
|
||||
You accept the Conditions of Use of Software and Infrastructure ("Conditions") by installing or using any of SimpleX Chat software or using any of server infrastructure (collectively referred to as "Applications") operated by the Preset Server Operators, including SimpleX Chat Ltd, whether these servers are preset in the software or not.
|
||||
|
||||
**Minimal age**. You must be at least 13 years old to use SimpleX Chat Applications. The minimum age to use SimpleX Applications without parental approval may be higher in your country.
|
||||
|
||||
**Infrastructure**. Infrastructure of the preset server operators includes messaging and file relay servers. SimpleX Chat Ltd also provides iOS push notification servers for public use. This infrastructure does not have any modifications from the [published open-source code](https://github.com/simplex-chat/simplexmq) available under AGPLv3 license. Any infrastructure provider, whether commercial or not, is required by the Affero clause (named after Affero Inc. company that pioneered the community-based Q&A sites in early 2000s) to publish any modifications under the same license. The statements in relation to Infrastructure and relay servers anywhere in this document assume no modifications to the published code, even in the cases when it is not explicitly stated.
|
||||
|
||||
**Client applications**. SimpleX Chat client application Software (referred to as "app" or "apps") also has no modifications compared with published open-source code, and any developers of the alternative client apps based on SimpleX Chat code are required to publish any modifications under the same AGPLv3 license. Client applications should not include any tracking or analytics code, and do not share any tracking information with SimpleX Chat Ltd, preset server operators or any other third parties. If you ever discover any tracking or analytics code, please report it to SimpleX Chat Ltd, so it can be removed.
|
||||
|
||||
**Accessing the infrastructure**. For the efficiency of the network access, the client Software by default accesses all queues your app creates on any relay server within one user profile via the same network (TCP/IP) connection. At the cost of additional traffic this configuration can be changed to use different transport session for each connection. Relay servers do not collect information about which queues were created or accessed via the same connection, so the relay servers cannot establish which queues belong to the same user profile. Whoever might observe your network traffic would know which relay servers you use, and how much data you send, but not to whom it is sent - the data that leaves the servers is always different from the data they receive - there are no identifiers or ciphertext in common, even inside TLS encryption layer. Please refer to the [technical design document](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) for more information about the privacy model and known security and privacy risks.
|
||||
|
||||
**Privacy of user data**. Servers do not retain any data you transmit for any longer than necessary to deliver the messages between apps. Preset server operators collect aggregate statistics across all their servers, as supported by published code and can be enabled by any infrastructure operator, but not any statistics per-user, or per geographic location, or per IP address, or per transport session. SimpleX Chat Ltd does not have information about how many people use SimpleX Chat applications, it only knows an approximate number of app installations and the aggregate traffic through the preset servers. In any case, preset server operators do not and will not sell or in any way monetize user data. The future business model assumes charging for some optional Software features instead, in a transparent and fair way.
|
||||
|
||||
**Operating Infrastructure**. For the purpose of using SimpleX Chat Software, if you continue using preset servers, you agree that your end-to-end encrypted messages are transferred via the preset servers in any countries where preset server operators have or use facilities and service providers or partners. The information about geographic location and hosting providers of the preset messaging servers is available on server pages.
|
||||
|
||||
**Software**. You agree to downloading and installing updates to SimpleX Chat Applications when they are available; they would only be automatic if you configure your devices in this way.
|
||||
|
||||
**Traffic and device costs**. You are solely responsible for the traffic and device costs that you incur while using SimpleX Chat Applications, and any associated taxes.
|
||||
|
||||
**Legal usage**. You agree to use SimpleX Chat Applications only for legal purposes. You will not use (or assist others in using) the Applications in ways that: 1) violate or infringe the rights of Software users, SimpleX Chat Ltd, other preset server operators, or others, including privacy, publicity, intellectual property, or other proprietary rights; 2) involve sending illegal communications, e.g. spam. While server operators cannot access content or identify messages or groups, in some cases the links to the illegal communications can be shared publicly on social media or websites. Preset server operators reserve the right to remove such links from the preset servers and disrupt the conversations that send illegal content via their servers, whether they were reported by the users or discovered by the operators themselves.
|
||||
|
||||
**Damage to SimpleX Chat Ltd and Preset Server Operators**. You must not (or assist others to) access, use, modify, distribute, transfer, or exploit SimpleX Chat Applications in unauthorized manners, or in ways that harm Software users, SimpleX Chat Ltd, other preset server operators, their Infrastructure, or any other systems. For example, you must not 1) access preset operators' Infrastructure or systems without authorization, in any way other than by using the Software or by using a 3rd party client applications that satisfies the requirements of the Conditions of use (see the next section); 2) disrupt the integrity or performance of preset operators' Infrastructure; 3) collect information about the users in any manner; or 4) sell, rent, or charge for preset operators' Infrastructure. This does not prohibit you from providing your own Infrastructure to others, whether free or for a fee, as long as you do not violate these Conditions and AGPLv3 license, including the requirement to publish any modifications of the relay server software.
|
||||
|
||||
**3rd party client applications**. You may use a 3rd party application (App) to access preset operators' Infrastructure or systems, provided that this App:
|
||||
- is compatible with the protocol specifications not older than 1 year,
|
||||
- provides user-to-user messaging only or enables automated chat bots sending messages requested by users (in case of bots, it must be made clear to the users that these are automated bots),
|
||||
- implements the same limits, rules and restrictions as Software,
|
||||
- requires that the users accept the same Conditions of use of preset operators' Infrastructure as in Software prior to providing access to this Infrastructure,
|
||||
- displays the notice that it is the App for using SimpleX network,
|
||||
- provides its source code under open-source license accessible to the users via the App interface. In case the App uses the source code of Software, the App's source code must be provided under AGPLv3 license, and in case it is developed without using Software code its source code must be provided under any widely recognized free open-source license,
|
||||
- does NOT use the branding of SimpleX Chat Ltd without the permission,
|
||||
- does NOT pretend to be Software,
|
||||
- complies with these Conditions of use.
|
||||
|
||||
**Keeping your data secure**. SimpleX Chat is the first communication software that aims to be 100% private by design - server software neither has the ability to access your messages, nor it has information about who you communicate with. That means that you are solely responsible for keeping your device, your user profile and any data safe and secure. If you lose your phone or remove the Software from the device, you will not be able to recover the lost data, unless you made a back up. To protect the data you need to make regular backups, as using old backups may disrupt your communication with some of the contacts. SimpleX Chat Ltd and other preset server operators are not responsible for any data loss.
|
||||
|
||||
**Storing the messages on the device**. The messages are stored in the encrypted database on your device. Whether and how database passphrase is stored is determined by the configuration of the Software you use. The databases created prior to 2023 or in CLI (terminal) app may remain unencrypted, and it will be indicated in the app interface. In this case, if you make a backup of the data and store it unencrypted, the backup provider may be able to access the messages. Please note, that the desktop apps can be configured to store the database passphrase in the configuration file in plaintext, and unless you set the passphrase when first running the app, a random passphrase will be used and stored on the device. You can remove it from the device via the app settings.
|
||||
|
||||
**Storing the files on the device**. The files currently sent and received in the apps by default (except CLI app) are stored on your device encrypted using unique keys, different for each file, that are stored in the database. Once the message that the file was attached to is removed, even if the copy of the encrypted file is retained, it should be impossible to recover the key allowing to decrypt the file. This local file encryption may affect app performance, and it can be disabled via the app settings. This change will only affect the new files. If you later re-enable the encryption, it will also affect only the new files. If you make a backup of the app data and store it unencrypted, the backup provider will be able to access any unencrypted files. In any case, irrespective of the storage setting, the files are always sent by all apps end-to-end encrypted.
|
||||
|
||||
**No Access to Emergency Services**. SimpleX Chat Applications do not provide access to emergency service providers like the police, fire department, hospitals, or other public safety organizations. Make sure you can contact emergency service providers through a mobile, fixed-line telephone, or other service.
|
||||
|
||||
**Third-party services**. SimpleX Chat Applications may allow you to access, use, or interact with the websites of SimpleX Chat Ltd, preset server operators or other third-party websites, apps, content, and other products and services. When you use third-party services, their terms and privacy policies govern your use of those services.
|
||||
|
||||
**Your Rights**. You own the messages and the information you transmit through SimpleX Applications. Your recipients are able to retain the messages they receive from you; there is no technical ability to delete data from their devices. While there are various app features that allow deleting messages from the recipients' devices, such as _disappearing messages_ and _full message deletion_, their functioning on your recipients' devices cannot be guaranteed or enforced, as the device may be offline or have a modified version of the Software. At the same time, repudiation property of the end-to-end encryption algorithm allows you to plausibly deny having sent the message, like you can deny what you said in a private face-to-face conversation, as the recipient cannot provide any proof to the third parties, by design.
|
||||
|
||||
**License**. SimpleX Chat Ltd grants you a limited, revocable, non-exclusive, and non-transferable license to use SimpleX Chat Applications in accordance with these Conditions. The source-code of Applications is available and can be used under [AGPL v3 license](https://github.com/simplex-chat/simplex-chat/blob/stable/LICENSE).
|
||||
|
||||
**SimpleX Chat Ltd Rights**. SimpleX Chat Ltd (and, where applicable, preset server operators) owns all copyrights, trademarks, domains, logos, trade secrets, and other intellectual property rights associated with the Applications. You may not use SimpleX Chat Ltd copyrights, trademarks, domains, logos, and other intellectual property rights unless you have SimpleX Chat Ltd written permission, and unless under an open-source license distributed together with the source code. To report copyright, trademark, or other intellectual property infringement, please contact chat@simplex.chat.
|
||||
|
||||
**Disclaimers**. YOU USE SIMPLEX APPLICATIONS AT YOUR OWN RISK AND SUBJECT TO THE FOLLOWING DISCLAIMERS. SIMPLEX CHAT LTD PROVIDES APPLICATIONS ON AN “AS IS” BASIS WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, AND FREEDOM FROM COMPUTER VIRUS OR OTHER HARMFUL CODE. SIMPLEX CHAT LTD DOES NOT WARRANT THAT ANY INFORMATION PROVIDED BY THEM IS ACCURATE, COMPLETE, OR USEFUL, THAT THEIR APPLICATIONS WILL BE OPERATIONAL, ERROR-FREE, SECURE, OR SAFE, OR THAT THEIR APPLICATIONS WILL FUNCTION WITHOUT DISRUPTIONS, DELAYS, OR IMPERFECTIONS. SIMPLEX CHAT LTD AND OTHER PRESET OPERATORS DO NOT CONTROL, AND ARE NOT RESPONSIBLE FOR, CONTROLLING HOW OR WHEN THE USERS USE APPLICATIONS. SIMPLEX CHAT LTD AND OTHER PRESET OPERATORS ARE NOT RESPONSIBLE FOR THE ACTIONS OR INFORMATION (INCLUDING CONTENT) OF THEIR USERS OR OTHER THIRD PARTIES. YOU RELEASE SIMPLEX CHAT LTD, OTHER PRESET OPERATORS, AFFILIATES, DIRECTORS, OFFICERS, EMPLOYEES, PARTNERS, AND AGENTS ("SIMPLEX PARTIES") FROM ANY CLAIM, COMPLAINT, CAUSE OF ACTION, CONTROVERSY, OR DISPUTE (TOGETHER, "CLAIM") AND DAMAGES, KNOWN AND UNKNOWN, RELATING TO, ARISING OUT OF, OR IN ANY WAY CONNECTED WITH ANY SUCH CLAIM YOU HAVE AGAINST ANY THIRD PARTIES.
|
||||
|
||||
**Limitation of liability**. THE SIMPLEX PARTIES WILL NOT BE LIABLE TO YOU FOR ANY LOST PROFITS OR CONSEQUENTIAL, SPECIAL, PUNITIVE, INDIRECT, OR INCIDENTAL DAMAGES RELATING TO, ARISING OUT OF, OR IN ANY WAY IN CONNECTION WITH OUR CONDITIONS, US, OR SIMPLEX APPLICATIONS, EVEN IF THE SIMPLEX PARTIES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE AGGREGATE LIABILITY OF THE SIMPLEX PARTIES RELATING TO, ARISING OUT OF, OR IN ANY WAY IN CONNECTION WITH THESE CONDITIONS, THE SIMPLEX PARTIES, OR THE APPLICATIONS WILL NOT EXCEED ONE DOLLAR ($1). THE FOREGOING DISCLAIMER OF CERTAIN DAMAGES AND LIMITATION OF LIABILITY WILL APPLY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. THE LAWS OF SOME JURISDICTIONS MAY NOT ALLOW THE EXCLUSION OR LIMITATION OF CERTAIN DAMAGES, SO SOME OR ALL OF THE EXCLUSIONS AND LIMITATIONS SET FORTH ABOVE MAY NOT APPLY TO YOU. NOTWITHSTANDING ANYTHING TO THE CONTRARY IN THE CONDITIONS, IN SUCH CASES, THE LIABILITY OF THE SIMPLEX PARTIES WILL BE LIMITED TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
|
||||
**Availability**. The Applications may be disrupted, including for maintenance, upgrades, or network or equipment failures. SimpleX Chat Ltd may discontinue some or all of their Applications, including certain features and the support for certain devices and platforms, at any time. Preset server operators may discontinue providing the servers, at any time.
|
||||
|
||||
**Resolving disputes**. You agree to resolve any Claim you have with SimpleX Chat Ltd and/or preset server operators relating to or arising from these Conditions, them, or the Applications in the courts of England and Wales. You also agree to submit to the personal jurisdiction of such courts for the purpose of resolving all such disputes. The laws of England govern these Conditions, as well as any disputes, whether in court or arbitration, which might arise between SimpleX Chat Ltd (or preset server operators) and you, without regard to conflict of law provisions.
|
||||
|
||||
**Changes to the conditions**. SimpleX Chat Ltd may update the Conditions from time to time. The updated conditions have to be accepted within 30 days. Even if you fail to accept updated conditions, your continued use of SimpleX Chat Applications confirms your acceptance of the updated Conditions and supersedes any prior Conditions. You will comply with all applicable export control and trade sanctions laws. These Conditions cover the entire agreement between you and SimpleX Chat Ltd, and any preset server operators where applicable, regarding SimpleX Chat Applications. If you do not agree with these Conditions, you should stop using the Applications.
|
||||
|
||||
**Enforcing the conditions**. If SimpleX Chat Ltd or preset server operators fail to enforce any of these Conditions, that does not mean they waive the right to enforce them. If any provision of the Conditions is deemed unlawful, void, or unenforceable, that provision shall be deemed severable from the Conditions and shall not affect the enforceability of the remaining provisions. The Applications are not intended for distribution to or use in any country where such distribution or use would violate local law or would subject SimpleX Chat Ltd to any regulations in another country. SimpleX Chat Ltd reserve the right to limit the access to the Applications in any country. Preset operators reserve the right to limit access to their servers in any country. If you have specific questions about these Conditions, please contact SimpleX Chat Ltd at chat@simplex.chat.
|
||||
|
||||
**Ending these conditions**. You may end these Conditions with SimpleX Chat Ltd and preset server operators at any time by deleting the Applications from your devices and discontinuing use of the Infrastructure of SimpleX Chat Ltd and preset server operators. The provisions related to Licenses, Disclaimers, Limitation of Liability, Resolving dispute, Availability, Changes to the conditions, Enforcing the conditions, and Ending these conditions will survive termination of your relationship with SimpleX Chat Ltd and/or preset server operators.
|
||||
|
||||
Updated March 3, 2025
|
605
README.md
|
@ -1,443 +1,232 @@
|
|||
[](https://github.com/simplex-chat/simplex-chat/actions/workflows/build.yml)
|
||||
[](https://github.com/simplex-chat/simplex-chat/releases)
|
||||
<img align="right" src="images/logo.svg" alt="SimpleX logo" height="90">
|
||||
|
||||
# SimpleX chat
|
||||
|
||||
## Private, secure, decentralized
|
||||
|
||||
[](https://github.com/simplex-chat/simplex-chat/actions?query=workflow%3Abuild)
|
||||
[](https://github.com/simplex-chat/simplex-chat/releases)
|
||||
[](https://www.reddit.com/r/SimpleXChat)
|
||||
<a rel="me" href="https://mastodon.social/@simplex"></a>
|
||||
|
||||
| 30/03/2023 | EN, [FR](/docs/lang/fr/README.md), [CZ](/docs/lang/cs/README.md), [PL](/docs/lang/pl/README.md) |
|
||||
> **NEW in v0.4: [groups](#groups) and [sending files](#sending-files)!**
|
||||
|
||||
<img src="images/simplex-chat-logo.svg" alt="SimpleX logo" width="100%">
|
||||
The motivation for SimpleX chat is [presented here](./simplex.md).
|
||||
|
||||
# SimpleX - the first messaging platform that has no user identifiers of any kind - 100% private by design!
|
||||
SimpleX chat prototype is a thin terminal UI on top of [SimpleXMQ](https://github.com/simplex-chat/simplexmq) message broker that uses [SMP protocols](https://github.com/simplex-chat/simplexmq/blob/master/protocol).
|
||||
|
||||
[<img src="./images/trail-of-bits.jpg" height="80">](http://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html) [<img src="./images/privacy-guides.jpg" height="64">](https://www.privacyguides.org/en/real-time-communication/#simplex-chat) [<img src="./images/whonix-logo.jpg" height="64">](https://www.whonix.org/wiki/Chat#Recommendation) [<img src="./images/kuketz-blog.jpg" height="64">](https://www.kuketz-blog.de/simplex-eindruecke-vom-messenger-ohne-identifier/)
|
||||
|
||||
## Welcome to SimpleX Chat!
|
||||
|
||||
1. 📲 [Install the app](#install-the-app).
|
||||
2. ↔️ [Connect to the team](#connect-to-the-team), [join user groups](#join-user-groups) and [follow our updates](#follow-our-updates).
|
||||
3. 🤝 [Make a private connection](#make-a-private-connection) with a friend.
|
||||
4. 🔤 [Help translating SimpleX Chat](#help-translating-simplex-chat).
|
||||
5. ⚡️ [Contribute](#contribute) and [support us with donations](#please-support-us-with-your-donations).
|
||||
|
||||
[Learn more about SimpleX Chat](#contents).
|
||||
|
||||
## Install the app
|
||||
|
||||
[<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/apple_store.svg" alt="iOS app" height="42">](https://apps.apple.com/us/app/simplex-chat/id1605771084)
|
||||
|
||||
[](https://play.google.com/store/apps/details?id=chat.simplex.app)
|
||||
|
||||
[<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/f_droid.svg" alt="F-Droid" height="41">](https://app.simplex.chat)
|
||||
|
||||
[<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/testflight.png" alt="iOS TestFlight" height="41">](https://testflight.apple.com/join/DWuT2LQu)
|
||||
|
||||
[<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/apk_icon.png" alt="APK" height="41">](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk)
|
||||
|
||||
- 🖲 Protects your messages and metadata - who you talk to and when.
|
||||
- 🔐 Double ratchet end-to-end encryption, with additional encryption layer.
|
||||
- 📱 Mobile apps for Android ([Google Play](https://play.google.com/store/apps/details?id=chat.simplex.app), [APK](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk)) and [iOS](https://apps.apple.com/us/app/simplex-chat/id1605771084).
|
||||
- 🚀 [TestFlight preview for iOS](https://testflight.apple.com/join/DWuT2LQu) with the new features 1-2 weeks earlier - **limited to 10,000 users**!
|
||||
- 🖥 Available as a terminal (console) [app / CLI](#zap-quick-installation-of-a-terminal-app) on Linux, MacOS, Windows.
|
||||
|
||||
## Connect to the team
|
||||
|
||||
You can connect to the team via the app using "chat with the developers button" available when you have no conversations in the profile, "Send questions and ideas" in the app settings or via our [SimpleX address](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). Please connect to:
|
||||
|
||||
- to ask any questions
|
||||
- to suggest any improvements
|
||||
- to share anything relevant
|
||||
|
||||
We are replying the questions manually, so it is not instant – it can take up to 24 hours.
|
||||
|
||||
If you are interested in helping us to integrate open-source language models, and in [joining our team](./docs/JOIN_TEAM.md), please get in touch.
|
||||
|
||||
## Join user groups
|
||||
|
||||
You can join the groups created by other users via the new [directory service](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). We are not responsible for the content shared in these groups.
|
||||
|
||||
**Please note**: The groups below are created for the users to be able to ask questions, make suggestions and ask questions about SimpleX Chat only.
|
||||
|
||||
You also can:
|
||||
- criticize the app, and make comparisons with other messengers.
|
||||
- share new messengers you think could be interesting for privacy, as long as you don't spam.
|
||||
- share some privacy related publications, infrequently.
|
||||
- having preliminary approved with the admin in direct message, share the link to a group you created, but only once. Once the group has more than 10 members it can be submitted to [SimpleX Directory Service](./docs/DIRECTORY.md) where the new users will be able to discover it.
|
||||
|
||||
You must:
|
||||
- be polite to other users
|
||||
- avoid spam (too frequent messages, even if they are relevant)
|
||||
- avoid any personal attacks or hostility.
|
||||
- avoid sharing any content that is not relevant to the above (that includes, but is not limited to, discussing politics or any aspects of society other than privacy, security, technology and communications, sharing any content that may be found offensive by other users, etc.).
|
||||
|
||||
Messages not following these rules will be deleted, the right to send messages may be revoked, and the access to the new members to the group may be temporarily restricted, to prevent re-joining under a different name - our imperfect group moderation does not have a better solution at the moment.
|
||||
|
||||
You can join an English-speaking users group if you want to ask any questions: [#SimpleX users group](https://simplex.chat/contact#/?v=2-7&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FiBkJE72asZX1NUZaYFIeKRVk6oVjb-iv%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEAinqu3j74AMjODLoIRR487ZW6ysip_dlpD6Zxk18SPFY%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22groupLinkId%22%3A%223wAFGCLygQHR5AwynZOHlQ%3D%3D%22%7D)
|
||||
|
||||
There is also a group [#simplex-devs](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FvYCRjIflKNMGYlfTkuHe4B40qSlQ0439%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAHNdcqNbzXZhyMoSBjT2R0-Eb1EPaLyUg3KZjn-kmM1w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22PD20tcXjw7IpkkMCfR6HLA%3D%3D%22%7D) for developers who build on SimpleX platform:
|
||||
|
||||
- chat bots and automations
|
||||
- integrations with other apps
|
||||
- social apps and services
|
||||
- etc.
|
||||
|
||||
There are groups in other languages, that we have the apps interface translated into. These groups are for testing, and asking questions to other SimpleX Chat users:
|
||||
|
||||
[\#SimpleX-DE](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FmfiivxDKWFuowXrQOp11jsY8TuP__rBL%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAiz3pKNwvKudckFYMUfgoT0s96B0jfZ7ALHAu7rtE9HQ%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22jZeJpXGrRXQJU_-MSJ_v2A%3D%3D%22%7D) (German-speaking), [\#SimpleX-ES](https://simplex.chat/contact#/?v=2-4&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FJ5ES83pJimY2BRklS8fvy_iQwIU37xra%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEA0F0STP6UqN_12_k2cjjTrIjFgBGeWhOAmbY1qlk3pnM%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22VmUU0fqmYdCRmVCyvStvHA%3D%3D%22%7D) (Spanish-speaking), [\#SimpleX-FR](https://simplex.chat/contact#/?v=2-7&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FxCHBE_6PBRMqNEpm4UQDHXb9cz-mN7dd%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEAetqlcM7zTCRw-iatnwCrvpJSto7lq5Yv6AsBMWv7GSM%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22foO5Xw4hhjOa_x7zET7otw%3D%3D%22%7D) (French-speaking), [\#SimpleX-RU](https://simplex.chat/contact#/?v=2-4&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FVXQTB0J2lLjYkgjWByhl6-1qmb5fgZHh%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAI6JaEWezfSwvcoTEkk6au-gkjrXR2ew2OqZYMYBvayk%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22ORH9OEe8Duissh-hslfeVg%3D%3D%22%7D) (Russian-speaking), [\#SimpleX-IT](https://simplex.chat/contact#/?v=2-7&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FqpHu0psOUdYfc11yQCzSyq5JhijrBzZT%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEACZ_7fbwlM45wl6cGif8cY47oPQ_AMdP0ATqOYLA6zHY%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%229uRQRTir3ealdcSfB0zsrw%3D%3D%22%7D) (Italian-speaking).
|
||||
|
||||
You can join either by opening these links in the app or by opening them in a desktop browser and scanning the QR code.
|
||||
|
||||
## Follow our updates
|
||||
|
||||
We publish our updates and releases via:
|
||||
|
||||
- [Reddit](https://www.reddit.com/r/SimpleXChat/), [Twitter](https://twitter.com/SimpleXChat), [Lemmy](https://lemmy.ml/c/simplex), [Mastodon](https://mastodon.social/@simplex) and [Nostr](https://snort.social/p/npub1exv22uulqnmlluszc4yk92jhs2e5ajcs6mu3t00a6avzjcalj9csm7d828).
|
||||
- SimpleX Chat [team profile](#connect-to-the-team).
|
||||
- [blog](https://simplex.chat/blog/) and [RSS feed](https://simplex.chat/feed.rss).
|
||||
- [mailing list](https://simplex.chat/#join-simplex), very rarely.
|
||||
|
||||
## Make a private connection
|
||||
|
||||
You need to share a link with your friend or scan a QR code from their phone, in person or during a video call, to make a connection and start messaging.
|
||||
|
||||
The channel through which you share the link does not have to be secure - it is enough that you can confirm who sent you the message and that your SimpleX connection is established.
|
||||
|
||||
<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/app1.png" alt="Make a private connection" height="360"> <img src="https://github.com/simplex-chat/.github/blob/master/profile/images/arrow.png" height="360"> <img src="https://github.com/simplex-chat/.github/blob/master/profile/images/app2.png" alt="Conversation" height="360"> <img src="https://github.com/simplex-chat/.github/blob/master/profile/images/arrow.png" height="360"> <img src="https://github.com/simplex-chat/.github/blob/master/profile/images/app3.png" alt="Video call" height="360">
|
||||
|
||||
After you connect, you can [verify connection security code](./blog/20230103-simplex-chat-v4.4-disappearing-messages.md#connection-security-verification).
|
||||
|
||||
## User guide (NEW)
|
||||
|
||||
Read about the app features and settings in the new [User guide](./docs/guide/README.md).
|
||||
|
||||
## Contribute
|
||||
|
||||
We would love to have you join the development! You can help us with:
|
||||
|
||||
- [share the color theme](./docs/THEMES.md) you use in Android app!
|
||||
- writing a tutorial or recipes about hosting servers, chat bot automations, etc.
|
||||
- contributing to SimpleX Chat knowledge-base.
|
||||
- developing features - please connect to us via chat so we can help you get started.
|
||||
|
||||
## Help translating SimpleX Chat
|
||||
|
||||
Thanks to our users and [Weblate](https://hosted.weblate.org/engage/simplex-chat/), SimpleX Chat apps, website and documents are translated to many other languages.
|
||||
|
||||
Join our translators to help SimpleX grow!
|
||||
|
||||
|locale|language |contributor|[Android](https://play.google.com/store/apps/details?id=chat.simplex.app) and [iOS](https://apps.apple.com/us/app/simplex-chat/id1605771084)|[website](https://simplex.chat)|Github docs|
|
||||
|:----:|:-------:|:---------:|:---------:|:---------:|:---------:|
|
||||
|🇬🇧 en|English | |✓|✓|✓|✓|
|
||||
|ar|العربية |[jermanuts](https://github.com/jermanuts)|[](https://hosted.weblate.org/projects/simplex-chat/android/ar/)<br>-|[](https://hosted.weblate.org/projects/simplex-chat/website/ar/)||
|
||||
|🇧🇬 bg|Български | |[](https://hosted.weblate.org/projects/simplex-chat/android/bg/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/bg/)|||
|
||||
|🇨🇿 cs|Čeština |[zen0bit](https://github.com/zen0bit)|[](https://hosted.weblate.org/projects/simplex-chat/android/cs/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/cs/)|[](https://hosted.weblate.org/projects/simplex-chat/website/cs/)|[✓](https://github.com/simplex-chat/simplex-chat/tree/master/docs/lang/cs)|
|
||||
|🇩🇪 de|Deutsch |[mlanp](https://github.com/mlanp)|[](https://hosted.weblate.org/projects/simplex-chat/android/de/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/de/)|[](https://hosted.weblate.org/projects/simplex-chat/website/de/)||
|
||||
|🇪🇸 es|Español |[Mateyhv](https://github.com/Mateyhv)|[](https://hosted.weblate.org/projects/simplex-chat/android/es/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/es/)|[](https://hosted.weblate.org/projects/simplex-chat/website/es/)||
|
||||
|🇫🇮 fi|Suomi | |[](https://hosted.weblate.org/projects/simplex-chat/android/fi/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/fi/)|[](https://hosted.weblate.org/projects/simplex-chat/website/fi/)||
|
||||
|🇫🇷 fr|Français |[ishi_sama](https://github.com/ishi-sama)|[](https://hosted.weblate.org/projects/simplex-chat/android/fr/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/fr/)|[](https://hosted.weblate.org/projects/simplex-chat/website/fr/)|[✓](https://github.com/simplex-chat/simplex-chat/tree/master/docs/lang/fr)|
|
||||
|🇮🇱 he|עִברִית | |[](https://hosted.weblate.org/projects/simplex-chat/android/he/)<br>-|||
|
||||
|🇭🇺 hu|Magyar | |[](https://hosted.weblate.org/projects/simplex-chat/android/hu/)<br>-|||
|
||||
|🇮🇹 it|Italiano |[unbranched](https://github.com/unbranched)|[](https://hosted.weblate.org/projects/simplex-chat/android/it/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/it/)|[](https://hosted.weblate.org/projects/simplex-chat/website/it/)||
|
||||
|🇯🇵 ja|日本語 | |[](https://hosted.weblate.org/projects/simplex-chat/android/ja/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/ja/)|[](https://hosted.weblate.org/projects/simplex-chat/website/ja/)||
|
||||
|🇳🇱 nl|Nederlands|[mika-nl](https://github.com/mika-nl)|[](https://hosted.weblate.org/projects/simplex-chat/android/nl/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/nl/)|[](https://hosted.weblate.org/projects/simplex-chat/website/nl/)||
|
||||
|🇵🇱 pl|Polski |[BxOxSxS](https://github.com/BxOxSxS)|[](https://hosted.weblate.org/projects/simplex-chat/android/pl/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/pl/)|||
|
||||
|🇧🇷 pt-BR|Português||[](https://hosted.weblate.org/projects/simplex-chat/android/pt_BR/)<br>-|[](https://hosted.weblate.org/projects/simplex-chat/website/pt_BR/)||
|
||||
|🇷🇺 ru|Русский ||[](https://hosted.weblate.org/projects/simplex-chat/android/ru/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/ru/)|||
|
||||
|🇹🇭 th|ภาษาไทย |[titapa-punpun](https://github.com/titapa-punpun)|[](https://hosted.weblate.org/projects/simplex-chat/android/th/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/th/)|||
|
||||
|🇹🇷 tr|Türkçe | |[](https://hosted.weblate.org/projects/simplex-chat/android/tr/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/tr/)|||
|
||||
|🇺🇦 uk|Українська| |[](https://hosted.weblate.org/projects/simplex-chat/android/uk/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/uk/)|[](https://hosted.weblate.org/projects/simplex-chat/website/uk/)||
|
||||
|🇨🇳 zh-CHS|简体中文|[sith-on-mars](https://github.com/sith-on-mars)<br><br>[Float-hu](https://github.com/Float-hu)|[](https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/)<br>[](https://hosted.weblate.org/projects/simplex-chat/ios/zh_Hans/)<br> |<br><br>[](https://hosted.weblate.org/projects/simplex-chat/website/zh_Hans/)||
|
||||
|
||||
Languages in progress: Arabic, Japanese, Korean, Portuguese and [others](https://hosted.weblate.org/projects/simplex-chat/#languages). We will be adding more languages as some of the already added are completed – please suggest new languages, review the [translation guide](./docs/TRANSLATIONS.md) and get in touch with us!
|
||||
|
||||
## Please support us with your donations
|
||||
|
||||
Huge thank you to everybody who donated to SimpleX Chat!
|
||||
|
||||
We are prioritizing users privacy and security - it would be impossible without your support.
|
||||
|
||||
Our pledge to our users is that SimpleX protocols are and will remain open, and in public domain, - so anybody can build the future implementations of the clients and the servers. We are building SimpleX platform based on the same principles as email and web, but much more private and secure.
|
||||
|
||||
Your donations help us raise more funds - any amount, even the price of the cup of coffee, would make a big difference for us.
|
||||
|
||||
It is possible to donate via:
|
||||
|
||||
- [GitHub](https://github.com/sponsors/simplex-chat) (commission-free) or [OpenCollective](https://opencollective.com/simplex-chat) (~10% commission).
|
||||
- BTC: bc1q2gy6f02nn6vvcxs0pnu29tpnpyz0qf66505d4u
|
||||
- XMR: 8568eeVjaJ1RQ65ZUn9PRQ8ENtqeX9VVhcCYYhnVLxhV4JtBqw42so2VEUDQZNkFfsH5sXCuV7FN8VhRQ21DkNibTZP57Qt
|
||||
- BCH: bitcoincash:qq6c8vfvxqrk6rhdysgvkhqc24sggkfsx5nqvdlqcg
|
||||
- ETH: 0xD9ee7Db0AD0dc1Dfa7eD53290199ED06beA04692
|
||||
- USDT (Ethereum): 0xD9ee7Db0AD0dc1Dfa7eD53290199ED06beA04692
|
||||
- ZEC: t1fwjQW5gpFhDqXNhxqDWyF9j9WeKvVS5Jg
|
||||
- ZEC shielded: u16rnvkflumf5uw9frngc2lymvmzgdr2mmc9unyu0l44unwfmdcpfm0axujd2w34ct3ye709azxsqge45705lpvvqu264ltzvfay55ygyq
|
||||
- DOGE: D99pV4n9TrPxBPCkQGx4w4SMSa6QjRBxPf
|
||||
- SOL: 7JCf5m3TiHmYKZVr6jCu1KeZVtb9Y1jRMQDU69p5ARnu
|
||||
- please ask if you want to donate any other coins.
|
||||
|
||||
Thank you,
|
||||
|
||||
Evgeny
|
||||
|
||||
SimpleX Chat founder
|
||||
|
||||
## Contents
|
||||
|
||||
- [Why privacy matters](#why-privacy-matters)
|
||||
- [SimpleX approach to privacy and security](#simplex-approach-to-privacy-and-security)
|
||||
- [Complete privacy](#complete-privacy-of-your-identity-profile-contacts-and-metadata)
|
||||
- [Protection against spam and abuse](#the-best-protection-against-spam-and-abuse)
|
||||
- [Ownership and security of your data](#complete-ownership-control-and-security-of-your-data)
|
||||
- [Users own SimpleX network](#users-own-simplex-network)
|
||||
- [Frequently asked questions](#frequently-asked-questions)
|
||||
- [News and updates](#news-and-updates)
|
||||
- [Quick installation of a terminal app](#zap-quick-installation-of-a-terminal-app)
|
||||
- [SimpleX Platform design](#simplex-platform-design)
|
||||
- [Privacy and security: technical details and limitations](#privacy-and-security-technical-details-and-limitations)
|
||||
- [For developers](#for-developers)
|
||||
- [Roadmap](#roadmap)
|
||||
- [Disclaimers, Security contact, License](#disclaimers)
|
||||
|
||||
## Why privacy matters
|
||||
|
||||
Everyone should care about privacy and security of their communications - innocuous conversations can put you in danger even if there is nothing to hide.
|
||||
|
||||
One of the most shocking stories is the experience of [Mohamedou Ould Salahi](https://en.wikipedia.org/wiki/Mohamedou_Ould_Slahi) that he wrote about in his memoir and that is shown in The Mauritanian movie. He was put into Guantanamo camp, without trial, and was tortured there for 15 years after a phone call to his relative in Afghanistan, under suspicion of being involved in 9/11 attacks, even though he lived in Germany for the 10 years prior to the attacks.
|
||||
|
||||
It is not enough to use an end-to-end encrypted messenger, we all should use the messengers that protect the privacy of our personal networks - who we are connected with.
|
||||
|
||||
## SimpleX approach to privacy and security
|
||||
|
||||
### Complete privacy of your identity, profile, contacts and metadata
|
||||
|
||||
**Unlike any other existing messaging platform, SimpleX has no identifiers assigned to the users** - not even random numbers. This protects the privacy of who are you communicating with, hiding it from SimpleX platform servers and from any observers. [Read more](./docs/SIMPLEX.md#full-privacy-of-your-identity-profile-contacts-and-metadata).
|
||||
|
||||
### The best protection against spam and abuse
|
||||
|
||||
As you have no identifier on SimpleX platform, you cannot be contacted unless you share a one-time invitation link or an optional temporary user address. [Read more](./docs/SIMPLEX.md#the-best-protection-against-spam-and-abuse).
|
||||
|
||||
### Complete ownership, control and security of your data
|
||||
|
||||
SimpleX stores all user data on client devices, the messages are only held temporarily on SimpleX relay servers until they are received. [Read more](./docs/SIMPLEX.md#complete-ownership-control-and-security-of-your-data).
|
||||
|
||||
### Users own SimpleX network
|
||||
|
||||
You can use SimpleX with your own servers and still communicate with people using the servers that are pre-configured in the apps or any other SimpleX servers. [Read more](./docs/SIMPLEX.md#users-own-simplex-network).
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
1. _How SimpleX can deliver messages without any user identifiers?_ See [v2 release announcement](./blog/20220511-simplex-chat-v2-images-files.md#the-first-messaging-platform-without-user-identifiers) explaining how SimpleX works.
|
||||
|
||||
2. _Why should I not just use Signal?_ Signal is a centralized platform that uses phone numbers to identify its users and their contacts. It means that while the content of your messages on Signal is protected with robust end-to-end encryption, there is a large amount of meta-data visible to Signal - who you talk with and when.
|
||||
|
||||
3. _How is it different from Matrix, Session, Ricochet, Cwtch, etc., that also don't require user identities?_ Although these platforms do not require a _real identity_, they do rely on anonymous user identities to deliver messages – it can be, for example, an identity key or a random number. Using a persistent user identity, even anonymous, creates a risk that user's connection graph becomes known to the observers and/or service providers, and it can lead to de-anonymizing some users. If the same user profile is used to connect to two different people via any messenger other than SimpleX, these two people can confirm if they are connected to the same person - they would use the same user identifier in the messages. With SimpleX there is no meta-data in common between your conversations with different contacts - the quality that no other messaging platform has.
|
||||
|
||||
## News and updates
|
||||
|
||||
Recent and important updates:
|
||||
|
||||
[Mar 8, 2025. SimpleX Chat v6.3: new user experience and safety in public groups](./blog/20250308-simplex-chat-v6-3-new-user-experience-safety-in-public-groups.md)
|
||||
|
||||
[Jan 14, 2025. SimpleX network: large groups and privacy-preserving content moderation](./blog/20250114-simplex-network-large-groups-privacy-preserving-content-moderation.md)
|
||||
|
||||
[Dec 10, 2024. SimpleX network: preset servers operated by Flux, business chats and more with v6.2 of the apps](./20241210-simplex-network-v6-2-servers-by-flux-business-chats.md)
|
||||
|
||||
[Oct 14, 2024. SimpleX network: security review of protocols design by Trail of Bits, v6.1 released with better calls and user experience.](./blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md)
|
||||
|
||||
[Aug 14, 2024. SimpleX network: the investment from Jack Dorsey and Asymmetric, v6.0 released with the new user experience and private message routing](./blog/20240814-simplex-chat-vision-funding-v6-private-routing-new-user-experience.md)
|
||||
|
||||
[Jun 4, 2024. SimpleX network: private message routing, v5.8 released with IP address protection and chat themes](./blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md)
|
||||
|
||||
[Mar 14, 2024. SimpleX Chat v5.6 beta: adding quantum resistance to Signal double ratchet algorithm.](./blog/20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.md)
|
||||
|
||||
[Nov 25, 2023. SimpleX Chat v5.4 released: link mobile and desktop apps via quantum resistant protocol, and much better groups](./blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.md).
|
||||
|
||||
[Apr 22, 2023. SimpleX Chat: vision and funding, v5.0 released with videos and files up to 1gb](./blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md).
|
||||
|
||||
[Mar 1, 2023. SimpleX File Transfer Protocol – send large files efficiently, privately and securely, soon to be integrated into SimpleX Chat apps.](./blog/20230301-simplex-file-transfer-protocol.md).
|
||||
|
||||
[Nov 8, 2022. Security audit by Trail of Bits, the new website and v4.2 released](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md).
|
||||
|
||||
[All updates](./blog)
|
||||
|
||||
## :zap: Quick installation of a terminal app
|
||||
|
||||
```sh
|
||||
curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/stable/install.sh | bash
|
||||
```
|
||||
|
||||
Once the chat client is installed, simply run `simplex-chat` from your terminal.
|
||||
See [simplex.chat](https://simplex.chat) website for chat demo and the explanations of the system and how SMP protocol works.
|
||||
|
||||

|
||||
|
||||
Read more about [installing and using the terminal app](./docs/CLI.md).
|
||||
## Table of contents
|
||||
|
||||
## SimpleX Platform design
|
||||
- [Disclaimer](#disclaimer)
|
||||
- [Network topology](#network-topology)
|
||||
- [Terminal chat features](#terminal-chat-features)
|
||||
- [Installation](#installation)
|
||||
- [Download chat client](#download-chat-client)
|
||||
- [Build from source](#build-from-source)
|
||||
- [Using Docker](#using-docker)
|
||||
- [Using Haskell stack](#using-haskell-stack)
|
||||
- [Usage](#usage)
|
||||
- [Running the chat client](#running-the-chat-client)
|
||||
- [How to use SimpleX chat](#how-to-use-simplex-chat)
|
||||
- [Groups](#groups)
|
||||
- [Sending files](#sending-files)
|
||||
- [Access chat history](#access-chat-history)
|
||||
- [Future roadmap](#future-roadmap)
|
||||
- [License](#license)
|
||||
|
||||
SimpleX is a client-server network with a unique network topology that uses redundant, disposable message relay nodes to asynchronously pass messages via unidirectional (simplex) message queues, providing recipient and sender anonymity.
|
||||
## Disclaimer
|
||||
|
||||
Unlike P2P networks, all messages are passed through one or several server nodes, that do not even need to have persistence. In fact, the current [SMP server implementation](https://github.com/simplex-chat/simplexmq#smp-server) uses in-memory message storage, persisting only the queue records. SimpleX provides better metadata protection than P2P designs, as no global participant identifiers are used to deliver messages, and avoids [the problems of P2P networks](./docs/SIMPLEX.md#comparison-with-p2p-messaging-protocols).
|
||||
This is WIP implementation of SimpleX chat that implements a new network topology for asynchronous communication combining the advantages and avoiding the disadvantages of federated and P2P networks.
|
||||
|
||||
Unlike federated networks, the server nodes **do not have records of the users**, **do not communicate with each other** and **do not store messages** after they are delivered to the recipients. There is no way to discover the full list of servers participating in SimpleX network. This design avoids the problem of metadata visibility that all federated networks have and better protects from the network-wide attacks.
|
||||
If you expect a software being reliable most of the time and doing something useful, then this is probably not ready for you yet. We do use it for terminal chat though, and it seems to work most of the time - we would really appreciate if you try it and give us your feedback.
|
||||
|
||||
Only the client devices have information about users, their contacts and groups.
|
||||
**Please note:** The main differentiation of SimpleX network is the approach to internet message routing rather than encryption; for that reason no sufficient attention was paid to either TCP transport level encryption or to E2E encryption protocols - they are implemented in an ad hoc way based on RSA and AES algorithms. See [SMP protocol](https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md#appendix-a) on TCP transport encryption protocol (AEAD-GCM scheme, with an AES key negotiation based on RSA key hash known to the client in advance) and [this section](https://github.com/simplex-chat/simplexmq/blob/master/rfcs/2021-01-26-crypto.md#e2e-encryption) on E2E encryption protocol (an ad hoc hybrid scheme a la PGP). These protocols will change in a consumer ready version to something more robust.
|
||||
|
||||
See [SimpleX whitepaper](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/overview-tjr.md) for more information on platform objectives and technical design.
|
||||
## Network topology
|
||||
|
||||
See [SimpleX Chat Protocol](./docs/protocol/simplex-chat.md) for the format of messages sent between chat clients over [SimpleX Messaging Protocol](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/simplex-messaging.md).
|
||||
SimpleX is a decentralized client-server network that uses redundant, disposable nodes to asynchronously pass the messages via message queues, providing receiver and sender anonymity.
|
||||
|
||||
## Privacy and security: technical details and limitations
|
||||
Unlike P2P networks, all messages are passed through one or several (for redundancy) servers, that do not even need to have persistence (in fact, the current [SMP server implementation](https://github.com/simplex-chat/simplexmq#smp-server) uses in-memory message storage, persisting only the queue records) - it provides better metadata protection than P2P designs, as no global participant ID is required, and avoids many [problems of P2P networks](https://github.com/simplex-chat/simplex-chat/blob/master/simplex.md#comparison-with-p2p-messaging-protocols).
|
||||
|
||||
SimpleX Chat is a work in progress – we are releasing improvements as they are ready. You have to decide if the current state is good enough for your usage scenario.
|
||||
Unlike federated networks, the participating server nodes do NOT have records of the users, do NOT communicate with each other, do NOT store messages after they are delivered to the recipients, and there is no way to discover the full list of participating servers - it avoids the problem of metadata visibility that federated networks suffer from and better protects the network, as servers do not communicate with each other. Each server node provides unidirectional "dumb pipes" to the users, that do authorization without authentication, having no knowledge of the the users or their contacts. Each queue is assigned two RSA keys - one for receiver and one for sender - and each queue access is authorized with a signature created using a respective key's private counterpart.
|
||||
|
||||
We compiled a [glossary of terms](./docs/GLOSSARY.md) used to describe communication systems to help understand some terms below and to help compare advantages and disadvantages of various communication systems.
|
||||
The routing of messages relies on the knowledge of client devices how user contacts and groups map at any given moment of time to these disposable queues on server nodes.
|
||||
|
||||
What is already implemented:
|
||||
## Terminal chat features
|
||||
|
||||
1. Instead of user profile identifiers used by all other platforms, even the most private ones, SimpleX uses [pairwise per-queue identifiers](./docs/GLOSSARY.md#pairwise-pseudonymous-identifier) (2 addresses for each unidirectional message queue, with an optional 3rd address for push notifications on iOS, 2 queues in each connection between the users). It makes observing the network graph on the application level more difficult, as for `n` users there can be up to `n * (n-1)` message queues.
|
||||
2. [End-to-end encryption](./docs/GLOSSARY.md#end-to-end-encryption) in each message queue using [NaCl cryptobox](https://nacl.cr.yp.to/box.html). This is added to allow redundancy in the future (passing each message via several servers), to avoid having the same ciphertext in different queues (that would only be visible to the attacker if TLS is compromised). The encryption keys used for this encryption are not rotated, instead we are planning to rotate the queues. Curve25519 keys are used for key negotiation.
|
||||
3. [Double ratchet](./docs/GLOSSARY.md#double-ratchet-algorithm) end-to-end encryption in each conversation between two users (or group members). This is the same algorithm that is used in Signal and many other messaging apps; it provides OTR messaging with [forward secrecy](./docs/GLOSSARY.md#forward-secrecy) (each message is encrypted by its own ephemeral key) and [break-in recovery](./docs/GLOSSARY.md#post-compromise-security) (the keys are frequently re-negotiated as part of the message exchange). Two pairs of Curve448 keys are used for the initial [key agreement](./docs/GLOSSARY.md#key-agreement-protocol), initiating party passes these keys via the connection link, accepting side - in the header of the confirmation message.
|
||||
4. [Post-quantum resistant key exchange](./docs/GLOSSARY.md#post-quantum-cryptography) in double ratchet protocol *on every ratchet step*. Read more in [this post](./blog/20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.md) and also see this [publication by Apple]( https://security.apple.com/blog/imessage-pq3/) explaining the need for post-quantum key rotation.
|
||||
5. Additional layer of encryption using NaCL cryptobox for the messages delivered from the server to the recipient. This layer avoids having any ciphertext in common between sent and received traffic of the server inside TLS (and there are no identifiers in common as well).
|
||||
6. Several levels of [content padding](./docs/GLOSSARY.md#message-padding) to frustrate message size attacks.
|
||||
7. All message metadata, including the time when the message was received by the server (rounded to a second) is sent to the recipients inside an encrypted envelope, so even if TLS is compromised it cannot be observed.
|
||||
8. Only TLS 1.2/1.3 are allowed for client-server connections, limited to cryptographic algorithms: CHACHA20POLY1305_SHA256, Ed25519/Ed448, Curve25519/Curve448.
|
||||
9. To protect against replay attacks SimpleX servers require [tlsunique channel binding](https://www.rfc-editor.org/rfc/rfc5929.html) as session ID in each client command signed with per-queue ephemeral key.
|
||||
10. To protect your IP address from unknown messaging relays, and for per-message transport anonymity (compared with Tor/VPN per-connection anonymity), from v6.0 all SimpleX Chat clients use private message routing by default. Read more in [this post](./blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md#private-message-routing).
|
||||
11. To protect your IP address from unknown file relays, when SOCKS proxy is not enabled SimpleX Chat clients ask for a confirmation before downloading the files from unknown servers.
|
||||
12. To protect your IP address from known servers all SimpleX Chat clients support accessing messaging servers via Tor - see [v3.1 release announcement](./blog/20220808-simplex-chat-v3.1-chat-groups.md) for more details.
|
||||
13. Local database encryption with passphrase - your contacts, groups and all sent and received messages are stored encrypted. If you used SimpleX Chat before v4.0 you need to enable the encryption via the app settings.
|
||||
14. Transport isolation - different TCP connections and Tor circuits are used for traffic of different user profiles, optionally - for different contacts and group member connections.
|
||||
15. Manual messaging queue rotations to move conversation to another SMP relay.
|
||||
16. Sending end-to-end encrypted files using [XFTP protocol](https://simplex.chat/blog/20230301-simplex-file-transfer-protocol.html).
|
||||
17. Local files encryption.
|
||||
18. [Reproducible server builds](./docs/SERVER.md#reproduce-builds).
|
||||
- 1-to-1 chat with multiple people in the same terminal window.
|
||||
- Group messaging.
|
||||
- Sending files to contacts and groups.
|
||||
- Auto-populated recipient name - just type your messages to reply to the sender once the connection is established.
|
||||
- Demo SMP servers available and pre-configured in the app - or you can [deploy your own server](https://github.com/simplex-chat/simplexmq#using-smp-server-and-smp-agent).
|
||||
- No global identity or any names visible to the server(s), ensuring full privacy of your contacts and conversations.
|
||||
- E2E encryption, with RSA public key that has to be passed out-of-band (see [How to use SimpleX chat](#how-to-use-simplex-chat)).
|
||||
- Message signing and verification with automatically generated RSA keys.
|
||||
- Message integrity validation (via including the digests of the previous messages).
|
||||
- Authentication of each command/message by SMP servers with automatically generated RSA key pairs.
|
||||
- TCP transport encryption using SMP transport protocol.
|
||||
|
||||
We plan to add:
|
||||
RSA keys are not used as identity, they are randomly generated for each contact.
|
||||
|
||||
1. Automatic message queue rotation and redundancy. Currently the queues created between two users are used until the queue is manually changed by the user or contact is deleted. We are planning to add automatic queue rotation to make these identifiers temporary and rotate based on some schedule TBC (e.g., every X messages, or every X hours/days).
|
||||
2. Message "mixing" - adding latency to message delivery, to protect against traffic correlation by message time.
|
||||
3. Reproducible clients builds – this is a complex problem, but we are aiming to have it in 2025 at least partially.
|
||||
4. Recipients' XFTP relays to reduce traffic and conceal IP addresses from the relays chosen, and potentially controlled, by another party.
|
||||
## Installation
|
||||
|
||||
## For developers
|
||||
### Download chat client
|
||||
|
||||
You can:
|
||||
Download the chat binary for your system from the [latest stable release](https://github.com/simplex-chat/simplex-chat/releases) and make it executable as shown below.
|
||||
|
||||
- use SimpleX Chat library to integrate chat functionality into your mobile apps.
|
||||
- create chat bots and services in Haskell - see [simple](./apps/simplex-bot/) and more [advanced chat bot example](./apps/simplex-bot-advanced/).
|
||||
- create chat bots and services in any language running SimpleX Chat terminal CLI as a local WebSocket server. See [TypeScript SimpleX Chat client](./packages/simplex-chat-client/) and [JavaScript chat bot example](./packages/simplex-chat-client/typescript/examples/squaring-bot.js).
|
||||
- run [simplex-chat terminal CLI](./docs/CLI.md) to execute individual chat commands, e.g. to send messages as part of shell script execution.
|
||||
#### Linux and MacOS
|
||||
|
||||
If you are considering developing with SimpleX platform please get in touch for any advice and support.
|
||||
```sh
|
||||
chmod +x <binary>
|
||||
mv <binary> ~/.local/bin/simplex-chat
|
||||
```
|
||||
|
||||
Please also join [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2F6eHqy7uAbZPOcA6qBtrQgQquVlt4Ll91%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAqV_pg3FF00L98aCXp4D3bOs4Sxv_UmSd-gb0juVoQVs%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22XonlixcHBIb2ijCehbZoiw%3D%3D%22%7D) group to ask any questions and share your success stories.
|
||||
(or any other preferred location on PATH).
|
||||
|
||||
## Roadmap
|
||||
On MacOS you also need to [allow Gatekeeper to run it](https://support.apple.com/en-us/HT202491).
|
||||
|
||||
- ✅ Easy to deploy SimpleX server with in-memory message storage, without any dependencies.
|
||||
- ✅ Terminal (console) client with groups and files support.
|
||||
- ✅ One-click SimpleX server deployment on Linode.
|
||||
- ✅ End-to-end encryption using double-ratchet protocol with additional encryption layer.
|
||||
- ✅ Mobile apps v1 for Android and iOS.
|
||||
- ✅ Private instant notifications for Android using background service.
|
||||
- ✅ Haskell chat bot templates.
|
||||
- ✅ v2.0 - supporting images and files in mobile apps.
|
||||
- ✅ Manual chat history deletion.
|
||||
- ✅ End-to-end encrypted WebRTC audio and video calls via the mobile apps.
|
||||
- ✅ Privacy preserving instant notifications for iOS using Apple Push Notification service.
|
||||
- ✅ Chat database export and import.
|
||||
- ✅ Chat groups in mobile apps.
|
||||
- ✅ Connecting to messaging servers via Tor.
|
||||
- ✅ Dual server addresses to access messaging servers as v3 hidden services.
|
||||
- ✅ Chat server and TypeScript client SDK to develop chat interfaces, integrations and chat bots (ready for announcement).
|
||||
- ✅ Incognito mode to share a new random name with each contact.
|
||||
- ✅ Chat database encryption.
|
||||
- ✅ Automatic chat history deletion.
|
||||
- ✅ Links to join groups and improve groups stability.
|
||||
- ✅ Voice messages (with recipient opt-out per contact).
|
||||
- ✅ Basic authentication for SMP servers (to authorize creating new queues).
|
||||
- ✅ View deleted messages, full message deletion by sender (with recipient opt-in per contact).
|
||||
- ✅ Block screenshots and view in recent apps.
|
||||
- ✅ Advanced server configuration.
|
||||
- ✅ Disappearing messages (with recipient opt-in per-contact).
|
||||
- ✅ "Live" messages.
|
||||
- ✅ Contact verification via a separate out-of-band channel.
|
||||
- ✅ Multiple user profiles in the same chat database.
|
||||
- ✅ Optionally avoid re-using the same TCP session for multiple connections.
|
||||
- ✅ Preserve message drafts.
|
||||
- ✅ File server to optimize for efficient and private sending of large files.
|
||||
- ✅ Improved audio & video calls.
|
||||
- ✅ Support older Android OS and 32-bit CPUs.
|
||||
- ✅ Hidden chat profiles.
|
||||
- ✅ Sending and receiving large files via [XFTP protocol](./blog/20230301-simplex-file-transfer-protocol.md).
|
||||
- ✅ Video messages.
|
||||
- ✅ App access passcode.
|
||||
- ✅ Improved Android app UI design.
|
||||
- ✅ Optional alternative access password.
|
||||
- ✅ Message reactions
|
||||
- ✅ Message editing history
|
||||
- ✅ Reduced battery and traffic usage in large groups.
|
||||
- ✅ Message delivery confirmation (with sender opt-out per contact).
|
||||
- ✅ Desktop client.
|
||||
- ✅ Encryption of local files stored in the app.
|
||||
- ✅ Using mobile profiles from the desktop app.
|
||||
- ✅ Private notes.
|
||||
- ✅ Improve sending videos (including encryption of locally stored videos).
|
||||
- ✅ Post-quantum resistant key exchange in double ratchet protocol.
|
||||
- ✅ Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic).
|
||||
- ✅ Support multiple network operators in the app.
|
||||
- 🏗 Large groups, communities and public channels.
|
||||
- 🏗 Short links to connect and join groups.
|
||||
- 🏗 Improve stability and reduce battery usage.
|
||||
- 🏗 Improve experience for the new users.
|
||||
- Privacy & security slider - a simple way to set all settings at once.
|
||||
- SMP queue redundancy and rotation (manual is supported).
|
||||
- Include optional message into connection request sent via contact address.
|
||||
- Improved navigation and search in the conversation (expand and scroll to quoted message, scroll to search results, etc.).
|
||||
- Feeds/broadcasts.
|
||||
- Ephemeral/disappearing/OTR conversations with the existing contacts.
|
||||
- Privately share your location.
|
||||
- Web widgets for custom interactivity in the chats.
|
||||
- Programmable chat automations / rules (automatic replies/forward/deletion/sending, reminders, etc.).
|
||||
- Privacy-preserving identity server for optional DNS-based contact/group addresses to simplify connection and discovery, but not used to deliver messages:
|
||||
#### Windows
|
||||
|
||||
```sh
|
||||
move <binary> %APPDATA%/local/bin/simplex-chat.exe
|
||||
```
|
||||
|
||||
### Build from source
|
||||
|
||||
#### Using Docker
|
||||
|
||||
On Linux, you can build the chat executable using [docker build with custom output](https://docs.docker.com/engine/reference/commandline/build/#custom-build-outputs):
|
||||
|
||||
```shell
|
||||
$ git clone git@github.com:simplex-chat/simplex-chat.git
|
||||
$ cd simplex-chat
|
||||
$ DOCKER_BUILDKIT=1 docker build --output ~/.local/bin .
|
||||
```
|
||||
|
||||
> **Please note:** If you encounter ``version `GLIBC_2.28' not found`` error, rebuild it with `haskell:8.8.4-stretch` base image (change it in your local [Dockerfile](Dockerfile)).
|
||||
|
||||
#### Using Haskell stack
|
||||
|
||||
Install [Haskell stack](https://docs.haskellstack.org/en/stable/README/):
|
||||
|
||||
```shell
|
||||
curl -sSL https://get.haskellstack.org/ | sh
|
||||
```
|
||||
|
||||
and build the project:
|
||||
|
||||
```shell
|
||||
$ git clone git@github.com:simplex-chat/simplex-chat.git
|
||||
$ cd simplex-chat
|
||||
$ stack install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Running the chat client
|
||||
|
||||
To start the chat client, run `simplex-chat` from the terminal.
|
||||
|
||||
By default, app data directory is created in the home directory (`~/.simplex`, or `%APPDATA%/simplex` on Windows), and two SQLite database files `simplex.chat.db` and `simplex.agent.db` are initialized in it.
|
||||
|
||||
To specify a different file path prefix for the database files use `-d` command line option:
|
||||
|
||||
```shell
|
||||
$ simplex-chat -d alice
|
||||
```
|
||||
|
||||
Running above, for example, would create `alice.chat.db` and `alice.agent.db` database files in current directory.
|
||||
|
||||
Default SMP servers are hosted on Linode (London, UK and Fremont, CA) - they are [pre-configured in the app](https://github.com/simplex-chat/simplex-chat/blob/master/src/Simplex/Chat/Options.hs#L40). Base-64 encoded string after server host is the transport key digest.
|
||||
|
||||
If you deployed your own SMP server(s) you can configure client via `-s` option:
|
||||
|
||||
```shell
|
||||
$ simplex-chat -s smp.example.com:5223#KXNE1m2E1m0lm92WGKet9CL6+lO742Vy5G6nsrkvgs8=
|
||||
```
|
||||
|
||||
The base-64 encoded string in server address is the digest of RSA transport handshake key that the server will generate on the first run and output its digest.
|
||||
|
||||
You can still talk to people using default or any other server - it only affects the location of the message queue when you initiate the connection (and the reply queue can be on another server, as set by the other party's client).
|
||||
|
||||
Run `simplex-chat -h` to see all available options.
|
||||
|
||||
### How to use SimpleX chat
|
||||
|
||||
Once you have started the chat, you will be prompted to specify your "display name" and an optional "full name" to create a local chat profile. Your display name is an alias for your contacts to refer to you by - it is not unique and does not serve as a global identity. In case different contacts chose the same display name, the chat client adds a numeric suffix to their local display names.
|
||||
|
||||
This diagram shows how to connect and message a contact:
|
||||
|
||||
<div align="center">
|
||||
<img align="center" src="images/how-to-use-simplex.svg">
|
||||
</div>
|
||||
|
||||
Once you've set up your local profile, enter `/c` (for `/connect`) to create a new connection and generate an invitation. Send this invitation to your contact via any other channel.
|
||||
|
||||
You are able to create multiple invitations by entering `/connect` multiple times and sending these invitations to the corresponding contacts you'd like to connect with.
|
||||
|
||||
The invitation has the format `smp::<server>::<queue_id>::<rsa_public_key_for_this_queue_only>`. The invitation can only be used once and even if this is intercepted, the attacker would not be able to use it to send you the messages via this queue once your contact confirms that the connection is established.
|
||||
|
||||
The contact who received the invitation should enter `/c <invitation>` to accept the connection. This establishes the connection, and both parties are notified.
|
||||
|
||||
They would then use `@<name> <message>` commands to send messages. You may also just start typing a message to send it to the contact that was the last.
|
||||
|
||||
Use `/help` in chat to see the list of available commands.
|
||||
|
||||
### Groups
|
||||
|
||||
To create a group use `/g <group>`, then add contacts to it with `/a <group> <name>`and send messages with `#<group> <message>`. Use `/help groups` for other commands.
|
||||
|
||||

|
||||
|
||||
> **Please note**: the groups are not stored on any server, they are maintained as a list of members in the app database to whom the messages will be sent.
|
||||
|
||||
### Sending files
|
||||
|
||||
You can send a file to your contact with `/f @<contact> <file_path>` - the recipient will have to accept it before it is sent. Use `/help files` for other commands.
|
||||
|
||||

|
||||
|
||||
You can send files to a group with `/f #<group> <file_path>`.
|
||||
|
||||
### Access chat history
|
||||
|
||||
> 🚧 **Section currently out of date - will be updated soon** 🏗
|
||||
|
||||
SimpleX chat stores all your contacts and conversations in a local database file, making it private and portable by design, fully owned and controlled by you.
|
||||
|
||||
You can search your chat history via SQLite database file:
|
||||
|
||||
```
|
||||
sqlite3 ~/.simplex/smp-chat.db
|
||||
```
|
||||
|
||||
Now you can query `messages` table, for example:
|
||||
|
||||
```sql
|
||||
select * from messages
|
||||
where conn_alias = cast('alice' as blob)
|
||||
and body like '%cats%'
|
||||
order by internal_id desc;
|
||||
```
|
||||
|
||||
> **Please note:** SQLite foreign key constraints are disabled by default, and must be **[enabled separately for each database connection](https://sqlite.org/foreignkeys.html#fk_enable)**. The latter can be achieved by running `PRAGMA foreign_keys = ON;` command on an open database connection. By running data altering queries without enabling foreign keys prior to that, you may risk putting your database in an inconsistent state.
|
||||
|
||||
## Future roadmap
|
||||
|
||||
1. Mobile and desktop apps (in progress).
|
||||
2. SMP protocol improvements:
|
||||
- SMP queue redundancy and rotation.
|
||||
- Message delivery confirmation.
|
||||
- Support multiple devices.
|
||||
3. Privacy-preserving identity server for optional DNS-based contact/group addresses to simplify connection and discovery, but not used to deliver messages:
|
||||
- keep all your contacts and groups even if you lose the domain.
|
||||
- the server doesn't have information about your contacts and groups.
|
||||
- High capacity multi-node SMP relays.
|
||||
|
||||
## Disclaimers
|
||||
|
||||
[SimpleX protocols and security model](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) was reviewed, and had many breaking changes and improvements in v1.0.0.
|
||||
|
||||
The implementation security assessment of SimpleX cryptography and networking was done in October 2022 by [Trail of Bits](https://www.trailofbits.com/about) – see [the announcement](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md).
|
||||
|
||||
The cryptographic review of SimpleX protocols was done in July 2024 by Trail of Bits – see [the announcement](./blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md).
|
||||
|
||||
SimpleX Chat is still a relatively early stage platform (the mobile apps were released in March 2022), so you may discover some bugs and missing features. We would really appreciate if you let us know anything that needs to be fixed or improved.
|
||||
|
||||
The default servers configured in the app are provided on the best effort basis. We are currently not guaranteeing any SLAs, although historically our servers had over 99.9% uptime each.
|
||||
|
||||
We have never provided or have been requested access to our servers or any information from our servers by any third parties. If we are ever requested to provide such access or information, we will be following due legal process.
|
||||
|
||||
We do not log IP addresses of the users and we do not perform any traffic correlation on our servers. If transport level security is critical you must use Tor or some other similar network to access messaging servers. We will be improving the client applications to reduce the opportunities for traffic correlation.
|
||||
|
||||
Please read more in [Privacy Policy](./PRIVACY.md).
|
||||
|
||||
## Security contact
|
||||
|
||||
Please see our [Security Policy](./docs/SECURITY.md) on how to report security vulnerabilities to us. We will coordinate the fix and disclosure.
|
||||
|
||||
Please do NOT report security vulnerabilities via GitHub issues.
|
||||
4. Media server to optimize sending large files to groups.
|
||||
5. Channels server for large groups and broadcast channels.
|
||||
|
||||
## License
|
||||
|
||||
[AGPL v3](./LICENSE)
|
||||
|
||||
[<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/apple_store.svg" alt="iOS app" height="42">](https://apps.apple.com/us/app/simplex-chat/id1605771084)
|
||||
|
||||
[](https://play.google.com/store/apps/details?id=chat.simplex.app)
|
||||
|
||||
[<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/f_droid.svg" alt="F-Droid" height="41">](https://app.simplex.chat)
|
||||
|
||||
[<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/testflight.png" alt="iOS TestFlight" height="41">](https://testflight.apple.com/join/DWuT2LQu)
|
||||
|
||||
[<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/apk_icon.png" alt="APK" height="41">](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk)
|
||||
|
|
71
apps/ios/.gitignore
vendored
|
@ -1,71 +0,0 @@
|
|||
## User settings
|
||||
xcuserdata/
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
|
||||
## App packaging
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
# Swift Package Manager
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||
# Packages/
|
||||
# Package.pins
|
||||
# Package.resolved
|
||||
# *.xcodeproj
|
||||
#
|
||||
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
||||
# hence it is not needed unless you have added a package configuration file to your project
|
||||
# .swiftpm
|
||||
|
||||
.build/
|
||||
|
||||
# CocoaPods
|
||||
#
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
#
|
||||
# Pods/
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||
# *.xcworkspace
|
||||
|
||||
# Carthage
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
# Carthage/Checkouts
|
||||
|
||||
Carthage/Build/
|
||||
|
||||
# Accio dependency management
|
||||
Dependencies/
|
||||
.accio/
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo.
|
||||
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots/**/*.png
|
||||
fastlane/test_output
|
||||
|
||||
# Code Injection
|
||||
#
|
||||
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
||||
# https://github.com/johnno1962/injectionforxcode
|
||||
|
||||
iOSInjectionProject/
|
||||
|
||||
Libraries/
|
||||
|
||||
Shared/MyPlayground.playground/*
|
||||
|
||||
testpush.sh
|
|
@ -1,29 +0,0 @@
|
|||
# Localization
|
||||
|
||||
## Creating localization keys
|
||||
|
||||
There are three ways XCode generates localization keys from strings:
|
||||
|
||||
1. Automatically, from the texts used in standard components `Text`, `Label`, `Button`, etc.
|
||||
|
||||
2. All strings passed to view variables and function parameters declared as `LocalizedStringKey` type. Only string constants (possibly, with interpolation) or other variables of type `LocalizedStringKey` can be passed to these parameters. See, for example, ContentView.swift.
|
||||
|
||||
3. All strings wrapped in `NSLocalizedString`. Please note that such strings do not support swift interpolation, instead formatted strings should be used:
|
||||
|
||||
```swift
|
||||
String.localizedStringWithFormat(NSLocalizedString("You can now send messages to %@", comment: "notification body"), value)
|
||||
```
|
||||
|
||||
## Adding strings to the existing localizations
|
||||
|
||||
1. Choose `Product -> Export Localizations...` in the menu, choose `ios` folder as the destination and `SimpleX Localizations` as the folder name, confirm to overwrite it (make sure not to save to subfolder).
|
||||
2. Add `target` keys to the localizations that were added or changed.
|
||||
3. Choose `Product -> Import Localizations...` for any non-English folders - that would update Localizable files.
|
||||
|
||||
Localizable files values can be edited directly, the changes will be included in the next export. Following the process above though guarantees that all strings are localized.
|
||||
|
||||
## Development
|
||||
|
||||
Make sure to enable the option `Show non-localized strings` in `Product -> Scheme -> Edit scheme...` menu - it will be showing all non-localized strings as all caps.
|
||||
|
||||
Read more about editing XLIFF and string files here: https://developer.apple.com/documentation/xcode/editing-xliff-and-strings-files
|
|
@ -1 +0,0 @@
|
|||
# SimpleX Chat iOS app
|
|
@ -1,209 +0,0 @@
|
|||
//
|
||||
// AppDelegate.swift
|
||||
// SimpleX
|
||||
//
|
||||
// Created by Evgeny on 30/03/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SimpleXChat
|
||||
import SwiftUI
|
||||
|
||||
class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||
logger.debug("AppDelegate: didFinishLaunchingWithOptions")
|
||||
application.registerForRemoteNotifications()
|
||||
removePasscodesIfReinstalled()
|
||||
prepareForLaunch()
|
||||
deleteOldChatArchive()
|
||||
return true
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
|
||||
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
|
||||
let m = ChatModel.shared
|
||||
let deviceToken = DeviceToken(pushProvider: PushProvider(env: pushEnvironment), token: token)
|
||||
m.deviceToken = deviceToken
|
||||
// savedToken is set in startChat, when it is started before this method is called
|
||||
if m.savedToken != nil {
|
||||
registerToken(token: deviceToken)
|
||||
}
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
||||
logger.error("AppDelegate: didFailToRegisterForRemoteNotificationsWithError \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication,
|
||||
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
|
||||
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
||||
logger.debug("AppDelegate: didReceiveRemoteNotification")
|
||||
let m = ChatModel.shared
|
||||
if let ntfData = userInfo["notificationData"] as? [AnyHashable : Any],
|
||||
m.notificationMode != .off {
|
||||
if let verification = ntfData["verification"] as? String,
|
||||
let nonce = ntfData["nonce"] as? String {
|
||||
if let token = m.deviceToken {
|
||||
logger.debug("AppDelegate: didReceiveRemoteNotification: verification, confirming \(verification)")
|
||||
Task {
|
||||
do {
|
||||
if case .active = m.tokenStatus {} else { m.tokenStatus = .confirmed }
|
||||
try await apiVerifyToken(token: token, nonce: nonce, code: verification)
|
||||
m.tokenStatus = .active
|
||||
} catch {
|
||||
if let cr = error as? ChatError, case .errorAgent(.NTF(.AUTH)) = cr {
|
||||
m.tokenStatus = .expired
|
||||
}
|
||||
logger.error("AppDelegate: didReceiveRemoteNotification: apiVerifyToken or apiIntervalNofication error: \(responseError(error))")
|
||||
}
|
||||
completionHandler(.newData)
|
||||
}
|
||||
} else {
|
||||
completionHandler(.noData)
|
||||
}
|
||||
} else if let checkMessages = ntfData["checkMessages"] as? Bool, checkMessages {
|
||||
logger.debug("AppDelegate: didReceiveRemoteNotification: checkMessages")
|
||||
if m.ntfEnablePeriodic && allowBackgroundRefresh() && BGManager.shared.lastRanLongAgo {
|
||||
receiveMessages(completionHandler)
|
||||
} else {
|
||||
completionHandler(.noData)
|
||||
}
|
||||
} else {
|
||||
completionHandler(.noData)
|
||||
}
|
||||
} else {
|
||||
completionHandler(.noData)
|
||||
}
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
logger.debug("DEBUGGING: AppDelegate: applicationWillTerminate")
|
||||
ChatModel.shared.filesToDelete.forEach {
|
||||
removeFile($0)
|
||||
}
|
||||
ChatModel.shared.filesToDelete = []
|
||||
terminateChat()
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication,
|
||||
configurationForConnecting connectingSceneSession: UISceneSession,
|
||||
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||
let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
|
||||
if connectingSceneSession.role == .windowApplication {
|
||||
configuration.delegateClass = SceneDelegate.self
|
||||
}
|
||||
return configuration
|
||||
}
|
||||
|
||||
private func receiveMessages(_ completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
||||
let complete = BGManager.shared.completionHandler {
|
||||
logger.debug("AppDelegate: completed BGManager.receiveMessages")
|
||||
completionHandler(.newData)
|
||||
}
|
||||
|
||||
BGManager.shared.receiveMessages(complete)
|
||||
}
|
||||
|
||||
private func removePasscodesIfReinstalled() {
|
||||
// Check for the database existence, because app and self destruct passcodes
|
||||
// will be saved and restored by iOS when a user deletes and re-installs the app.
|
||||
// In this case the database and settings will be deleted, but the passcodes won't be.
|
||||
// Deleting passcodes ensures that the user will not get stuck on "Opening app..." screen.
|
||||
if (kcAppPassword.get() != nil || kcSelfDestructPassword.get() != nil) &&
|
||||
!UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA) && !hasDatabase() {
|
||||
_ = kcAppPassword.remove()
|
||||
_ = kcSelfDestructPassword.remove()
|
||||
_ = kcDatabasePassword.remove()
|
||||
}
|
||||
}
|
||||
|
||||
private func prepareForLaunch() {
|
||||
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
|
||||
}
|
||||
|
||||
static func keepScreenOn(_ on: Bool) {
|
||||
UIApplication.shared.isIdleTimerDisabled = on
|
||||
}
|
||||
}
|
||||
|
||||
class SceneDelegate: NSObject, ObservableObject, UIWindowSceneDelegate {
|
||||
var window: UIWindow?
|
||||
static var windowStatic: UIWindow?
|
||||
var windowScene: UIWindowScene?
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
UITableView.appearance().backgroundColor = .clear
|
||||
guard let windowScene = scene as? UIWindowScene else { return }
|
||||
self.windowScene = windowScene
|
||||
window = windowScene.keyWindow
|
||||
SceneDelegate.windowStatic = windowScene.keyWindow
|
||||
migrateAccentColorAndTheme()
|
||||
ThemeManager.applyTheme(currentThemeDefault.get())
|
||||
ThemeManager.adjustWindowStyle()
|
||||
}
|
||||
|
||||
private func migrateAccentColorAndTheme() {
|
||||
let defs = UserDefaults.standard
|
||||
/// For checking migration
|
||||
// themeOverridesDefault.set([])
|
||||
// currentThemeDefault.set(DefaultTheme.SYSTEM_THEME_NAME)
|
||||
// defs.set(0.5, forKey: DEFAULT_ACCENT_COLOR_RED)
|
||||
// defs.set(0.3, forKey: DEFAULT_ACCENT_COLOR_GREEN)
|
||||
// defs.set(0.8, forKey: DEFAULT_ACCENT_COLOR_BLUE)
|
||||
|
||||
let userInterfaceStyle = getUserInterfaceStyleDefault()
|
||||
if defs.double(forKey: DEFAULT_ACCENT_COLOR_GREEN) == 0 && userInterfaceStyle == .unspecified {
|
||||
// No migration needed or already migrated
|
||||
return
|
||||
}
|
||||
|
||||
let defaultAccentColor = Color(cgColor: CGColor(red: 0.000, green: 0.533, blue: 1.000, alpha: 1))
|
||||
let accentColor = Color(cgColor: getUIAccentColorDefault())
|
||||
if accentColor != defaultAccentColor {
|
||||
let colors = ThemeColors(primary: accentColor.toReadableHex())
|
||||
var overrides = themeOverridesDefault.get()
|
||||
var themeIds = currentThemeIdsDefault.get()
|
||||
switch userInterfaceStyle {
|
||||
case .light:
|
||||
let light = ThemeOverrides(base: DefaultTheme.LIGHT, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename))
|
||||
overrides.append(light)
|
||||
themeOverridesDefault.set(overrides)
|
||||
themeIds[DefaultTheme.LIGHT.themeName] = light.themeId
|
||||
currentThemeIdsDefault.set(themeIds)
|
||||
ThemeManager.applyTheme(DefaultTheme.LIGHT.themeName)
|
||||
case .dark:
|
||||
let dark = ThemeOverrides(base: DefaultTheme.DARK, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename))
|
||||
overrides.append(dark)
|
||||
themeOverridesDefault.set(overrides)
|
||||
themeIds[DefaultTheme.DARK.themeName] = dark.themeId
|
||||
currentThemeIdsDefault.set(themeIds)
|
||||
ThemeManager.applyTheme(DefaultTheme.DARK.themeName)
|
||||
case .unspecified:
|
||||
let light = ThemeOverrides(base: DefaultTheme.LIGHT, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename))
|
||||
let dark = ThemeOverrides(base: DefaultTheme.DARK, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename))
|
||||
overrides.append(light)
|
||||
overrides.append(dark)
|
||||
themeOverridesDefault.set(overrides)
|
||||
themeIds[DefaultTheme.LIGHT.themeName] = light.themeId
|
||||
themeIds[DefaultTheme.DARK.themeName] = dark.themeId
|
||||
currentThemeIdsDefault.set(themeIds)
|
||||
ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME)
|
||||
@unknown default: ()
|
||||
}
|
||||
} else if userInterfaceStyle != .unspecified {
|
||||
let themeName = switch userInterfaceStyle {
|
||||
case .light: DefaultTheme.LIGHT.themeName
|
||||
case .dark: DefaultTheme.DARK.themeName
|
||||
default: DefaultTheme.SYSTEM_THEME_NAME
|
||||
}
|
||||
ThemeManager.applyTheme(themeName)
|
||||
}
|
||||
defs.removeObject(forKey: DEFAULT_ACCENT_COLOR_RED)
|
||||
defs.removeObject(forKey: DEFAULT_ACCENT_COLOR_GREEN)
|
||||
defs.removeObject(forKey: DEFAULT_ACCENT_COLOR_BLUE)
|
||||
defs.removeObject(forKey: DEFAULT_USER_INTERFACE_STYLE)
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "0.533",
|
||||
"red" : "0.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 6.3 KiB |
|
@ -1,158 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "60.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "87.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "57.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "57x57"
|
||||
},
|
||||
{
|
||||
"filename" : "114.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "57x57"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "180.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "20.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "50.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "50x50"
|
||||
},
|
||||
{
|
||||
"filename" : "100.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "50x50"
|
||||
},
|
||||
{
|
||||
"filename" : "72.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "72x72"
|
||||
},
|
||||
{
|
||||
"filename" : "144.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "72x72"
|
||||
},
|
||||
{
|
||||
"filename" : "76.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "152.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "167.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 141 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 8 KiB |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4.3 KiB |
|
@ -1,158 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "60.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "87.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "57.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "57x57"
|
||||
},
|
||||
{
|
||||
"filename" : "114.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "57x57"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "180.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "20.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "50.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "50x50"
|
||||
},
|
||||
{
|
||||
"filename" : "100.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "50x50"
|
||||
},
|
||||
{
|
||||
"filename" : "72.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "72x72"
|
||||
},
|
||||
{
|
||||
"filename" : "144.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "72x72"
|
||||
},
|
||||
{
|
||||
"filename" : "76.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "152.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "167.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
},
|
||||
"symbols": [
|
||||
{
|
||||
"filename": "checkmark.2.svg",
|
||||
"idiom": "universal"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,227 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="3300px" height="2200px" viewBox="0 0 3300 2200" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>checkmark.2</title>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="double.checkmark">
|
||||
<g id="Notes">
|
||||
<rect id="artboard" fill="#FFFFFF" fill-rule="nonzero" x="0" y="0" width="3300" height="2200"></rect>
|
||||
<line x1="263" y1="292" x2="3036" y2="292" id="Path" stroke="#000000" stroke-width="0.5"></line>
|
||||
<text id="Weight/Scale-Variations" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||
font-size="13" font-weight="normal">
|
||||
<tspan x="263" y="322">Weight/Scale Variations</tspan>
|
||||
</text>
|
||||
<text id="Ultralight" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="533.711" y="322">Ultralight</tspan>
|
||||
</text>
|
||||
<text id="Thin" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="843.422" y="322">Thin</tspan>
|
||||
</text>
|
||||
<text id="Light" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="1138.63" y="322">Light</tspan>
|
||||
</text>
|
||||
<text id="Regular" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="1426.84" y="322">Regular</tspan>
|
||||
</text>
|
||||
<text id="Medium" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="1723.06" y="322">Medium</tspan>
|
||||
</text>
|
||||
<text id="Semibold" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2015.77" y="322">Semibold</tspan>
|
||||
</text>
|
||||
<text id="Bold" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2326.48" y="322">Bold</tspan>
|
||||
</text>
|
||||
<text id="Heavy" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2618.19" y="322">Heavy</tspan>
|
||||
</text>
|
||||
<text id="Black" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2917.4" y="322">Black</tspan>
|
||||
</text>
|
||||
<line x1="263" y1="1903" x2="3036" y2="1903" id="Path" stroke="#000000" stroke-width="0.5"></line>
|
||||
<g id="Group" transform="translate(264.3672, 1918.0684)" fill="#000000" fill-rule="nonzero">
|
||||
<path
|
||||
d="M7.88088,15.76172 C12.18752,15.76172 15.7715,12.1875 15.7715,7.88086 C15.7715,3.57422 12.17774,0 7.8711,0 C3.57422,0 0,3.57422 0,7.88086 C0,12.1875 3.58398,15.76172 7.88086,15.76172 L7.88088,15.76172 Z M7.88088,14.277344 C4.33596,14.277344 1.50392,11.435544 1.50392,7.880864 C1.50392,4.326184 4.32618,1.484384 7.8711,1.484384 C11.42578,1.484384 14.27734,4.326184 14.27734,7.880864 C14.27734,11.435544 11.43554,14.277344 7.88086,14.277344 L7.88088,14.277344 Z M4.28712,7.880864 C4.28712,8.310552 4.589854,8.60352 5.039074,8.60352 L7.138674,8.60352 L7.138674,10.72266 C7.138674,11.162114 7.431642,11.464848 7.86133,11.464848 C8.310548,11.464848 8.603518,11.162114 8.603518,10.72266 L8.603518,8.60352 L10.722658,8.60352 C11.162112,8.60352 11.464846,8.310552 11.464846,7.880864 C11.464846,7.44141 11.162112,7.138676 10.722658,7.138676 L8.603518,7.138676 L8.603518,5.029296 C8.603518,4.580078 8.31055,4.277342 7.86133,4.277342 C7.431642,4.277342 7.138674,4.580076 7.138674,5.029296 L7.138674,7.138676 L5.039074,7.138676 C4.589856,7.138676 4.28712,7.44141 4.28712,7.880864 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<g id="Group" transform="translate(283.254, 1915.9883)" fill="#000000" fill-rule="nonzero">
|
||||
<path
|
||||
d="M9.96094,19.92188 C15.41016,19.92188 19.92188,15.4004 19.92188,9.96094 C19.92188,4.51172 15.4004,0 9.95118,0 C4.51172,0 0,4.51172 0,9.96094 C0,15.4004 4.52148,19.92188 9.96094,19.92188 Z M9.96094,18.261724 C5.35156,18.261724 1.66992,14.570324 1.66992,9.960944 C1.66992,5.351564 5.3418,1.660164 9.95116,1.660164 C14.56052,1.660164 18.2617,5.351564 18.2617,9.960944 C18.2617,14.570324 14.5703,18.261724 9.96092,18.261724 L9.96094,18.261724 Z M5.4297,9.960944 C5.4297,10.43946 5.761732,10.761726 6.259778,10.761726 L9.130878,10.761726 L9.130878,13.642586 C9.130878,14.130868 9.46291,14.472664 9.941424,14.472664 C10.4297,14.472664 10.771502,14.140632 10.771502,13.642586 L10.771502,10.761726 L13.652362,10.761726 C14.140644,10.761726 14.48244,10.43946 14.48244,9.960944 C14.48244,9.472662 14.140644,9.130866 13.652362,9.130866 L10.771502,9.130866 L10.771502,6.259766 C10.771502,5.76172 10.4297,5.419922 9.941424,5.419922 C9.462908,5.419922 9.130878,5.761718 9.130878,6.259766 L9.130878,9.130866 L6.259778,9.130866 C5.761732,9.130866 5.4297,9.472662 5.4297,9.960944 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<g id="Group" transform="translate(307.1798, 1913.2246)" fill="#000000" fill-rule="nonzero">
|
||||
<path
|
||||
d="M12.71486,25.43944 C19.67776,25.43944 25.43946,19.67772 25.43946,12.7246 C25.43946,5.7617 19.66798,0 12.70508,0 C5.75196,0 -1.42108547e-15,5.76172 -1.42108547e-15,12.7246 C-1.42108547e-15,19.67772 5.76172,25.43944 12.71484,25.43944 L12.71486,25.43944 Z M12.71486,23.623034 C6.6797,23.623034 1.82618,18.759754 1.82618,12.724594 C1.82618,6.679674 6.66994,1.826154 12.70508,1.826154 C18.75,1.826154 23.61328,6.679674 23.61328,12.724594 C23.61328,18.759754 18.75976,23.623034 12.71484,23.623034 L12.71486,23.623034 Z M6.94338,12.724594 C6.94338,13.242172 7.314474,13.6035 7.861348,13.6035 L11.806668,13.6035 L11.806668,17.55858 C11.806668,18.09569 12.177762,18.476548 12.69534,18.476548 C13.23245,18.476548 13.603544,18.105454 13.603544,17.55858 L13.603544,13.6035 L17.558624,13.6035 C18.095734,13.6035 18.476592,13.242172 18.476592,12.724594 C18.476592,12.177718 18.105498,11.806626 17.558624,11.806626 L13.603544,11.806626 L13.603544,7.861306 C13.603544,7.31443 13.23245,6.933572 12.69534,6.933572 C12.177762,6.933572 11.806668,7.314432 11.806668,7.861306 L11.806668,11.806626 L7.861348,11.806626 C7.314472,11.806626 6.94338,12.17772 6.94338,12.724594 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<text id="Design-Variations" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="263" y="1953">Design Variations</tspan>
|
||||
</text>
|
||||
<text id="Symbols-are-supported-in-up-to-nine-weights-and-three-scales." fill="#000000"
|
||||
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="263" y="1971">Symbols are supported in up to nine weights and three scales.</tspan>
|
||||
</text>
|
||||
<text id="For-optimal-layout-with-text-and-other-symbols,-vertically-align" fill="#000000"
|
||||
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="263" y="1989">For optimal layout with text and other symbols, vertically align</tspan>
|
||||
</text>
|
||||
<text id="symbols-with-the-adjacent-text." fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||
font-size="13" font-weight="normal">
|
||||
<tspan x="263" y="2007">symbols with the adjacent text.</tspan>
|
||||
</text>
|
||||
<line x1="776" y1="1919" x2="776" y2="1933" id="Path" stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<g id="Group" transform="translate(778.4902, 1918.7324)" fill="#000000" fill-rule="nonzero">
|
||||
<path
|
||||
d="M0.8203116,14.423832 C1.3378896,14.423832 1.5917956,14.2285116 1.7773436,13.681636 L3.0371096,10.234376 L8.7988296,10.234376 L10.0585956,13.681636 C10.2441424,14.228512 10.4980496,14.423832 11.0058616,14.423832 C11.5234396,14.423832 11.8554716,14.111324 11.8554716,13.623042 C11.8554716,13.4570264 11.8261748,13.300776 11.7480498,13.095698 L7.1679698,0.898438 C6.9433598,0.302734 6.5429698,0 5.9179698,0 C5.3125018,0 4.9023458,0.292968 4.6875018,0.888672 L0.1074218,13.105472 C0.0292968,13.31055 -3.55271368e-16,13.4668 -3.55271368e-16,13.632816 C-3.55271368e-16,14.121098 0.3125,14.423832 0.820312,14.423832 L0.8203116,14.423832 Z M3.5156316,8.750004 L5.8886716,2.177744 L5.9374998,2.177744 L8.3105398,8.750004 L3.5156316,8.750004 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<line x1="792.836" y1="1919" x2="792.836" y2="1933" id="Path" stroke="#00AEEF" stroke-width="0.5">
|
||||
</line>
|
||||
<text id="Margins" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="776" y="1953">Margins</tspan>
|
||||
</text>
|
||||
<text id="Leading-and-trailing-margins-on-the-left-and-right-side-of-each-symbol" fill="#000000"
|
||||
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="776" y="1971">Leading and trailing margins on the left and right side of each symbol
|
||||
</tspan>
|
||||
</text>
|
||||
<text id="can-be-adjusted-by-modifying-the-x-location-of-the-margin-guidelines." fill="#000000"
|
||||
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="776" y="1989">can be adjusted by modifying the x-location of the margin guidelines.
|
||||
</tspan>
|
||||
</text>
|
||||
<text id="Modifications-are-automatically-applied-proportionally-to-all" fill="#000000"
|
||||
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="776" y="2007">Modifications are automatically applied proportionally to all</tspan>
|
||||
</text>
|
||||
<text id="scales-and-weights." fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="776" y="2025">scales and weights.</tspan>
|
||||
</text>
|
||||
<g id="Group" transform="translate(1291.2481, 1914.5174)" fill="#000000" fill-rule="nonzero">
|
||||
<path
|
||||
d="M0.593687825,20.3477978 L2.29290583,22.0567818 C3.15228183,22.9259218 4.13860983,22.8673278 5.06634583,21.8419378 L15.7597058,10.0548378 L14.7929098,9.07827578 L4.17766983,20.7579558 C3.82610783,21.1583458 3.49407583,21.2560018 3.02532583,20.7872526 L1.85344983,19.6251426 C1.38469983,19.1661586 1.49212183,18.8243606 1.89251223,18.4630326 L13.3671122,7.66225258 L12.3905502,6.69545658 L0.798750225,17.5841366 C-0.187577775,18.5021046 -0.265703775,19.4786686 0.593672225,20.3478166 L0.593687825,20.3477978 Z M7.00970783,2.15443778 C6.58978583,2.56459378 6.56048983,3.14076578 6.79486383,3.53139178 C7.02923983,3.89271978 7.48822383,4.12709578 8.13275383,3.96107978 C9.59759783,3.61928378 11.1210338,3.56068978 12.5468138,4.49818978 L11.9608758,5.95326778 C11.6190798,6.78334578 11.7948602,7.36928378 12.3319698,7.91615778 L14.6268898,10.2306178 C15.1151718,10.7188998 15.5253278,10.7384298 16.0917338,10.6407738 L17.1561878,10.4454614 L17.8202498,11.1192894 L17.7811874,11.6759294 C17.742125,12.1739754 17.869078,12.5548354 18.3573594,13.0333514 L19.1190774,13.7755394 C19.5975934,14.2540554 20.2128274,14.2833514 20.6815774,13.8146018 L23.5917374,10.8946818 C24.0604874,10.4259318 24.0409554,9.83022778 23.5624406,9.35171378 L22.7909566,8.58999578 C22.3124406,8.11147978 21.9413466,7.95522978 21.4628326,7.99429178 L20.8866606,8.04311998 L20.2421286,7.40835398 L20.4862686,6.28530798 C20.6132218,5.71890198 20.4569718,5.27944798 19.8710346,4.69351198 L17.6737746,2.50601198 C14.3339346,-0.814308021 9.90033463,-0.736168021 7.00971463,2.15444998 L7.00970783,2.15443778 Z M8.50384783,2.52553178 C10.9354878,0.748187779 14.2265078,1.05092178 16.4530678,3.27748578 L18.8847078,5.68958578 C19.1190838,5.92396178 19.1581458,6.10950778 19.0897858,6.45130378 L18.7675198,7.93567978 L20.2714258,9.42005578 L21.2577538,9.36146198 C21.5116598,9.35169636 21.5897858,9.3712276 21.7850978,9.56653998 L22.3612698,10.142712 L19.9198698,12.584112 L19.3436978,12.00794 C19.1483854,11.8126276 19.1190878,11.734502 19.1288538,11.47083 L19.1972132,10.494268 L17.7030732,9.00989198 L16.1796352,9.26379798 C15.8573692,9.33215738 15.7108852,9.30286038 15.4667452,9.06848558 L13.4647852,7.06652558 C13.2108792,6.83214958 13.1815812,6.66613558 13.337832,6.29504158 L14.216738,4.20520158 C12.654238,2.75012358 10.622978,2.12512158 8.59173803,2.72082558 C8.43548803,2.75988798 8.37689403,2.63293498 8.50384743,2.52551318 L8.50384783,2.52553178 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<text id="Exporting" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="1289" y="1953">Exporting</tspan>
|
||||
</text>
|
||||
<text id="Symbols-should-be-outlined-when-exporting-to-ensure-the" fill="#000000" fill-rule="nonzero"
|
||||
font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="1289" y="1971">Symbols should be outlined when exporting to ensure the</tspan>
|
||||
</text>
|
||||
<text id="design-is-preserved-when-submitting-to-Xcode." fill="#000000" fill-rule="nonzero"
|
||||
font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="1289" y="1989">design is preserved when submitting to Xcode.</tspan>
|
||||
</text>
|
||||
<text id="template-version" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2952" y="1933">Template v.5.0</tspan>
|
||||
</text>
|
||||
<text id="Requires-Xcode-15-or-greater" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||
font-size="13" font-weight="normal">
|
||||
<tspan x="2865" y="1951">Requires Xcode 15 or greater</tspan>
|
||||
</text>
|
||||
<text id="descriptive-name" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2835" y="1969">Generated from double.checkmark</tspan>
|
||||
</text>
|
||||
<text id="Typeset-at-100.0-points" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||
font-size="13" font-weight="normal">
|
||||
<tspan x="2901" y="1987">Typeset at 100.0 points</tspan>
|
||||
</text>
|
||||
<text id="Small" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="263" y="726">Small</tspan>
|
||||
</text>
|
||||
<text id="Medium" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="263" y="1156">Medium</tspan>
|
||||
</text>
|
||||
<text id="Large" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="263" y="1586">Large</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="Guides" transform="translate(263, 600.785)">
|
||||
<g id="H-reference" transform="translate(76.9937, 24.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||
<path
|
||||
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<line x1="0" y1="95.215" x2="2773" y2="95.215" id="Baseline-S" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<line x1="0" y1="24.756" x2="2773" y2="24.756" id="Capline-S" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<g id="H-reference" transform="translate(76.9937, 454.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||
<path
|
||||
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<line x1="0" y1="525.215" x2="2773" y2="525.215" id="Baseline-M" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<line x1="0" y1="454.755" x2="2773" y2="454.755" id="Capline-M" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<g id="H-reference" transform="translate(76.9937, 884.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||
<path
|
||||
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<line x1="0" y1="955.215" x2="2773" y2="955.215" id="Baseline-L" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<line x1="0" y1="884.755" x2="2773" y2="884.755" id="Capline-L" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<line x1="256.625" y1="1.13686838e-13" x2="256.625" y2="119.336" id="left-margin-Ultralight-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<line x1="348.798" y1="1.13686838e-13" x2="348.798" y2="119.336" id="right-margin-Ultralight-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<line x1="1143.53" y1="1.13686838e-13" x2="1143.53" y2="119.336" id="left-margin-Regular-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<line x1="1257.15" y1="1.13686838e-13" x2="1257.15" y2="119.336" id="right-margin-Regular-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<line x1="2622.62" y1="1.13686838e-13" x2="2622.62" y2="119.336" id="left-margin-Black-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<line x1="2760.18" y1="1.13686838e-13" x2="2760.18" y2="119.336" id="right-margin-Black-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
</g>
|
||||
<g id="Symbols" transform="translate(529.3906, 625.2969)" stroke="#000000" stroke-width="0.5">
|
||||
<g id="Black-S" transform="translate(2365.995, 0)">
|
||||
<path
|
||||
d="M67.46878,71.191381 C71.17968,71.191381 74.06058,69.873022 76.01368,66.99216 L111.07228,15.2343 C112.43948,13.2324 113.02538,11.1328 113.02538,9.2773 C113.02538,4.0039 108.82618,0 103.35738,0 C99.69528,0 97.30278,1.3183 95.05668,4.834 L67.32228,47.9492 L53.69918,32.6172 C51.79488,30.4687 49.54888,29.4433 46.52148,29.4433 C41.05278,29.4433 37,33.4472 37,38.7695 C37,41.2109 37.63478,43.1152 39.73438,45.459 L59.36328,67.67576 C61.51168,70.117162 64.14848,71.191381 67.46878,71.191381 Z"
|
||||
id="Path"></path>
|
||||
<path
|
||||
d="M9.52148,29.4433 C12.54888,29.4433 14.79488,30.4687 16.69918,32.6172 L30.32228,47.9492 L32.291,44.888 L44.484,58.915 L39.01368,66.99216 C37.1235832,69.780091 34.3645791,71.1046997 30.825305,71.1872572 L30.46878,71.191381 C27.14848,71.191381 24.51168,70.117162 22.36328,67.67576 L2.73438,45.459 C0.63478,43.1152 0,41.2109 0,38.7695 C0,33.4472 4.05278,29.4433 9.52148,29.4433 Z M66.35738,0 C71.82618,0 76.02538,4.0039 76.02538,9.2773 C76.02538,11.1328 75.43948,13.2324 74.07228,15.2343 L61.951,33.129 L49.252,18.52 L58.05668,4.834 C60.2386057,1.41874857 62.5586852,0.0771252245 66.0465687,0.00324899359 Z"
|
||||
id="Combined-Shape"></path>
|
||||
</g>
|
||||
<g id="Regular-S" transform="translate(886.905, 3.7109)">
|
||||
<path
|
||||
d="M55.87888,66.113294 C57.78318,66.113294 59.29688,65.28322 60.37108,63.62306 L95.96678,7.3242 C96.79688,6.0547 97.08988,5.0293 97.08988,4.0039 C97.08988,1.6113 95.52738,0 93.08598,0 C91.32818,0 90.35158,0.586 89.27738,2.2949 L55.68358,56.2012 L38.00778,32.3731 C36.88478,30.8594 35.81058,30.2246 34.19918,30.2246 C31.75778,30.2246 30,31.9336 30,34.375 C30,35.4004 30.43948,36.5234 31.26958,37.5977 L51.24028,63.5254 C52.60738,65.28322 53.97458,66.113294 55.87888,66.113294 Z"
|
||||
id="Path"></path>
|
||||
<path
|
||||
d="M4.19918,30.2246 C5.81058,30.2246 6.88478,30.8594 8.00778,32.3731 L25.68358,56.2012 L30.332,48.741 L35.554,55.426 L30.37108,63.62306 C29.3457073,65.2077582 27.919881,66.0361177 26.1361316,66.1081489 L25.87888,66.113294 C23.97458,66.113294 22.60738,65.28322 21.24028,63.5254 L1.26958,37.5977 C0.43948,36.5234 0,35.4004 0,34.375 C0,31.9336 1.75778,30.2246 4.19918,30.2246 Z M63.08598,0 C65.52738,0 67.08988,1.6113 67.08988,4.0039 C67.08988,5.0293 66.79688,6.0547 65.96678,7.3242 L48.893,34.328 L43.564,27.508 L59.27738,2.2949 C60.3068217,0.657204167 61.2466272,0.0507828125 62.8702602,0.00309092159 Z"
|
||||
id="Combined-Shape"></path>
|
||||
</g>
|
||||
<g id="Ultralight-S" transform="translate(0, 4.4375)">
|
||||
<path
|
||||
d="M36.79298,62.07178 C37.24418,62.07178 37.53178,61.87744 37.78868,61.53417 L76.33598,2.0112 C76.57578,1.6045 76.64168,1.3965 76.64168,1.1885 C76.64168,0.5215 76.12358,0 75.45318,0 C75.01228,0 74.71678,0.1772 74.50538,0.5693 L36.73398,58.97114 L18.24078,37.7768 C17.98048,37.3984 17.67828,37.2177 17.20218,37.2177 C16.48638,37.2177 16,37.7006 16,38.371 C16,38.6699 16.12159,38.9755 16.40678,39.2778 L35.65088,61.52733 C36.01908,61.96826 36.29638,62.07178 36.79298,62.07178 Z"
|
||||
id="Path"></path>
|
||||
<path
|
||||
d="M1.20218,37.2177 C1.67828,37.2177 1.98048,37.3984 2.24078,37.7768 L20.73398,58.97114 L23.078,55.345 L24.636,57.137 L21.78868,61.53417 C21.5603244,61.8392989 21.3077121,62.0267547 20.937503,62.0646445 L20.79298,62.07178 C20.29638,62.07178 20.01908,61.96826 19.65088,61.52733 L0.40678,39.2778 C0.12159,38.9755 0,38.6699 0,38.371 C0,37.7006 0.48638,37.2177 1.20218,37.2177 Z M59.45318,0 C60.12358,0 60.64168,0.5215 60.64168,1.1885 C60.64168,1.3965 60.57578,1.6045 60.33598,2.0112 L32.216,45.432 L30.652,43.634 L58.50538,0.5693 C58.6932911,0.220766667 58.9476516,0.0420308642 59.3115144,0.00661467764 Z"
|
||||
id="Combined-Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 24 KiB |
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
},
|
||||
"symbols": [
|
||||
{
|
||||
"filename": "checkmark.wide.svg",
|
||||
"idiom": "universal"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="3300px" height="2200px" viewBox="0 0 3300 2200" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>checkmark.wide</title>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="double.checkmark">
|
||||
<g id="Notes">
|
||||
<rect id="artboard" fill="#FFFFFF" fill-rule="nonzero" x="0" y="0" width="3300" height="2200"></rect>
|
||||
<line x1="263" y1="292" x2="3036" y2="292" id="Path" stroke="#000000" stroke-width="0.5"></line>
|
||||
<text id="Weight/Scale-Variations" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||
font-size="13" font-weight="normal">
|
||||
<tspan x="263" y="322">Weight/Scale Variations</tspan>
|
||||
</text>
|
||||
<text id="Ultralight" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="533.711" y="322">Ultralight</tspan>
|
||||
</text>
|
||||
<text id="Thin" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="843.422" y="322">Thin</tspan>
|
||||
</text>
|
||||
<text id="Light" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="1138.63" y="322">Light</tspan>
|
||||
</text>
|
||||
<text id="Regular" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="1426.84" y="322">Regular</tspan>
|
||||
</text>
|
||||
<text id="Medium" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="1723.06" y="322">Medium</tspan>
|
||||
</text>
|
||||
<text id="Semibold" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2015.77" y="322">Semibold</tspan>
|
||||
</text>
|
||||
<text id="Bold" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2326.48" y="322">Bold</tspan>
|
||||
</text>
|
||||
<text id="Heavy" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2618.19" y="322">Heavy</tspan>
|
||||
</text>
|
||||
<text id="Black" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2917.4" y="322">Black</tspan>
|
||||
</text>
|
||||
<line x1="263" y1="1903" x2="3036" y2="1903" id="Path" stroke="#000000" stroke-width="0.5"></line>
|
||||
<g id="Group" transform="translate(264.3672, 1918.0684)" fill="#000000" fill-rule="nonzero">
|
||||
<path
|
||||
d="M7.88088,15.76172 C12.18752,15.76172 15.7715,12.1875 15.7715,7.88086 C15.7715,3.57422 12.17774,0 7.8711,0 C3.57422,0 0,3.57422 0,7.88086 C0,12.1875 3.58398,15.76172 7.88086,15.76172 L7.88088,15.76172 Z M7.88088,14.277344 C4.33596,14.277344 1.50392,11.435544 1.50392,7.880864 C1.50392,4.326184 4.32618,1.484384 7.8711,1.484384 C11.42578,1.484384 14.27734,4.326184 14.27734,7.880864 C14.27734,11.435544 11.43554,14.277344 7.88086,14.277344 L7.88088,14.277344 Z M4.28712,7.880864 C4.28712,8.310552 4.589854,8.60352 5.039074,8.60352 L7.138674,8.60352 L7.138674,10.72266 C7.138674,11.162114 7.431642,11.464848 7.86133,11.464848 C8.310548,11.464848 8.603518,11.162114 8.603518,10.72266 L8.603518,8.60352 L10.722658,8.60352 C11.162112,8.60352 11.464846,8.310552 11.464846,7.880864 C11.464846,7.44141 11.162112,7.138676 10.722658,7.138676 L8.603518,7.138676 L8.603518,5.029296 C8.603518,4.580078 8.31055,4.277342 7.86133,4.277342 C7.431642,4.277342 7.138674,4.580076 7.138674,5.029296 L7.138674,7.138676 L5.039074,7.138676 C4.589856,7.138676 4.28712,7.44141 4.28712,7.880864 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<g id="Group" transform="translate(283.254, 1915.9883)" fill="#000000" fill-rule="nonzero">
|
||||
<path
|
||||
d="M9.96094,19.92188 C15.41016,19.92188 19.92188,15.4004 19.92188,9.96094 C19.92188,4.51172 15.4004,0 9.95118,0 C4.51172,0 0,4.51172 0,9.96094 C0,15.4004 4.52148,19.92188 9.96094,19.92188 Z M9.96094,18.261724 C5.35156,18.261724 1.66992,14.570324 1.66992,9.960944 C1.66992,5.351564 5.3418,1.660164 9.95116,1.660164 C14.56052,1.660164 18.2617,5.351564 18.2617,9.960944 C18.2617,14.570324 14.5703,18.261724 9.96092,18.261724 L9.96094,18.261724 Z M5.4297,9.960944 C5.4297,10.43946 5.761732,10.761726 6.259778,10.761726 L9.130878,10.761726 L9.130878,13.642586 C9.130878,14.130868 9.46291,14.472664 9.941424,14.472664 C10.4297,14.472664 10.771502,14.140632 10.771502,13.642586 L10.771502,10.761726 L13.652362,10.761726 C14.140644,10.761726 14.48244,10.43946 14.48244,9.960944 C14.48244,9.472662 14.140644,9.130866 13.652362,9.130866 L10.771502,9.130866 L10.771502,6.259766 C10.771502,5.76172 10.4297,5.419922 9.941424,5.419922 C9.462908,5.419922 9.130878,5.761718 9.130878,6.259766 L9.130878,9.130866 L6.259778,9.130866 C5.761732,9.130866 5.4297,9.472662 5.4297,9.960944 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<g id="Group" transform="translate(307.1798, 1913.2246)" fill="#000000" fill-rule="nonzero">
|
||||
<path
|
||||
d="M12.71486,25.43944 C19.67776,25.43944 25.43946,19.67772 25.43946,12.7246 C25.43946,5.7617 19.66798,0 12.70508,0 C5.75196,0 -1.42108547e-15,5.76172 -1.42108547e-15,12.7246 C-1.42108547e-15,19.67772 5.76172,25.43944 12.71484,25.43944 L12.71486,25.43944 Z M12.71486,23.623034 C6.6797,23.623034 1.82618,18.759754 1.82618,12.724594 C1.82618,6.679674 6.66994,1.826154 12.70508,1.826154 C18.75,1.826154 23.61328,6.679674 23.61328,12.724594 C23.61328,18.759754 18.75976,23.623034 12.71484,23.623034 L12.71486,23.623034 Z M6.94338,12.724594 C6.94338,13.242172 7.314474,13.6035 7.861348,13.6035 L11.806668,13.6035 L11.806668,17.55858 C11.806668,18.09569 12.177762,18.476548 12.69534,18.476548 C13.23245,18.476548 13.603544,18.105454 13.603544,17.55858 L13.603544,13.6035 L17.558624,13.6035 C18.095734,13.6035 18.476592,13.242172 18.476592,12.724594 C18.476592,12.177718 18.105498,11.806626 17.558624,11.806626 L13.603544,11.806626 L13.603544,7.861306 C13.603544,7.31443 13.23245,6.933572 12.69534,6.933572 C12.177762,6.933572 11.806668,7.314432 11.806668,7.861306 L11.806668,11.806626 L7.861348,11.806626 C7.314472,11.806626 6.94338,12.17772 6.94338,12.724594 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<text id="Design-Variations" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="263" y="1953">Design Variations</tspan>
|
||||
</text>
|
||||
<text id="Symbols-are-supported-in-up-to-nine-weights-and-three-scales." fill="#000000"
|
||||
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="263" y="1971">Symbols are supported in up to nine weights and three scales.</tspan>
|
||||
</text>
|
||||
<text id="For-optimal-layout-with-text-and-other-symbols,-vertically-align" fill="#000000"
|
||||
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="263" y="1989">For optimal layout with text and other symbols, vertically align</tspan>
|
||||
</text>
|
||||
<text id="symbols-with-the-adjacent-text." fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||
font-size="13" font-weight="normal">
|
||||
<tspan x="263" y="2007">symbols with the adjacent text.</tspan>
|
||||
</text>
|
||||
<line x1="776" y1="1919" x2="776" y2="1933" id="Path" stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<g id="Group" transform="translate(778.4902, 1918.7324)" fill="#000000" fill-rule="nonzero">
|
||||
<path
|
||||
d="M0.8203116,14.423832 C1.3378896,14.423832 1.5917956,14.2285116 1.7773436,13.681636 L3.0371096,10.234376 L8.7988296,10.234376 L10.0585956,13.681636 C10.2441424,14.228512 10.4980496,14.423832 11.0058616,14.423832 C11.5234396,14.423832 11.8554716,14.111324 11.8554716,13.623042 C11.8554716,13.4570264 11.8261748,13.300776 11.7480498,13.095698 L7.1679698,0.898438 C6.9433598,0.302734 6.5429698,0 5.9179698,0 C5.3125018,0 4.9023458,0.292968 4.6875018,0.888672 L0.1074218,13.105472 C0.0292968,13.31055 -3.55271368e-16,13.4668 -3.55271368e-16,13.632816 C-3.55271368e-16,14.121098 0.3125,14.423832 0.820312,14.423832 L0.8203116,14.423832 Z M3.5156316,8.750004 L5.8886716,2.177744 L5.9374998,2.177744 L8.3105398,8.750004 L3.5156316,8.750004 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<line x1="792.836" y1="1919" x2="792.836" y2="1933" id="Path" stroke="#00AEEF" stroke-width="0.5">
|
||||
</line>
|
||||
<text id="Margins" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="776" y="1953">Margins</tspan>
|
||||
</text>
|
||||
<text id="Leading-and-trailing-margins-on-the-left-and-right-side-of-each-symbol" fill="#000000"
|
||||
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="776" y="1971">Leading and trailing margins on the left and right side of each symbol
|
||||
</tspan>
|
||||
</text>
|
||||
<text id="can-be-adjusted-by-modifying-the-x-location-of-the-margin-guidelines." fill="#000000"
|
||||
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="776" y="1989">can be adjusted by modifying the x-location of the margin guidelines.
|
||||
</tspan>
|
||||
</text>
|
||||
<text id="Modifications-are-automatically-applied-proportionally-to-all" fill="#000000"
|
||||
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="776" y="2007">Modifications are automatically applied proportionally to all</tspan>
|
||||
</text>
|
||||
<text id="scales-and-weights." fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="776" y="2025">scales and weights.</tspan>
|
||||
</text>
|
||||
<g id="Group" transform="translate(1291.2481, 1914.5174)" fill="#000000" fill-rule="nonzero">
|
||||
<path
|
||||
d="M0.593687825,20.3477978 L2.29290583,22.0567818 C3.15228183,22.9259218 4.13860983,22.8673278 5.06634583,21.8419378 L15.7597058,10.0548378 L14.7929098,9.07827578 L4.17766983,20.7579558 C3.82610783,21.1583458 3.49407583,21.2560018 3.02532583,20.7872526 L1.85344983,19.6251426 C1.38469983,19.1661586 1.49212183,18.8243606 1.89251223,18.4630326 L13.3671122,7.66225258 L12.3905502,6.69545658 L0.798750225,17.5841366 C-0.187577775,18.5021046 -0.265703775,19.4786686 0.593672225,20.3478166 L0.593687825,20.3477978 Z M7.00970783,2.15443778 C6.58978583,2.56459378 6.56048983,3.14076578 6.79486383,3.53139178 C7.02923983,3.89271978 7.48822383,4.12709578 8.13275383,3.96107978 C9.59759783,3.61928378 11.1210338,3.56068978 12.5468138,4.49818978 L11.9608758,5.95326778 C11.6190798,6.78334578 11.7948602,7.36928378 12.3319698,7.91615778 L14.6268898,10.2306178 C15.1151718,10.7188998 15.5253278,10.7384298 16.0917338,10.6407738 L17.1561878,10.4454614 L17.8202498,11.1192894 L17.7811874,11.6759294 C17.742125,12.1739754 17.869078,12.5548354 18.3573594,13.0333514 L19.1190774,13.7755394 C19.5975934,14.2540554 20.2128274,14.2833514 20.6815774,13.8146018 L23.5917374,10.8946818 C24.0604874,10.4259318 24.0409554,9.83022778 23.5624406,9.35171378 L22.7909566,8.58999578 C22.3124406,8.11147978 21.9413466,7.95522978 21.4628326,7.99429178 L20.8866606,8.04311998 L20.2421286,7.40835398 L20.4862686,6.28530798 C20.6132218,5.71890198 20.4569718,5.27944798 19.8710346,4.69351198 L17.6737746,2.50601198 C14.3339346,-0.814308021 9.90033463,-0.736168021 7.00971463,2.15444998 L7.00970783,2.15443778 Z M8.50384783,2.52553178 C10.9354878,0.748187779 14.2265078,1.05092178 16.4530678,3.27748578 L18.8847078,5.68958578 C19.1190838,5.92396178 19.1581458,6.10950778 19.0897858,6.45130378 L18.7675198,7.93567978 L20.2714258,9.42005578 L21.2577538,9.36146198 C21.5116598,9.35169636 21.5897858,9.3712276 21.7850978,9.56653998 L22.3612698,10.142712 L19.9198698,12.584112 L19.3436978,12.00794 C19.1483854,11.8126276 19.1190878,11.734502 19.1288538,11.47083 L19.1972132,10.494268 L17.7030732,9.00989198 L16.1796352,9.26379798 C15.8573692,9.33215738 15.7108852,9.30286038 15.4667452,9.06848558 L13.4647852,7.06652558 C13.2108792,6.83214958 13.1815812,6.66613558 13.337832,6.29504158 L14.216738,4.20520158 C12.654238,2.75012358 10.622978,2.12512158 8.59173803,2.72082558 C8.43548803,2.75988798 8.37689403,2.63293498 8.50384743,2.52551318 L8.50384783,2.52553178 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<text id="Exporting" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="1289" y="1953">Exporting</tspan>
|
||||
</text>
|
||||
<text id="Symbols-should-be-outlined-when-exporting-to-ensure-the" fill="#000000" fill-rule="nonzero"
|
||||
font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="1289" y="1971">Symbols should be outlined when exporting to ensure the</tspan>
|
||||
</text>
|
||||
<text id="design-is-preserved-when-submitting-to-Xcode." fill="#000000" fill-rule="nonzero"
|
||||
font-family="Helvetica" font-size="13" font-weight="normal">
|
||||
<tspan x="1289" y="1989">design is preserved when submitting to Xcode.</tspan>
|
||||
</text>
|
||||
<text id="template-version" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2952" y="1933">Template v.5.0</tspan>
|
||||
</text>
|
||||
<text id="Requires-Xcode-15-or-greater" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||
font-size="13" font-weight="normal">
|
||||
<tspan x="2865" y="1951">Requires Xcode 15 or greater</tspan>
|
||||
</text>
|
||||
<text id="descriptive-name" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="2835" y="1969">Generated from double.checkmark</tspan>
|
||||
</text>
|
||||
<text id="Typeset-at-100.0-points" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||
font-size="13" font-weight="normal">
|
||||
<tspan x="2901" y="1987">Typeset at 100.0 points</tspan>
|
||||
</text>
|
||||
<text id="Small" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="263" y="726">Small</tspan>
|
||||
</text>
|
||||
<text id="Medium" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="263" y="1156">Medium</tspan>
|
||||
</text>
|
||||
<text id="Large" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||
font-weight="normal">
|
||||
<tspan x="263" y="1586">Large</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="Guides" transform="translate(263, 600.785)">
|
||||
<g id="H-reference" transform="translate(76.9937, 24.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||
<path
|
||||
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<line x1="0" y1="95.215" x2="2773" y2="95.215" id="Baseline-S" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<line x1="0" y1="24.756" x2="2773" y2="24.756" id="Capline-S" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<g id="H-reference" transform="translate(76.9937, 454.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||
<path
|
||||
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<line x1="0" y1="525.215" x2="2773" y2="525.215" id="Baseline-M" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<line x1="0" y1="454.755" x2="2773" y2="454.755" id="Capline-M" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<g id="H-reference" transform="translate(76.9937, 884.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||
<path
|
||||
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||
id="Shape"></path>
|
||||
</g>
|
||||
<line x1="0" y1="955.215" x2="2773" y2="955.215" id="Baseline-L" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<line x1="0" y1="884.755" x2="2773" y2="884.755" id="Capline-L" stroke="#27AAE1" stroke-width="0.5">
|
||||
</line>
|
||||
<line x1="256.625" y1="1.13686838e-13" x2="256.625" y2="119.336" id="left-margin-Ultralight-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<line x1="348.798" y1="1.13686838e-13" x2="348.798" y2="119.336" id="right-margin-Ultralight-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<line x1="1143.53" y1="1.13686838e-13" x2="1143.53" y2="119.336" id="left-margin-Regular-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<line x1="1257.15" y1="1.13686838e-13" x2="1257.15" y2="119.336" id="right-margin-Regular-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<line x1="2622.62" y1="1.13686838e-13" x2="2622.62" y2="119.336" id="left-margin-Black-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
<line x1="2760.18" y1="1.13686838e-13" x2="2760.18" y2="119.336" id="right-margin-Black-S"
|
||||
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||
</g>
|
||||
<g id="Symbols" transform="translate(529.3906, 625.2969)" stroke="#000000" stroke-width="0.5">
|
||||
<g id="Black-S" transform="translate(2365.995, 0)">
|
||||
<path
|
||||
d="M30.46878,71.191381 C34.17968,71.191381 37.06058,69.873022 39.01368,66.99216 L74.07228,15.2343 C75.43948,13.2324 76.02538,11.1328 76.02538,9.2773 C76.02538,4.0039 71.82618,0 66.35738,0 C62.69528,0 60.30278,1.3183 58.05668,4.834 L30.32228,47.9492 L16.69918,32.6172 C14.79488,30.4687 12.54888,29.4433 9.52148,29.4433 C4.05278,29.4433 0,33.4472 0,38.7695 C0,41.2109 0.63478,43.1152 2.73438,45.459 L22.36328,67.67576 C24.51168,70.117162 27.14848,71.191381 30.46878,71.191381 Z"
|
||||
id="Path"></path>
|
||||
</g>
|
||||
<g id="Regular-S" transform="translate(886.905, 3.7109)">
|
||||
<path
|
||||
d="M25.87888,66.113294 C27.78318,66.113294 29.29688,65.28322 30.37108,63.62306 L65.96678,7.3242 C66.79688,6.0547 67.08988,5.0293 67.08988,4.0039 C67.08988,1.6113 65.52738,0 63.08598,0 C61.32818,0 60.35158,0.586 59.27738,2.2949 L25.68358,56.2012 L8.00778,32.3731 C6.88478,30.8594 5.81058,30.2246 4.19918,30.2246 C1.75778,30.2246 0,31.9336 0,34.375 C0,35.4004 0.43948,36.5234 1.26958,37.5977 L21.24028,63.5254 C22.60738,65.28322 23.97458,66.113294 25.87888,66.113294 Z"
|
||||
id="Path"></path>
|
||||
</g>
|
||||
<g id="Ultralight-S" transform="translate(0, 4.4375)">
|
||||
<path
|
||||
d="M20.79298,62.07178 C21.24418,62.07178 21.53178,61.87744 21.78868,61.53417 L60.33598,2.0112 C60.57578,1.6045 60.64168,1.3965 60.64168,1.1885 C60.64168,0.5215 60.12358,0 59.45318,0 C59.01228,0 58.71678,0.1772 58.50538,0.5693 L20.73398,58.97114 L2.24078,37.7768 C1.98048,37.3984 1.67828,37.2177 1.20218,37.2177 C0.48638,37.2177 0,37.7006 0,38.371 C0,38.6699 0.12159,38.9755 0.40678,39.2778 L19.65088,61.52733 C20.01908,61.96826 20.29638,62.07178 20.79298,62.07178 Z"
|
||||
id="Path"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 22 KiB |
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "decentralized_light.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "decentralized_light@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "decentralized_light@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 18 KiB |
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "decentralized.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "decentralized@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "decentralized@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 19 KiB |
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Flux_logo_blue_white.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 33 KiB |
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Flux_logo_blue.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 34 KiB |
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Flux_symbol_blue-white.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 17 KiB |
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "github_1x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "github_2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "github_3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 5.8 KiB |
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "github_light_1x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "github_light_2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "github_light_3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 3.5 KiB |
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "60.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "180.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon-light.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-light@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "icon-light@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9 KiB |