A battle-tested, reproducible guide to deploy your own closed-system, high-security SimpleX messaging infrastructure on a Raspberry Pi using Tor v3 hidden services.
Version: 0.7.3-alpha (22. December 2025)
Tested on: Raspberry Pi 4 (4GB), Raspberry Pi OS Lite 64-bit (Bookworm)
SimpleX Version: 6.4.5.1
⚠️ STABILITY WARNING - Multi-SMP ConfigurationRunning 10 SMP servers (Appendix A) is currently experimental and may experience stability issues. Recommendation: Start with 1-3 SMP servers for production use. We are actively working on improving Multi-SMP stability.
✅ Dual Tor Architecture is now part of the Core Setup (Section 4).
⚠️ IMPORTANT: Tor Connectivity FluctuationsYou may experience temporary connectivity issues with your Tor hidden services. This is normal behavior due to:
- Tor circuit rebuilding (circuits rotate every ~10 minutes)
- Network congestion on the Tor network
- Guard node changes
The Tor Watchdog automatically handles recovery. For persistent issues, verify your Dual Tor setup (Section 4) and see Appendix F for troubleshooting.
We're actively working on improving stability. Running 12 Tor Hidden Services on a single device can occasionally experience connectivity fluctuations due to Tor network conditions (bad guard nodes, circuit timeouts).
Mitigation: We've added Appendix E: Tor Watchdog which automatically monitors and restarts Tor when hidden services become unreachable. This significantly improves uptime.
Symptoms: Clients showing 0% or partial server connectivity, messages not arriving intermittently.
We're investigating: Vanguards integration, guard node optimization, and circuit stability improvements for future releases.
This guide provides a complete, self-hosted messaging infrastructure for closed user groups – families, organizations, teams, or any group requiring maximum privacy and operational security.
Unlike using public SimpleX infrastructure, this setup creates an isolated, invisible messaging network where:
- Zero clearnet exposure – All traffic routes exclusively through Tor v3 onion services
- No public server listing – Your .onion addresses exist nowhere on the internet
- Closed user group – Only people you explicitly invite can communicate
- Zero metadata leakage – No third-party servers see your traffic patterns
- Full infrastructure control – You own and operate every component
When using public SimpleX servers (default preset servers or Flux servers), your messaging infrastructure is:
┌─────────────────────────────────────────────────────────────────────┐
│ PUBLIC SIMPLEX INFRASTRUCTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ smp11.simplex.im ◄──── Publicly known address │
│ smp12.simplex.im ◄──── Can be monitored by adversaries │
│ smp14.simplex.im ◄──── Traffic patterns observable │
│ flux-smp-*.runonflux.io ◄── Third-party operated │
│ │
│ ⚠️ Adversary knows WHERE to look │
│ ⚠️ Server operators can see connection metadata │
│ ⚠️ Hosting providers log IP addresses │
│ │
└─────────────────────────────────────────────────────────────────────┘
Even with SimpleX's excellent encryption and privacy features, using public servers means:
- Adversaries know which servers to monitor
- Server operators (even trusted ones) can see connection patterns
- Hosting providers may log transport metadata
- Your traffic is mixed with millions of other users (which can be good or bad)
This guide creates a completely isolated infrastructure:
┌─────────────────────────────────────────────────────────────────────┐
│ YOUR CLOSED INFRASTRUCTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ abc123...xyz.onion ◄──── Address known only to your group │
│ def456...xyz.onion ◄──── Not listed anywhere on the internet │
│ ghi789...xyz.onion ◄──── Cannot be discovered by scanning │
│ ... (10 servers) ◄──── You control ALL infrastructure │
│ │
│ ✅ Adversary doesn't know WHERE to look │
│ ✅ YOU are the only operator │
│ ✅ No third-party sees any metadata │
│ ✅ Traffic patterns invisible to outside observers │
│ │
└─────────────────────────────────────────────────────────────────────┘
The fundamental security advantage: An adversary must first discover that your infrastructure exists before they can even attempt to monitor it. With Tor v3 hidden services, this is computationally infeasible.
In your SimpleX client, disable ALL public servers:
Settings → Network & Servers → SMP Servers:
☐ SimpleX Chat servers (DISABLED)
☐ Flux servers (DISABLED)
☑ Your .onion servers (ENABLED + "Use for new connections" ✓)
Settings → Network & Servers → XFTP Servers:
☐ SimpleX Chat servers (DISABLED)
☐ Flux servers (DISABLED)
☑ Your .onion server (ENABLED + "Use for new connections" ✓)
Result:
- All communication stays within your closed infrastructure
- No metadata leaks to any third party
- Only members of your group can communicate
- Maximum operational security
Limitation: You cannot communicate with users outside your group.
If you need to communicate with external SimpleX users:
Settings → Network & Servers → SMP Servers:
☑ SimpleX Chat servers (ENABLED, but "Use for new connections" ✗)
☐ Flux servers (DISABLED)
☑ Your .onion servers (ENABLED + "Use for new connections" ✓)
How this works:
- New connections you create use only your servers (your queues stay private)
- External users can reach you via public servers (you add their server as "known")
- Your infrastructure remains hidden, but you can communicate outside
Trade-off:
- ✅ You can message anyone on SimpleX network
⚠️ Public servers see some of your connection metadata⚠️ Traffic to external contacts is partially visible
| Feature | Closed Mode | Hybrid Mode |
|---|---|---|
| External communication | ❌ Only your group | ✅ Anyone on SimpleX |
| Infrastructure visibility | ✅ Completely hidden | |
| Metadata to third parties | ✅ Zero | |
| Recommended for | High-security groups | Mixed use cases |
This infrastructure is designed for closed user groups requiring maximum operational security:
| Use Case | Why Closed Infrastructure? |
|---|---|
| Journalist Networks | Protect sources, editorial communications invisible to adversaries |
| Whistleblower Systems | Tip submission infrastructure that cannot be discovered |
| Activist Cells | Organize without exposing existence of communication network |
| Legal Teams | Attorney-client privilege with infrastructure you control |
| Medical Teams | Patient data on systems with zero third-party access |
| Family Privacy | Private communication without Big Tech or government visibility |
| Research Groups | Protect subjects and collaboration from surveillance |
| Corporate Security | M&A, trade secrets on infrastructure competitors cannot find |
| Government/Military | Classified communications on controlled infrastructure |
This guide is overkill if you:
- Just want better privacy than WhatsApp (use SimpleX with default servers)
- Need to communicate with many external people (use hybrid mode at minimum)
- Don't have time to maintain infrastructure (use managed services)
- Trust SimpleX/Flux operators (their servers are well-run)
This guide uses a Raspberry Pi as a low-budget, high-security reference platform:
| Advantage | Description |
|---|---|
| Cost | ~€50-80 total hardware investment |
| Power | 5-15W consumption, runs 24/7 for pennies |
| Stealth | Small form factor, easy to conceal or relocate |
| Air-Gap Ready | Can operate completely isolated from main networks |
| Reproducible | Identical setup across multiple locations |
| Disposable | SD card can be destroyed; device is replaceable |
| Plausible Deniability | "It's just a media server" |
Note: This architecture scales to VPS, dedicated servers, or Kubernetes clusters. The Raspberry Pi version proves the concept at minimal cost before larger deployments.
┌─────────────────────────────────────────────────────────────────┐
│ YOUR RASPBERRY PI │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 10 × SMP SERVERS │ │
│ │ (Private Message Routing Pool) │ │
│ │ │ │
│ │ Each server = unique .onion address │ │
│ │ Addresses known ONLY to your group │ │
│ │ Traffic distributed across all instances │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 1 × XFTP SERVER │ │
│ │ (Encrypted File Transfer) │ │
│ │ │ │
│ │ 20GB storage, files auto-expire │ │
│ │ Chunked, encrypted uploads/downloads │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ DUAL TOR ARCHITECTURE │ │
│ │ (12 Hidden Services + SOCKS Proxy) │ │
│ │ │ │
│ │ tor@default: Hidden Services only (SOCKSPort 0) │ │
│ │ tor@tor2: SOCKS Proxy only (SocksPort 9050) │ │
│ │ Auto-recovery via Tor Watchdog │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────┐
│ TOR NETWORK │
│ (Your traffic │
│ hidden among │
│ all Tor users)│
└─────────────────┘
│
▼
┌─────────────────┐
│ YOUR CLIENTS │
│ (Only people │
│ you invited) │
└─────────────────┘
| Layer | Protection |
|---|---|
| Discovery | Tor v3 onion = address cannot be found without knowing it |
| Transport | Tor hidden services (ed25519, no exit nodes) |
| Application | SimpleX double-ratchet E2EE + post-quantum resistance |
| Metadata | Private routing, traffic mixing, no user IDs |
| Infrastructure | Self-hosted, YOU are the only operator |
| Network | Firewall blocks all clearnet access to services |
| Availability | Tor Watchdog auto-recovers from connectivity issues |
| Isolation | Dual Tor Architecture separates Hidden Services from SOCKS |
Traditional attacks on messaging require knowing where to look:
| Attack Type | Public Servers | Your Closed Infrastructure |
|---|---|---|
| Traffic Analysis | ✅ Possible (known endpoints) | ❌ Must find endpoints first |
| Server Compromise | ✅ Target known servers | ❌ Which .onion to target? |
| Legal Requests | ✅ Subpoena known operators | ❌ Operator unknown |
| Network Monitoring | ✅ Monitor known IPs | ❌ No IPs, only Tor traffic |
The security advantage is not just encryption – it's invisibility.
Transparency is security. This section documents what this setup protects against – and what it doesn't.
| Threat Actor | Protection Level | How It Works |
|---|---|---|
| Mass surveillance | ✅ Strong | Your infrastructure doesn't exist in any database |
| Targeted server monitoring | ✅ Strong | Adversary must discover your .onion addresses first |
| ISP / Network observer | ✅ Strong | All traffic is Tor-encrypted; destination hidden |
| Third-party metadata | ✅ Strong | No SimpleX/Flux servers see your patterns |
| Server operator (SimpleX Ltd.) | ✅ Strong | You ARE the operator; they see nothing |
| Traffic correlation (external) | ✅ Strong | 10 servers = 10 separate Tor circuits |
| Threat Actor | Protection Level | Why |
|---|---|---|
| Physical device access | ❌ None | One device = one point of compromise |
| Root compromise of Pi | ❌ None | Attacker sees all 10 server instances |
| You (the operator) | ❌ None | You control all servers |
| Insider threat | Group member can leak .onion addresses | |
| Nation-state with Tor visibility | Long-term traffic analysis possible | |
| Endpoint compromise | ❌ None | Client device hacked = game over |
| Tor network instability | Watchdog helps, but outages possible |
All 10 SMP servers run on one device under one operator (you). This means:
Advantages:
- Simpler administration
- Lower cost
- Full control
Limitations:
- No protection from yourself (you can correlate everything)
- Single point of physical failure
- If compromised, ALL servers are compromised
For higher security, consider:
- Distributing servers across multiple physical locations
- Having different trusted people operate different servers
- Geographic distribution across jurisdictions
| Threat Level | Recommended Setup | Example Users |
|---|---|---|
| Standard | Closed Mode, single Pi | Families, small teams |
| Elevated | Closed Mode + Appendix B-F | NGOs, journalists |
| High | Multi-location + different operators | Activists in hostile states |
| Extreme | Air-gapped + multi-jurisdiction + burner devices | Conflict zones, whistleblowers |
- Base setup
- Install dependencies
- Create SimpleX service user
- Install Tor
- Configure Tor hidden services
- Install SimpleX binaries
- Initialize SMP server
- Initialize XFTP server
- Create systemd services
- Firewall (nftables)
- Verify everything
- Client setup
- Troubleshooting
- Appendix A: Multi-SMP for Private Message Routing
- Appendix B: SSH over Tor
- Appendix C: Tor v3 Client Authorization (coming soon)
- Appendix D: Vanguards (coming soon)
- Appendix E: Tor Watchdog ⭐ Recommended!
- Appendix F: Dual Tor Architecture (Reference & Troubleshooting)
Flash Raspberry Pi OS Lite (64-bit) and boot:
sudo apt update && sudo apt full-upgrade -y
sudo rebootCRITICAL: This includes
binutils-goldwhich is required for the Haskell build (Option B).
sudo apt update
sudo apt install -y \
curl wget gnupg2 lsb-release ca-certificates \
git build-essential pkg-config \
libssl-dev zlib1g-dev liblzma-dev libbz2-dev libgmp-dev \
libffi-dev libncurses-dev \
jq bc netcat-openbsd \
nftables torsocks \
binutils binutils-gold
# Verify gold linker is available (needed for Option B)
command -v ld.gold && ld.gold -vWhy binutils-gold? GHC/Cabal uses
-fuse-ld=goldfor some packages. Without it, you get crypticcannot find 'ld'errors during the build.
# Create service user (no login shell)
sudo useradd -r -s /usr/sbin/nologin -m -d /var/opt/simplex simplex
# Create directories
sudo mkdir -p /etc/opt/simplex
sudo mkdir -p /etc/opt/simplex-xftp
sudo mkdir -p /var/opt/simplex
sudo mkdir -p /var/opt/simplex-xftp
sudo mkdir -p /var/opt/simplex-xftp/files
# Set ownership BEFORE initialization
sudo chown -R simplex:simplex /etc/opt/simplex
sudo chown -R simplex:simplex /etc/opt/simplex-xftp
sudo chown -R simplex:simplex /var/opt/simplex
sudo chown -R simplex:simplex /var/opt/simplex-xftp
# Set permissions (simplex user must be able to enter directories)
sudo chmod 750 /etc/opt/simplex /etc/opt/simplex-xftp
sudo chmod 750 /var/opt/simplex /var/opt/simplex-xftp
sudo chmod 750 /var/opt/simplex-xftp/files
⚠️ CRITICAL: This setup uses TWO Tor instances!
tor@default→ Hidden Services only (no SOCKS)tor@tor2→ SOCKS Proxy only (for Private Routing)This separation is required for stable Private Message Routing. See Appendix F for background and troubleshooting.
# Add Tor Project repository
curl -fsSL https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/tor-archive-keyring.gpg
CODENAME="$(. /etc/os-release && echo "$VERSION_CODENAME")"
echo "deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org ${CODENAME} main" \
| sudo tee /etc/apt/sources.list.d/tor.list
sudo apt update
sudo apt install -y tor deb.torproject.org-keyring# Create the second Tor instance for SOCKS proxy
sudo tor-instance-create tor2
# Configure tor@tor2 for SOCKS only
sudo tee /etc/tor/instances/tor2/torrc > /dev/null <<'EOF'
# SOCKS Proxy for SMP Private Routing
# This instance handles ONLY outbound SOCKS connections
Log notice syslog
SocksPort 9050
EOF# Enable both Tor instances
sudo systemctl enable tor@default
sudo systemctl enable tor@tor2
# Start both Tor instances
sudo systemctl start tor@default
sudo systemctl start tor@tor2
# Verify both are running
sudo systemctl is-active tor@default tor@tor2Expected output:
active
active
# Check tor@tor2 is providing SOCKS on port 9050
sudo ss -lntp | grep 9050
# Test SOCKS proxy functionality
curl -x socks5h://127.0.0.1:9050 -s https://check.torproject.org/api/ip | jqExpected output for ss:
LISTEN 127.0.0.1:9050 users:(("tor",pid=...,fd=...))
💡 Why Two Tor Instances? Running Hidden Services AND SOCKS proxy in a single Tor instance causes resource conflicts, resulting in
SocksErrorGeneralServerFailureerrors. By separating them, each instance can dedicate resources to its specific task.
5. Configure Tor hidden services
Now configure tor@default for Hidden Services only (SOCKS is handled by tor@tor2):
sudo nano /etc/tor/torrcReplace the entire file with:
# === tor@default Configuration ===
# This instance handles ONLY Hidden Services
# SOCKS proxy is handled by tor@tor2 (see Section 4)
# CRITICAL: Disable SOCKS in this instance!
# tor@tor2 provides SOCKS on port 9050
SOCKSPort 0
# === SimpleX SMP ===
HiddenServiceDir /var/lib/tor/simplex-smp/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5223
# === SimpleX XFTP ===
HiddenServiceDir /var/lib/tor/simplex-xftp/
HiddenServiceVersion 3
HiddenServicePort 443 127.0.0.1:443Why
SOCKSPort 0? This disables SOCKS intor@default, dedicating it exclusively to Hidden Services. The SOCKS proxy for Private Routing runs ontor@tor2(configured in Section 4).
Port architecture:
- SMP listens on 5223 only (avoids privileged port issues)
- XFTP listens on 443 (with capability fix in systemd)
- Each service has its own onion address, so no port conflicts
Restart Tor and get your onion addresses:
sudo systemctl restart tor@default
sleep 5
# Save these addresses!
SMP_ONION=$(sudo cat /var/lib/tor/simplex-smp/hostname)
XFTP_ONION=$(sudo cat /var/lib/tor/simplex-xftp/hostname)
echo "SMP: $SMP_ONION"
echo "XFTP: $XFTP_ONION"These .onion addresses are the only way to reach your infrastructure. They should be:
- Shared only with members of your closed group
- Stored in encrypted form (password manager, encrypted note)
- Never posted publicly or sent over insecure channels
You have two options to get the SimpleX server binaries:
| Option | Time | Skill Level | Recommended For |
|---|---|---|---|
| A) Pre-built binaries | ~2 minutes | Beginner | Most users |
| B) Build from source | ~60 minutes | Intermediate | Security auditors, custom builds |
Download verified ARM64 binaries from our GitHub releases:
# Download binaries
wget https://github.com/cannatoshi/simplex-smp-xftp-via-tor-on-rpi-hardened/releases/download/v0.7.3-alpha/smp-server
wget https://github.com/cannatoshi/simplex-smp-xftp-via-tor-on-rpi-hardened/releases/download/v0.7.3-alpha/xftp-server
wget https://github.com/cannatoshi/simplex-smp-xftp-via-tor-on-rpi-hardened/releases/download/v0.7.3-alpha/SHA256SUMS
# Verify checksums (IMPORTANT!)
sha256sum -c SHA256SUMSExpected output:
smp-server: OK
xftp-server: OK
⚠️ If verification fails, DO NOT USE the binaries! Re-download or build from source.
Install the binaries:
# Install to system path
sudo install -m 0755 smp-server /usr/local/bin/smp-server
sudo install -m 0755 xftp-server /usr/local/bin/xftp-server
# Verify installation
smp-server -v
xftp-server -vExpected output: SMP server v6.4.5.1 and XFTP server v6.4.5.1
Done! Skip to Section 7: Initialize SMP server.
For those who want to verify the source code or make custom modifications.
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | shAnswers when prompted:
- Base channel:
D - Pre-releases:
N - Add to PATH:
P - Install HLS:
N - Stack integration:
Y
Load environment:
source ~/.ghcup/envghcup install ghc 9.6.3
ghcup set ghc 9.6.3
ghcup install cabal 3.10.3.0
ghcup set cabal 3.10.3.0
# Verify
ghc --version
cabal --versionIgnore warnings about newer versions. We use tested versions.
cd ~
git clone https://github.com/simplex-chat/simplexmq.git
cd simplexmq
git fetch --tags
LATEST_TAG=$(git describe --tags --abbrev=0)
echo "Building: $LATEST_TAG"
git checkout "$LATEST_TAG"
cabal update
cabal build exe:smp-server exe:xftp-serverThis takes 45-60 minutes on a Pi 4. Go get coffee. ☕
sudo install -m 0755 "$(cabal list-bin exe:smp-server)" /usr/local/bin/smp-server
sudo install -m 0755 "$(cabal list-bin exe:xftp-server)" /usr/local/bin/xftp-server
# Verify
smp-server -v
xftp-server -vCRITICAL: Run as
simplexuser, not root!
SMP_ONION=$(sudo cat /var/lib/tor/simplex-smp/hostname)
sudo -u simplex smp-server init -n "$SMP_ONION"
⚠️ IMPORTANT: The SOCKS proxy configuration for Private Message Routing MUST be in the[PROXY]section, NOT in[TRANSPORT]! This is a common mistake that breaks Private Routing.
sudo nano /etc/opt/simplex/smp-server.iniRequired changes in [TRANSPORT] section:
[TRANSPORT]
host: <your-smp-onion>.onion
port: 5223
log_tls_errors: offRequired changes in [PROXY] section (CRITICAL for Private Routing!):
Find the [PROXY] section and configure:
[PROXY]
# SOCKS proxy for forwarding messages to destination servers
# MUST use IP address, not "localhost"!
# Points to tor@tor2 (NOT tor@default!)
socks_proxy: 127.0.0.1:9050
# For Tor-only .onion servers, use 'onion' mode
# 'onion' = SOCKS only for .onion destinations (RECOMMENDED!)
# 'always' = SOCKS for all destinations (NOT recommended - causes instability!)
socks_mode: onion
⚠️ CRITICAL Configuration:
socks_proxy: 127.0.0.1:9050→ Points to tor@tor2 (NOT tor@default!)socks_mode: onion→ Use SOCKS only for .onion addressesDO NOT use
socks_mode: always! This forces ALL traffic through SOCKS, causing instability when Tor circuits rebuild.
Disable HTTPS web server (find [WEB] section):
[WEB]
static_path: /var/opt/simplex/www
# https: 443
# cert: /etc/opt/simplex/web.crt
# key: /etc/opt/simplex/web.keyOr use these sed commands to automate:
# Set correct port
sudo sed -i 's/^port: .*/port: 5223/' /etc/opt/simplex/smp-server.ini
# Disable HTTPS
sudo sed -i 's/^https:.*/# &/' /etc/opt/simplex/smp-server.ini
sudo sed -i 's/^cert:/# cert:/' /etc/opt/simplex/smp-server.ini
sudo sed -i 's/^key:/# key:/' /etc/opt/simplex/smp-server.ini
# CRITICAL: Configure SOCKS proxy in [PROXY] section for Private Routing
# First, remove any existing socks_proxy entries (might be in wrong section)
sudo sed -i '/^socks_proxy:/d' /etc/opt/simplex/smp-server.ini
sudo sed -i '/^socks_mode:/d' /etc/opt/simplex/smp-server.ini
# Add socks_proxy and socks_mode in [PROXY] section
sudo sed -i '/^\[PROXY\]$/a socks_proxy: 127.0.0.1:9050\nsocks_mode: onion' /etc/opt/simplex/smp-server.iniVerify the configuration:
sudo grep -A5 "\[PROXY\]" /etc/opt/simplex/smp-server.ini | head -10Expected output:
[PROXY]
socks_proxy: 127.0.0.1:9050
socks_mode: onion
# Ensure correct ownership
sudo chown -R simplex:simplex /etc/opt/simplex /var/opt/simplex
# Key file must be readable by simplex user
sudo chmod 600 /etc/opt/simplex/server.key
sudo chown simplex:simplex /etc/opt/simplex/server.key
# Verify
sudo ls -la /etc/opt/simplex/All files should show simplex simplex.
CRITICAL: Run as
simplexuser, not root!
XFTP_ONION=$(sudo cat /var/lib/tor/simplex-xftp/hostname)
sudo -u simplex xftp-server init -n "$XFTP_ONION" -p /var/opt/simplex-xftp/files -q 20gbsudo nano /etc/opt/simplex-xftp/file-server.iniEnsure:
[TRANSPORT]
port: 443# CRITICAL: Ensure files directory exists and has correct permissions
sudo mkdir -p /var/opt/simplex-xftp/files
sudo chown -R simplex:simplex /etc/opt/simplex-xftp /var/opt/simplex-xftp
sudo chmod 750 /var/opt/simplex-xftp/files
sudo chmod 600 /etc/opt/simplex-xftp/server.key
# Verify files directory exists
sudo ls -la /var/opt/simplex-xftp/You should see:
drwxr-x--- simplex simplex files
sudo tee /etc/systemd/system/smp-server.service > /dev/null <<'EOF'
[Unit]
Description=SimpleX SMP Server
After=network.target tor@default.service tor@tor2.service
Wants=tor@default.service tor@tor2.service
[Service]
Type=simple
User=simplex
Group=simplex
ExecStart=/usr/local/bin/smp-server start
Restart=always
RestartSec=5
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/opt/simplex
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
EOF💡 Note the dependencies:
tor@default.service→ Provides Hidden Servicestor@tor2.service→ Provides SOCKS proxy for Private RoutingBoth are required for full functionality!
CRITICAL: XFTP binds to port 443, which requires special capability for non-root users.
sudo tee /etc/systemd/system/xftp-server.service > /dev/null <<'EOF'
[Unit]
Description=SimpleX XFTP Server
After=network.target tor@default.service tor@tor2.service
Wants=tor@default.service tor@tor2.service
[Service]
Type=simple
User=simplex
Group=simplex
ExecStart=/usr/local/bin/xftp-server start
Restart=always
RestartSec=5
# CRITICAL: Allow binding to port 443 as non-root
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/opt/simplex-xftp
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
EOFsudo systemctl daemon-reload
sudo systemctl enable smp-server xftp-server
sudo systemctl start smp-server xftp-server
# Check status
sudo systemctl status smp-server --no-pager
sudo systemctl status xftp-server --no-pagerBoth should show active (running).
sudo tee /etc/nftables.conf > /dev/null <<'EOF'
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state established,related accept
# SSH from LAN only (remove after completing Appendix B)
ip saddr 192.168.0.0/16 tcp dport 22 accept
ip saddr 10.0.0.0/8 tcp dport 22 accept
# Block SimpleX ports from clearnet (only accessible via Tor)
tcp dport { 443, 5223 } drop
ip protocol icmp accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
EOF
sudo systemctl enable --now nftablessudo systemctl is-active tor@default smp-server xftp-serverAll should return active.
# Check both Tor instances are running
sudo systemctl is-active tor@default tor@tor2
# Verify SOCKS port is handled by tor@tor2
sudo ss -lntp | grep 9050
# Expected: tor@tor2 listening on 127.0.0.1:9050
# Test SOCKS proxy functionality
curl -x socks5h://127.0.0.1:9050 -s https://check.torproject.org/api/ip | jqsudo ss -lntp | grep -E ':(443|5223)'Expected:
LISTEN *:5223 users:(("smp-server",...))
LISTEN *:443 users:(("xftp-server",...))
SMP_ONION=$(sudo cat /var/lib/tor/simplex-smp/hostname)
XFTP_ONION=$(sudo cat /var/lib/tor/simplex-xftp/hostname)
torsocks nc -zv "$SMP_ONION" 5223 && echo "✓ SMP OK"
torsocks nc -zv "$XFTP_ONION" 443 && echo "✓ XFTP OK"echo "=== SERVER ADDRESSES ==="
sudo journalctl -u smp-server --no-pager | grep "Server address:" | tail -1
sudo journalctl -u xftp-server --no-pager | grep "Server address:" | tail -1Copy these for your SimpleX clients.
- Android/iOS: Install Orbot, enable SOCKS proxy
127.0.0.1:9050 - Desktop: Settings → Network → SOCKS proxy
127.0.0.1:9050
Step 1: Disable public servers
Settings → Network & Servers → SMP Servers:
→ SimpleX Chat servers → Disable (toggle off)
→ Flux servers → Disable (toggle off, if present)
Settings → Network & Servers → XFTP Servers:
→ SimpleX Chat servers → Disable (toggle off)
→ Flux servers → Disable (toggle off, if present)
Step 2: Add your servers
Settings → Network & Servers → SMP Servers → + Add Server
→ Paste: smp://<fingerprint>:<password>@<onion>:5223
→ Enable "Use for new connections" ✓
Settings → Network & Servers → XFTP Servers → + Add Server
→ Paste: xftp://<fingerprint>@<onion>:443
→ Enable "Use for new connections" ✓
Step 3: Verify configuration
Your server list should show:
- ✅ Your .onion servers: Enabled + "Use for new connections"
- ❌ SimpleX Chat servers: Disabled
- ❌ Flux servers: Disabled
For maximum privacy with your closed infrastructure:
Settings → Network & Servers → Private Message Routing:
→ Private routing: Always
→ Allow downgrade: No
Note: "Allow downgrade: No" is recommended for closed groups where all servers are under your control and properly configured.
Share server addresses securely with your group:
- In person (QR code scan)
- Via already-secure channel (existing SimpleX contact, Signal, encrypted email)
- Never via unencrypted email, SMS, or public channels
Cause: Missing gold linker. GHC uses -fuse-ld=gold.
Fix:
sudo apt install binutils-goldCause: Download corrupted or tampered with.
Fix:
# Re-download
rm smp-server xftp-server SHA256SUMS
wget https://github.com/cannatoshi/simplex-smp-xftp-via-tor-on-rpi-hardened/releases/download/v0.7.3-alpha/smp-server
wget https://github.com/cannatoshi/simplex-smp-xftp-via-tor-on-rpi-hardened/releases/download/v0.7.3-alpha/xftp-server
wget https://github.com/cannatoshi/simplex-smp-xftp-via-tor-on-rpi-hardened/releases/download/v0.7.3-alpha/SHA256SUMS
# Verify again
sha256sum -c SHA256SUMSIf still failing, build from source (Option B) or report an issue on GitHub.
Cause: Key file owned by root or wrong permissions.
Fix:
sudo chown simplex:simplex /etc/opt/simplex/server.key
sudo chmod 600 /etc/opt/simplex/server.key
sudo chown -R simplex:simplex /etc/opt/simplexCause: Non-root user cannot bind to privileged ports (<1024).
Fix: Use port 5223 for SMP, or add capability:
# In systemd unit:
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICECause: Multiple ports in config (port: 5223,443).
Fix: Use single port:
sudo sed -i 's/^port: .*/port: 5223/' /etc/opt/simplex/smp-server.ini
sudo systemctl restart smp-serverCause: Service file doesn't exist or no daemon-reload.
Fix:
# Create service file (see section 9)
sudo systemctl daemon-reload
sudo systemctl enable --now smp-serverCause: Files directory missing.
Fix:
sudo mkdir -p /var/opt/simplex-xftp/files
sudo chown -R simplex:simplex /var/opt/simplex-xftpChecklist:
- Is Tor running?
systemctl status tor@default - Do onion hostnames exist?
sudo cat /var/lib/tor/simplex-smp/hostname - Does Tor port match service port?
HiddenServicePort 5223 127.0.0.1:5223→ service must listen on 5223HiddenServicePort 443 127.0.0.1:443→ service must listen on 443
Cause: SMP servers cannot resolve .onion addresses for forwarding. This happens when:
socks_proxyis in the wrong section ([TRANSPORT]instead of[PROXY])socks_proxyis missing entirely- Using
localhostinstead of127.0.0.1
Symptom in logs:
Error connecting: xxx.onion PCENetworkError (NEConnectError {connectError = "...does not exist (Name or service not known)"})
Fix: Configure SOCKS proxy correctly in the [PROXY] section:
# Remove any existing socks_proxy entries (might be in wrong section)
sudo sed -i '/^socks_proxy:/d' /etc/opt/simplex/smp-server.ini
sudo sed -i '/^socks_mode:/d' /etc/opt/simplex/smp-server.ini
# Add correct configuration in [PROXY] section
sudo sed -i '/^\[PROXY\]$/a socks_proxy: 127.0.0.1:9050\nsocks_mode: onion' /etc/opt/simplex/smp-server.ini
# Restart server
sudo systemctl restart smp-serverVerify:
sudo grep -A3 "\[PROXY\]" /etc/opt/simplex/smp-server.iniShould show:
[PROXY]
socks_proxy: 127.0.0.1:9050
socks_mode: onion
Symptoms:
- Private Message Routing fails
- Log shows:
SocksErrorGeneralServerFailure - Intermittent connectivity issues
Root Cause: Running Hidden Services and SOCKS proxy in a single Tor instance causes resource conflicts.
Solution: Implement Dual Tor Architecture (see Appendix F):
# Quick fix commands:
# 1. Ensure tor@default has SOCKSPort 0
grep -q "SOCKSPort 0" /etc/tor/torrc || echo "SOCKSPort 0" | sudo tee -a /etc/tor/torrc
# 2. Create tor@tor2 if not exists
sudo tor-instance-create tor2 2>/dev/null || true
echo -e "Log notice syslog\nSocksPort 9050" | sudo tee /etc/tor/instances/tor2/torrc
# 3. Enable and start tor@tor2
sudo systemctl enable --now tor@tor2
# 4. Fix socks_mode (use 'onion', NOT 'always'!)
sudo sed -i 's/socks_mode: always/socks_mode: onion/' /etc/opt/simplex/smp-server.ini
# 5. Restart services
sudo systemctl restart tor@default smp-server
# 6. Verify
sudo systemctl is-active tor@default tor@tor2
sudo ss -lntp | grep 9050Cause: Server-side SOCKS proxy misconfiguration.
Quick Client Workaround (temporary):
Settings → Network & Servers → Private Message Routing:
→ Allow downgrade: Yes
This allows messages to be delivered directly if Private Routing fails.
Permanent Fix: Fix server configuration as described above, then set Allow downgrade: No again.
Symptoms:
- SimpleX clients show 0% or partial server connectivity
- Connectivity fluctuates between 0% and 100%
- Messages arrive with delays or not at all
- Logs show:
Guard XYZ is failing more circuits than usual
Cause: Tor network instability. Running 12 Hidden Services creates many Tor circuits. If Tor gets assigned a bad guard node, circuits may fail repeatedly. Tor normally holds guard nodes for 2-3 months for security, which means a bad guard can cause prolonged issues.
Quick Fix (restart Tor):
sudo systemctl restart tor@defaultIf that doesn't help (reset Tor state):
sudo systemctl stop tor@default
sudo rm /var/lib/tor/state
sudo systemctl start tor@default
⚠️ This forces Tor to pick new guard nodes. Use sparingly.
Permanent Fix: Install the Tor Watchdog - it automatically monitors and restarts Tor when Hidden Services become unreachable.
Check Tor status:
# Is Tor running and bootstrapped?
sudo journalctl -u tor@default --since "5 min ago" | grep -i bootstrap
# Any circuit errors?
sudo journalctl -u tor@default --since "10 min ago" | grep -iE "timeout|fail|circuit"Cause: Individual Hidden Service circuits may fail independently.
Fix:
# Test each server individually
for i in "" 2 3 4 5 6 7 8 9 10; do
if [ -z "$i" ]; then
ONION=$(sudo cat /var/lib/tor/simplex-smp/hostname)
NAME="SMP1"
else
ONION=$(sudo cat /var/lib/tor/simplex-smp$i/hostname)
NAME="SMP$i"
fi
timeout 30 torsocks nc -zv "$ONION" 5223 2>/dev/null && echo "✓ $NAME OK" || echo "✗ $NAME FAIL"
doneIf specific servers consistently fail, check their configs individually.
# Restart everything
sudo systemctl restart tor@default tor@tor2 smp-server xftp-server
# View logs
sudo journalctl -u smp-server -f
sudo journalctl -u xftp-server -f
sudo journalctl -u tor@default -f
sudo journalctl -u tor@tor2 -f
# Check ports
sudo ss -lntp | grep -E ':(443|5223|9050)'
# Check SOCKS proxy config (should be in [PROXY] section!)
sudo grep -A3 "\[PROXY\]" /etc/opt/simplex/smp-server.ini
# Check both Tor instances
sudo systemctl is-active tor@default tor@tor2
# Verify SOCKS is on tor@tor2
sudo ss -lntp | grep 9050
# Check Tor status
sudo journalctl -u tor@default --since "5 min ago" | grep -iE "bootstrap|circuit|guard"
# Test Hidden Service connectivity
torsocks nc -zv $(sudo cat /var/lib/tor/simplex-smp/hostname) 5223
# Test SOCKS proxy
curl -x socks5h://127.0.0.1:9050 -s https://check.torproject.org/api/ip | jq
# Export addresses (KEEP THESE SECRET!)
echo "SMP: $(sudo cat /var/lib/tor/simplex-smp/hostname)"
echo "XFTP: $(sudo cat /var/lib/tor/simplex-xftp/hostname)"The following appendices provide advanced security configurations for high-threat environments such as journalist networks, activist groups, or anyone requiring maximum metadata protection.
Prerequisite: Complete Sections 1-13 first.
Difficulty: Intermediate
Time: 30-45 minutes
Result: 10 SMP servers on a single Raspberry Pi with full Private Routing support
⚠️ EXPERIMENTAL FEATURERunning 10 SMP servers is currently experimental and may experience stability issues.
Recommendations:
- Start with 1-3 SMP servers for production use
- Monitor logs closely:
journalctl -u smp-server -f- Ensure Dual Tor Architecture is properly configured
We are actively working on improving Multi-SMP stability. Feedback welcome!
SimpleX Chat introduced Private Message Routing in v5.8 (enabled by default since v6.0) to protect sender IP addresses from recipient servers. It implements a two-hop packet routing mechanism similar to Tor's onion routing.
Without Private Routing:
Alice → [Alice's IP visible] → Bob's SMP Server → Bob
Bob's server (or Bob himself, if self-hosted) can see Alice's IP address when she sends messages.
With Private Routing enabled:
Alice → Forwarding Server → Destination Server → Bob
(sees Alice's IP) (sees Forwarding IP)
- Alice's client encrypts the message for Bob's destination server
- Wraps it in another encryption layer for a forwarding server
- Forwarding server relays without knowing the final recipient's queue ID
- Destination server receives from forwarding server, not Alice directly
Result: Per-packet anonymity. No single server sees both sender AND recipient.
Even within your closed infrastructure, running multiple SMP servers provides significant privacy advantages:
| Feature | 1 Server | 4+ Servers | 10 Servers |
|---|---|---|---|
| Private Routing | ❌ Limited | ✅ Functional | ✅ Optimal |
| Traffic Mixing | ❌ None | ✅ Basic | ✅ Strong |
| Internal Correlation | High | Reduced | Minimal |
| Queue Distribution | Centralized | Spread | Maximum Entropy |
With 10 servers, even within your closed group:
- Queues distributed randomly across all servers
- Different server pairs used for each conversation
- Traffic patterns harder to analyze even for you as operator
- Separation of concerns – compromise of one server doesn't reveal all traffic
┌─────────────────────────────────────────────────────────────────┐
│ DUAL TOR ARCHITECTURE │
│ │
│ tor@default tor@tor2 │
│ (Hidden Services) (SOCKS Proxy) │
│ SOCKSPort 0 SocksPort 9050 │
│ │ │ │
│ ▼ ▼ │
│ INBOUND OUTBOUND │
│ (.onion addresses) (Private Routing) │
└───────┬────────────────────────┬────────────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ RASPBERRY PI 4 │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ SMP #1 │ │ SMP #2 │ │ SMP #3 │ │ SMP #4 │ │
│ │ :5223 │ │ :5224 │ │ :5225 │ │ :5226 │ │
│ │ onion1 │ │ onion2 │ │ onion3 │ │ onion4 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ SMP #5 │ │ SMP #6 │ │ SMP #7 │ │ SMP #8 │ │
│ │ :5227 │ │ :5228 │ │ :5229 │ │ :5230 │ │
│ │ onion5 │ │ onion6 │ │ onion7 │ │ onion8 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ ┌─────────┐ ┌─────────┐ ┌─────────────────────┐ │
│ │ SMP #9 │ │ SMP #10 │ │ XFTP #1 │ │
│ │ :5231 │ │ :5232 │ │ :443 │ │
│ │ onion9 │ │ onion10 │ │ onionX │ │
│ └─────────┘ └─────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Port Mapping:
| Instance | Local Port | Tor External | Onion Address |
|---|---|---|---|
| Original | 5223 | 5223 | unique .onion |
| SMP #2 | 5224 | 5223 | unique .onion |
| SMP #3 | 5225 | 5223 | unique .onion |
| SMP #4 | 5226 | 5223 | unique .onion |
| SMP #5 | 5227 | 5223 | unique .onion |
| SMP #6 | 5228 | 5223 | unique .onion |
| SMP #7 | 5229 | 5223 | unique .onion |
| SMP #8 | 5230 | 5223 | unique .onion |
| SMP #9 | 5231 | 5223 | unique .onion |
| SMP #10 | 5232 | 5223 | unique .onion |
Each server has its own Tor hidden service, so clients connect to onion:5223 regardless of local port.
# Create config and data directories for SMP 2-10
sudo mkdir -p /etc/opt/simplex-smp{2,3,4,5,6,7,8,9,10}
sudo mkdir -p /var/opt/simplex-smp{2,3,4,5,6,7,8,9,10}
# Set ownership
sudo chown -R simplex:simplex /etc/opt/simplex-smp{2,3,4,5,6,7,8,9,10}
sudo chown -R simplex:simplex /var/opt/simplex-smp{2,3,4,5,6,7,8,9,10}
# Verify
ls -la /etc/opt/ | grep simplex-smpExpected: 9 directories (smp2 through smp10), all owned by simplex:simplex.
A.5 Configure Tor Hidden Services
Add to /etc/tor/torrc:
sudo nano /etc/tor/torrcAppend:
# === SimpleX SMP Multi-Instance (2-10) ===
HiddenServiceDir /var/lib/tor/simplex-smp2/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5224
HiddenServiceDir /var/lib/tor/simplex-smp3/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5225
HiddenServiceDir /var/lib/tor/simplex-smp4/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5226
HiddenServiceDir /var/lib/tor/simplex-smp5/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5227
HiddenServiceDir /var/lib/tor/simplex-smp6/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5228
HiddenServiceDir /var/lib/tor/simplex-smp7/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5229
HiddenServiceDir /var/lib/tor/simplex-smp8/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5230
HiddenServiceDir /var/lib/tor/simplex-smp9/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5231
HiddenServiceDir /var/lib/tor/simplex-smp10/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5232Restart Tor and collect onion addresses:
sudo systemctl restart tor@default
sleep 5
# Get all onion addresses
echo "=== ONION ADDRESSES (KEEP SECRET!) ==="
for i in 2 3 4 5 6 7 8 9 10; do
echo "SMP$i: $(sudo cat /var/lib/tor/simplex-smp$i/hostname)"
doneSave these addresses securely! Share only with your closed group members.
The smp-server binary uses hardcoded paths. We use a symlink trick to initialize each instance.
For each instance (2-10), repeat:
# Example for SMP2 - repeat for 3,4,5,6,7,8,9,10
INSTANCE=2
ONION="your-smp2-onion.onion" # Replace with actual onion
PASSWORD="YourSecurePassword"
# Backup original paths
sudo mv /etc/opt/simplex /etc/opt/simplex-backup 2>/dev/null || true
sudo mv /var/opt/simplex /var/opt/simplex-backup 2>/dev/null || true
# Create symlinks
sudo ln -s /etc/opt/simplex-smp$INSTANCE /etc/opt/simplex
sudo ln -s /var/opt/simplex-smp$INSTANCE /var/opt/simplex
# Initialize
sudo -u simplex smp-server init -n "$ONION" --password "$PASSWORD" -y
# Remove symlinks
sudo rm /etc/opt/simplex /var/opt/simplex
# Restore original
sudo mv /etc/opt/simplex-backup /etc/opt/simplex 2>/dev/null || true
sudo mv /var/opt/simplex-backup /var/opt/simplex 2>/dev/null || true
⚠️ CRITICAL: The SOCKS proxy configuration MUST be in the[PROXY]section for Private Message Routing to work! Placing it in[TRANSPORT]will NOT work.Also CRITICAL: Use
socks_mode: onion, NOTsocks_mode: always! Usingalwayscauses instability when Tor circuits rebuild.
# Configure all instances
for i in 2 3 4 5 6 7 8 9 10; do
PORT=$((5222 + i)) # 5224, 5225, 5226, etc.
echo "Configuring SMP$i on port $PORT"
# Set port in [TRANSPORT]
sudo sed -i "s/^port: .*/port: $PORT/" /etc/opt/simplex-smp$i/smp-server.ini
# Disable HTTPS
sudo sed -i 's/^https:.*/# &/' /etc/opt/simplex-smp$i/smp-server.ini
# CRITICAL: Remove any existing socks_proxy/socks_mode entries (might be in wrong section)
sudo sed -i '/^socks_proxy:/d' /etc/opt/simplex-smp$i/smp-server.ini
sudo sed -i '/^socks_mode:/d' /etc/opt/simplex-smp$i/smp-server.ini
# CRITICAL: Add SOCKS proxy in [PROXY] section (NOT [TRANSPORT]!)
# Use socks_mode: onion (NOT always!)
sudo sed -i '/^\[PROXY\]$/a socks_proxy: 127.0.0.1:9050\nsocks_mode: onion' /etc/opt/simplex-smp$i/smp-server.ini
done
# Also fix the original server if not already done
sudo sed -i '/^socks_proxy:/d' /etc/opt/simplex/smp-server.ini
sudo sed -i '/^socks_mode:/d' /etc/opt/simplex/smp-server.ini
sudo sed -i '/^\[PROXY\]$/a socks_proxy: 127.0.0.1:9050\nsocks_mode: onion' /etc/opt/simplex/smp-server.iniVerify the configuration is correct:
echo "=== VERIFYING [PROXY] SECTION CONFIG ==="
echo "Original server:"
sudo grep -A3 "\[PROXY\]" /etc/opt/simplex/smp-server.ini | head -5
for i in 2 3 4 5 6 7 8 9 10; do
echo "SMP$i:"
sudo grep -A3 "\[PROXY\]" /etc/opt/simplex-smp$i/smp-server.ini | head -5
doneExpected output for each server:
[PROXY]
socks_proxy: 127.0.0.1:9050
socks_mode: onion
sudo tee /etc/systemd/system/smp-server@.service > /dev/null <<'EOF'
[Unit]
Description=SimpleX SMP Server (Instance %i)
After=network.target tor@default.service tor@tor2.service
Wants=tor@default.service tor@tor2.service
[Service]
Type=simple
User=simplex
Group=simplex
ExecStart=/usr/local/bin/smp-server start
# Map instance paths to default paths expected by smp-server
BindPaths=/etc/opt/simplex-smp%i:/etc/opt/simplex
BindPaths=/var/opt/simplex-smp%i:/var/opt/simplex
Restart=always
RestartSec=5
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
EOFThe magic is in BindPaths= – systemd remaps the directories so each instance thinks it's using the default paths.
sudo systemctl daemon-reload
# Enable all instances (2-10)
sudo systemctl enable smp-server@{2,3,4,5,6,7,8,9,10}
# Start all instances
sudo systemctl start smp-server@{2,3,4,5,6,7,8,9,10}
# Restart original to pick up any config changes
sudo systemctl restart smp-server
# Check status
sudo systemctl status smp-server@{2,3,4,5,6,7,8,9,10} --no-pager | grep -E "(smp-server@|Active:)"All should show active (running).
sudo ss -lntp | grep smp-server | wc -lShould show 10 (original + 9 new instances).
echo "=== TOR CONNECTIVITY TEST ==="
for i in "" 2 3 4 5 6 7 8 9 10; do
if [ -z "$i" ]; then
ONION=$(sudo cat /var/lib/tor/simplex-smp/hostname)
NAME="Original"
else
ONION=$(sudo cat /var/lib/tor/simplex-smp$i/hostname)
NAME="SMP$i"
fi
torsocks nc -zv "$ONION" 5223 2>/dev/null && echo "✓ $NAME OK" || echo "✗ $NAME FAIL"
doneAll 10 should succeed.
# Wait 30 seconds, then check for SOCKS/routing errors
sleep 30 && sudo journalctl -u 'smp-server*' --since "1 min ago" | grep -iE "(error|fail)" | tail -10If there are no errors (empty output), Private Routing is working correctly!
For each member of your closed group:
- Open SimpleX Chat
- Go to Settings → Network & Servers → SMP Servers
- Disable all public servers (SimpleX Chat, Flux)
- For each of your 10 servers, tap + Add Server and paste:
smp://FINGERPRINT:PASSWORD@ONION_ADDRESS:5223 - Enable "Use for new connections" on ALL 10 servers ✅
- Settings → Network & Servers → Private Message Routing
- Set Private routing to:
Always - Set Allow downgrade to:
No(for closed groups, all servers support it)
Each group member's server list should show:
- ✅ Your 10 .onion servers: Enabled + "Use for new connections"
- ❌ SimpleX Chat servers: Disabled
- ❌ Flux servers: Disabled
After setup, check your Pi's load:
echo "=== SYSTEM STATUS ==="
uptime
free -h
ps aux | grep -E "(smp|xftp)" | grep -v grep | wc -lExpected with 10 SMP + 1 XFTP:
| Resource | Usage |
|---|---|
| CPU | ~2-5% idle |
| RAM | ~400-500 MB |
| Load Average | < 0.5 |
A Raspberry Pi 4 (4GB) handles 10+ SMP servers easily.
✅ Complete isolation – All traffic stays within your closed infrastructure
✅ Traffic mixing – Messages distributed across 10 servers
✅ Private routing – No server sees both sender and recipient
✅ Discovery protection – Adversary must find your .onion addresses first
✅ Self-hosted – You are the only operator
❌ Multi-operator mixing – All servers under your control
❌ Physical distribution – All servers on one device
❌ Protection from you – You can correlate all traffic if you choose
See Threat Model & Limitations for detailed analysis.
# Start all instances
sudo systemctl start smp-server smp-server@{2,3,4,5,6,7,8,9,10}
# Stop all instances
sudo systemctl stop smp-server smp-server@{2,3,4,5,6,7,8,9,10}
# Restart all instances
sudo systemctl restart smp-server smp-server@{2,3,4,5,6,7,8,9,10}
# View logs for instance 5
sudo journalctl -u smp-server@5 -f
# Check all ports
sudo ss -lntp | grep smp-server
# Count running instances
sudo ss -lntp | grep smp-server | wc -l
# Verify SOCKS config in [PROXY] section (CRITICAL!)
for i in "" 2 3 4 5 6 7 8 9 10; do
if [ -z "$i" ]; then
echo "=== Original ===" && sudo grep -A3 "\[PROXY\]" /etc/opt/simplex/smp-server.ini | head -5
else
echo "=== SMP$i ===" && sudo grep -A3 "\[PROXY\]" /etc/opt/simplex-smp$i/smp-server.ini | head -5
fi
done
# Export all addresses (KEEP SECRET!)
echo "=== ALL SERVER ADDRESSES ==="
echo "SMP1: $(sudo cat /var/lib/tor/simplex-smp/hostname)"
for i in 2 3 4 5 6 7 8 9 10; do
echo "SMP$i: $(sudo cat /var/lib/tor/simplex-smp$i/hostname)"
done
echo "XFTP: $(sudo cat /var/lib/tor/simplex-xftp/hostname)"Prerequisite: Complete Sections 1-13 (Core Setup) first.
Difficulty: Beginner-Intermediate
Time: 15-20 minutes
Result: SSH access exclusively via Tor hidden service – zero clearnet exposure
Your SimpleX infrastructure is Tor-only and invisible. Your SSH access should be too.
┌──────────────┐ ┌──────────────┐
│ Admin PC │ ──── SSH:22 ────▶ │ Raspberry Pi │
│ (Real IP) │ (Clearnet) │ (Real IP) │
└──────────────┘ └──────────────┘
↓
ISP logs connection
Pi exposed on port 22
Brute-force attacks possible
Administration reveals Pi location
Even with your SimpleX services hidden, SSH over clearnet:
- Reveals the Pi's IP address to your ISP
- Creates a target for attackers
- Links your admin sessions to a physical location
- Undermines the "invisible infrastructure" concept
┌──────────────┐ ┌──────────────┐
│ Admin PC │ ══ Tor Circuit ══▶ │ Raspberry Pi │
│ (Anonymous) │ (.onion:22) │ (Invisible) │
└──────────────┘ └──────────────┘
↓
No ISP visibility to destination
No exposed ports on clearnet
No brute-force surface
Admin from anywhere anonymously
| Feature | Description |
|---|---|
| Zero clearnet exposure | Port 22 never touches the internet |
| Location independence | Admin from any country, any network |
| ISP invisibility | Only Tor traffic visible, destination hidden |
| NAT/Firewall bypass | Works behind CGNAT, hotel WiFi, corporate firewalls |
| Brute-force immunity | Attackers can't find what doesn't exist |
| Consistent security model | Everything via Tor, nothing via clearnet |
┌────────────────────────────────────────────────────────────────┐
│ RASPBERRY PI │
├────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────┐ ┌────────────────────────────┐ │
│ │ SSHD │ │ TOR DAEMON │ │
│ │ 127.0.0.1:22 │◄────│ HiddenServicePort 22 │ │
│ │ │ │ → xyzabc...onion:22 │ │
│ └────────────────────┘ └────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ NFTABLES │ │
│ │ tcp dport 22 from clearnet → DROP │ │
│ │ tcp dport 22 from 127.0.0.1 → ACCEPT │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
│
▼ (Tor only)
┌─────────────────┐
│ TOR NETWORK │
└─────────────────┘
│
▼
┌─────────────────┐
│ Admin Client │
│ (torsocks) │
└─────────────────┘
B.3 Configure Tor Hidden Service for SSH
Add the SSH hidden service to your existing Tor configuration:
sudo nano /etc/tor/torrcAppend at the end:
# === SSH Administration ===
HiddenServiceDir /var/lib/tor/ssh/
HiddenServiceVersion 3
HiddenServicePort 22 127.0.0.1:22Restart Tor and retrieve your SSH onion address:
sudo systemctl restart tor@default
sleep 5
# Get your SSH onion address
SSH_ONION=$(sudo cat /var/lib/tor/ssh/hostname)
echo "SSH Onion: $SSH_ONION"# Backup to a safe location (example: encrypted file)
echo "$SSH_ONION" | gpg -c > ssh_onion_backup.gpgEdit the SSH daemon configuration:
sudo nano /etc/ssh/sshd_configFind and modify (or add) these lines:
# Bind ONLY to localhost - Tor will forward connections
ListenAddress 127.0.0.1
# Disable password authentication (key-only)
PasswordAuthentication no
PubkeyAuthentication yes
# Harden against attacks
PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 30
# Disable unnecessary features
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PermitTunnel noNote:
ListenAddress 127.0.0.1is the key change. SSH will only accept connections from localhost, which means only Tor can reach it.
Before restarting SSH, verify your public key is installed:
# Check authorized_keys exists
cat ~/.ssh/authorized_keysIf empty or missing, add your public key now:
# On your ADMIN MACHINE, generate key if needed:
ssh-keygen -t ed25519 -C "simplex-admin"
# Copy public key to Pi (while you still have LAN access!)
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@raspberry-pi-ipsudo systemctl restart sshd
# Verify it's listening on localhost only
sudo ss -lntp | grep sshdExpected output:
LISTEN 127.0.0.1:22 users:(("sshd",pid=...))
If you see 0.0.0.0:22 or *:22, the config change didn't apply. Check /etc/ssh/sshd_config again.
Update nftables to block all clearnet SSH:
sudo nano /etc/nftables.confReplace the entire file with:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Loopback - required for Tor→SSH and Tor→SimpleX
iif lo accept
# Established connections
ct state established,related accept
# BLOCK SSH from clearnet (Tor connects via loopback)
tcp dport 22 drop
# Block SimpleX ports from clearnet
tcp dport { 443, 5223-5232 } drop
# ICMP disabled for stealth (optional: uncomment to enable)
# ip protocol icmp accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}Apply the firewall:
sudo nft -f /etc/nftables.conf
sudo systemctl restart nftablesDebian/Ubuntu:
sudo apt install tor torsocks
sudo systemctl enable --now tormacOS:
brew install tor torsocks
brew services start torWindows:
- Install Tor Expert Bundle or use WSL2
The simplest method:
SSH_ONION="your-ssh-onion-address.onion"
torsocks ssh user@$SSH_ONIONAdd to ~/.ssh/config for convenience:
Host simplex-pi
HostName your-ssh-onion-address.onion
User pi
IdentityFile ~/.ssh/id_ed25519
ProxyCommand nc -X 5 -x 127.0.0.1:9050 %h %p
ServerAliveInterval 60
ServerAliveCountMax 3
ConnectTimeout 120Now connect with:
ssh simplex-piNote:
ProxyCommanduses netcat to route through Tor SOCKS proxy. This works on Linux/macOS. For Windows, use PuTTY with a SOCKS proxy configuration.
From your admin machine, try connecting directly (without Tor):
# This should fail/timeout
ssh user@raspberry-pi-lan-ipIf this works, your firewall or SSHD config is wrong.
# This should succeed
torsocks ssh user@your-ssh-onion.onion# Verify SSH is localhost-only
sudo ss -lntp | grep :22
# Should show:
# LISTEN 127.0.0.1:22 ..."I locked myself out!"
If you lose Tor access, you need physical access to the Pi:
- Connect monitor + keyboard to Pi
- Login locally (local console still works)
- Fix configuration:
# Temporarily allow LAN SSH sudo sed -i 's/ListenAddress 127.0.0.1/ListenAddress 0.0.0.0/' /etc/ssh/sshd_config sudo systemctl restart sshd
- Fix your issue, then restore Tor-only config
The SSH onion address is derived from keys in /var/lib/tor/ssh/. Back these up:
# On the Pi
sudo tar -czf /tmp/tor-ssh-keys.tar.gz -C /var/lib/tor ssh/
sudo chown $USER /tmp/tor-ssh-keys.tar.gz
# Transfer to admin machine
torsocks scp user@your-onion:/tmp/tor-ssh-keys.tar.gz ./
# Encrypt and store securely
gpg -c tor-ssh-keys.tar.gzIf you lose the SD card, you can restore these keys to get the same onion address.
SSH over Tor is slower than clearnet SSH due to:
- 3-hop Tor circuit (6 relays round-trip)
- Onion service rendezvous overhead
- Tor network latency
Typical latencies:
- Clearnet SSH: 10-50ms
- Tor SSH: 200-800ms
Tips for usability:
- Enable SSH compression: Add
Compression yesto SSH config - Increase timeouts:
ServerAliveInterval 60prevents disconnects - Use tmux/screen: Persistent sessions survive disconnects
- Batch operations: Script repetitive tasks
✅ No exposed ports – SSH invisible on clearnet
✅ Location anonymity – Admin IP hidden from Pi
✅ NAT traversal – Works from any network
✅ Consistent security model – Everything via Tor
❌ Key compromise protection – If your SSH key leaks, attacker can connect
❌ Onion address secrecy – If leaked, anyone with Tor can attempt connection
- Appendix C (Tor v3 Client Authorization) – Make onion invisible without auth key
- Use hardware security key (FIDO2) for SSH authentication
- Implement fail2ban (monitors auth failures even on localhost)
# Get SSH onion address
sudo cat /var/lib/tor/ssh/hostname
# Connect via Tor
torsocks ssh user@your-ssh-onion.onion
# Or with SSH config
ssh simplex-pi
# Check SSH is localhost-only
sudo ss -lntp | grep :22
# Restart SSH
sudo systemctl restart sshd
# Restart Tor
sudo systemctl restart tor@default
# Test Tor connectivity
torsocks nc -zv your-ssh-onion.onion 22After completing Appendices A and B, your /etc/tor/torrc should contain:
# Disable SOCKS in this instance - we use tor@tor2 for SOCKS proxy
SOCKSPort 0
# === SSH Administration ===
HiddenServiceDir /var/lib/tor/ssh/
HiddenServiceVersion 3
HiddenServicePort 22 127.0.0.1:22
# === SimpleX SMP (Original) ===
HiddenServiceDir /var/lib/tor/simplex-smp/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5223
# === SimpleX XFTP ===
HiddenServiceDir /var/lib/tor/simplex-xftp/
HiddenServiceVersion 3
HiddenServicePort 443 127.0.0.1:443
# === SimpleX SMP Multi-Instance (2-10) ===
HiddenServiceDir /var/lib/tor/simplex-smp2/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5224
HiddenServiceDir /var/lib/tor/simplex-smp3/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5225
HiddenServiceDir /var/lib/tor/simplex-smp4/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5226
HiddenServiceDir /var/lib/tor/simplex-smp5/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5227
HiddenServiceDir /var/lib/tor/simplex-smp6/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5228
HiddenServiceDir /var/lib/tor/simplex-smp7/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5229
HiddenServiceDir /var/lib/tor/simplex-smp8/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5230
HiddenServiceDir /var/lib/tor/simplex-smp9/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5231
HiddenServiceDir /var/lib/tor/simplex-smp10/
HiddenServiceVersion 3
HiddenServicePort 5223 127.0.0.1:5232Total: 12 hidden services (1 SSH + 10 SMP + 1 XFTP)
Coming in v0.8
This will add an additional layer of protection: even if someone discovers your .onion address, they cannot connect without the correct authorization key.
Coming in v0.9
This will protect against guard discovery attacks that could be used to locate your hidden service.
Prerequisite: Complete Sections 1-13 (Core Setup) first.
Difficulty: Beginner
Time: 5 minutes
Result: Automatic recovery from Tor connectivity issues
Running 12 Tor Hidden Services creates many Tor circuits. Occasionally, Tor may experience connectivity issues:
- Bad guard nodes – Tor holds guard nodes for 2-3 months for security. If a guard becomes unreliable, you're stuck with it.
- Circuit timeouts – Hidden service circuits may fail to build.
- Network conditions – ISP issues, Tor network congestion.
Symptoms:
- Clients show 0% or partial server connectivity
- Messages stop arriving
- Logs show:
Guard XYZ is failing more circuits than usual - Connectivity fluctuates randomly
The manual fix is to restart Tor, but you can't do that if you're not monitoring 24/7.
A simple script that:
- Tests if your primary Hidden Service is reachable via Tor
- If unreachable, restarts Tor automatically
- Runs every 5 minutes via systemd timer
- Logs all restarts to syslog
sudo tee /usr/local/bin/tor-watchdog.sh > /dev/null <<'EOF'
#!/bin/bash
# Tor Hidden Service Watchdog
# Checks if primary SMP server is reachable, restarts Tor if not
ONION=$(cat /var/lib/tor/simplex-smp/hostname)
if ! timeout 30 torsocks nc -z "$ONION" 5223 2>/dev/null; then
logger "Tor watchdog: Hidden service unreachable, restarting Tor"
systemctl restart tor@default
fi
EOF
sudo chmod +x /usr/local/bin/tor-watchdog.shsudo tee /etc/systemd/system/tor-watchdog.service > /dev/null <<'EOF'
[Unit]
Description=Tor Hidden Service Watchdog
After=tor@default.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/tor-watchdog.sh
EOFsudo tee /etc/systemd/system/tor-watchdog.timer > /dev/null <<'EOF'
[Unit]
Description=Run Tor Watchdog every 5 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
EOFsudo systemctl daemon-reload
sudo systemctl enable --now tor-watchdog.timersudo systemctl status tor-watchdog.timer --no-pagerExpected output:
● tor-watchdog.timer - Run Tor Watchdog every 5 minutes
Loaded: loaded (/etc/systemd/system/tor-watchdog.timer; enabled; ...)
Active: active (running) since ...
sudo /usr/local/bin/tor-watchdog.sh && echo "Watchdog OK"sudo journalctl | grep "Tor watchdog"If the watchdog has restarted Tor, you'll see:
Dec 21 20:30:15 simplex root: Tor watchdog: Hidden service unreachable, restarting Tor
┌────────────────────────────────────────────────────────────────┐
│ EVERY 5 MINUTES │
├────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ │
│ │ Timer fires │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ Test: Can we reach our own │ │
│ │ Hidden Service via Tor? │ │
│ │ torsocks nc -z $ONION 5223 │ │
│ └──────────────────┬───────────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ SUCCESS │ │ FAILURE │ │
│ │ Do nothing │ │ Restart Tor│ │
│ │ (all good) │ │ Log event │ │
│ └─────────────┘ └─────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
Edit the timer to check more or less frequently:
sudo nano /etc/systemd/system/tor-watchdog.timerChange OnUnitActiveSec=5min to desired interval (e.g., 3min, 10min).
sudo systemctl daemon-reload
sudo systemctl restart tor-watchdog.timerModify the script to check multiple Hidden Services:
sudo nano /usr/local/bin/tor-watchdog.sh#!/bin/bash
# Enhanced Tor Watchdog - checks multiple services
SERVICES=(
"simplex-smp:5223"
"simplex-xftp:443"
)
RESTART_NEEDED=false
for SERVICE in "${SERVICES[@]}"; do
DIR=$(echo "$SERVICE" | cut -d: -f1)
PORT=$(echo "$SERVICE" | cut -d: -f2)
ONION=$(cat "/var/lib/tor/$DIR/hostname")
if ! timeout 30 torsocks nc -z "$ONION" "$PORT" 2>/dev/null; then
logger "Tor watchdog: $DIR unreachable on port $PORT"
RESTART_NEEDED=true
fi
done
if [ "$RESTART_NEEDED" = true ]; then
logger "Tor watchdog: Restarting Tor due to unreachable services"
systemctl restart tor@default
fi# Timer status
sudo systemctl status tor-watchdog.timer
# Manual test
sudo /usr/local/bin/tor-watchdog.sh && echo "OK"
# View watchdog logs
sudo journalctl | grep "Tor watchdog"
# Stop watchdog
sudo systemctl stop tor-watchdog.timer
# Restart watchdog
sudo systemctl restart tor-watchdog.timer
# Disable watchdog
sudo systemctl disable tor-watchdog.timerThe watchdog is a mitigation, not a cure:
- ✅ Handles temporary Tor network issues
- ✅ Recovers from bad guard node selection
- ✅ Works unattended 24/7
- ❌ Cannot fix underlying network problems
- ❌ Cannot fix misconfigured torrc
- ❌ Frequent restarts may indicate deeper issues
If the watchdog restarts Tor more than a few times per day, investigate:
- Internet connection stability
- Router/firewall issues (especially with IPS/IDS)
- Tor configuration problems
Note: The Dual Tor setup is now part of the Core Setup (Section 4). This appendix provides background information, troubleshooting, and upgrade instructions for existing installations.
When running SimpleX SMP servers with Private Message Routing enabled, you need:
- Inbound connections via Tor Hidden Services (clients connect to your .onion)
- Outbound connections via SOCKS proxy (for forwarding messages to other servers)
The Problem: Running both Hidden Services AND a SOCKS proxy in a single Tor instance causes resource conflicts:
- Tor circuits compete for resources
- SOCKS requests interfere with Hidden Service circuits
- Result:
SocksErrorGeneralServerFailureerrors and unstable routing
The Solution: Two separate Tor instances with dedicated responsibilities:
| Instance | Purpose | Configuration |
|---|---|---|
tor@default |
Hidden Services ONLY | SOCKSPort 0 |
tor@tor2 |
SOCKS Proxy ONLY | SocksPort 9050 |
┌─────────────────────────────────────────────────────────────────┐
│ DUAL TOR ARCHITECTURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ tor@default │ │ tor@tor2 │ │
│ │ (Hidden Services) │ │ (SOCKS Proxy) │ │
│ │ │ │ │ │
│ │ SOCKSPort 0 │ │ SocksPort 9050 │ │
│ │ HiddenService ×N │ │ No HiddenServices │ │
│ │ │ │ │ │
│ └──────────┬──────────┘ └──────────┬──────────┘ │
│ │ │ │
│ │ INBOUND │ OUTBOUND │
│ │ (clients → .onion) │ (→ other servers) │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ SMP SERVER ││
│ │ ││
│ │ socks_proxy: 127.0.0.1:9050 ←── Points to tor@tor2 ││
│ │ socks_mode: onion ←── Only .onion via SOCKS ││
│ │ ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────┘
If you followed Section 4 correctly, verify your Dual Tor setup:
# 1. Both instances should be running
sudo systemctl is-active tor@default tor@tor2
# Expected: active / active
# 2. SOCKS should be on tor@tor2, port 9050
sudo ss -lntp | grep 9050
# Expected: tor (tor@tor2) listening on 127.0.0.1:9050
# 3. tor@default should have NO SOCKS
grep "SOCKSPort" /etc/tor/torrc
# Expected: SOCKSPort 0
# 4. tor@tor2 should have SOCKS on 9050
grep "SocksPort" /etc/tor/instances/tor2/torrc
# Expected: SocksPort 9050
# 5. Test SOCKS connectivity
curl -x socks5h://127.0.0.1:9050 -s https://check.torproject.org/api/ip | jq
# 6. Check SMP server config points to tor@tor2
sudo grep -A5 "\[PROXY\]" /etc/opt/simplex/smp-server.ini
# Expected:
# socks_proxy: 127.0.0.1:9050
# socks_mode: onionCause: SOCKS proxy not available or misconfigured.
Solution:
- Verify tor@tor2 is running:
sudo systemctl status tor@tor2 - Check port 9050:
sudo ss -lntp | grep 9050 - Ensure
socks_mode: onion(NOTalways) in smp-server.ini
Cause: Likely running SOCKS in tor@default.
Solution:
- Add
SOCKSPort 0to/etc/tor/torrc - Restart tor@default:
sudo systemctl restart tor@default - Verify tor@tor2 handles SOCKS:
sudo ss -lntp | grep 9050
Cause: Port conflict or missing directory.
Solution:
# Check for port conflict
sudo ss -lntp | grep 9050
# Verify instance exists
ls -la /etc/tor/instances/tor2/
# Check logs
sudo journalctl -u tor@tor2 -n 50# Check both Tor instances
sudo systemctl is-active tor@default tor@tor2
# Restart both
sudo systemctl restart tor@default tor@tor2
# View tor@tor2 logs
sudo journalctl -u tor@tor2 -f
# Test SOCKS proxy
curl -x socks5h://127.0.0.1:9050 -s https://check.torproject.org/api/ip
# Verify SOCKS is handled by tor@tor2
sudo ss -lntp | grep 9050
# Quick fix for SocksError
grep -q "SOCKSPort 0" /etc/tor/torrc || echo "SOCKSPort 0" | sudo tee -a /etc/tor/torrc
sudo systemctl restart tor@defaultIf you're upgrading from v0.7.2 or earlier:
# 1. Add SOCKSPort 0 to tor@default
grep -q "SOCKSPort 0" /etc/tor/torrc || echo "SOCKSPort 0" | sudo tee -a /etc/tor/torrc
# 2. Create and configure tor@tor2
sudo tor-instance-create tor2
echo -e "Log notice syslog\nSocksPort 9050" | sudo tee /etc/tor/instances/tor2/torrc
sudo systemctl enable --now tor@tor2
# 3. Fix socks_mode in all SMP configs (use 'onion', NOT 'always'!)
sudo sed -i 's/socks_mode: always/socks_mode: onion/' /etc/opt/simplex/smp-server.ini
# For Multi-SMP setups:
for i in 2 3 4 5 6 7 8 9 10; do
sudo sed -i 's/socks_mode: always/socks_mode: onion/' /etc/opt/simplex-smp$i/smp-server.ini 2>/dev/null || true
done
# 4. Update systemd dependencies (add to [Unit] section)
# Wants=tor@tor2.service
# After=tor@tor2.service
sudo systemctl daemon-reload
# 5. Restart everything
sudo systemctl restart tor@default smp-server
# 6. Verify
sudo systemctl is-active tor@default tor@tor2
sudo ss -lntp | grep 9050This project is actively developed. The following features are planned:
| Feature | Status | Version | Notes |
|---|---|---|---|
| Multi-SMP Private Routing | v0.6+ | Experimental with 10 servers; stable with 1-3 | |
| SSH over Tor | ✅ | v0.7 | Admin access only via .onion |
| Pre-built ARM64 Binaries | ✅ | v0.7 | Skip 60min compile time |
| Closed User Group Docs | ✅ | v0.7 | Documentation for isolated infrastructure |
| SOCKS Proxy Fix | ✅ | v0.7.1 | Correct [PROXY] section configuration |
| Tor Watchdog | ✅ | v0.7.2 | Auto-recovery from connectivity issues |
| Dual Tor Architecture | ✅ | v0.7.3 | Separate instances for HS and SOCKS |
| socks_mode: onion Fix | ✅ | v0.7.3 | Use 'onion' not 'always' for stability |
| Tor v3 Client Authorization | 🔜 | v0.8 | Hidden services invisible without keys |
| Vanguards | 🔜 | v0.9 | Guard discovery protection |
| LUKS Full-Disk Encryption | 📋 | Planned | Protect data at rest |
| Dead Man's Switch | 📋 | Planned | Auto-wipe on tampering |
| Feature | Status | Description |
|---|---|---|
| Queue Creation Password | ✅ v0.1 | Restrict who can create queues |
| Proof-of-Work (PoW) | 📋 Planned | CPU-based spam prevention |
| Rate Limiting | 📋 Planned | Connection/message limits |
| Connection Limits | 📋 Planned | HiddenServiceMaxStreams |
| Feature | Status | Description |
|---|---|---|
| Tor Watchdog | ✅ v0.7.2 | Automatic Tor restart on issues |
| Prometheus Metrics | 📋 Planned | Server statistics export |
| Grafana Dashboards | 📋 Planned | Visual monitoring |
| Web UI | 📋 Planned | Browser-based admin panel |
| Automated Backups | 📋 Planned | Encrypted backup to remote |
| Health Checks | 📋 Planned | Automated alerting |
| Feature | Status | Description |
|---|---|---|
| Pre-built ARM64 Binaries | ✅ v0.7 | Skip 60min build time |
| GPG Signed Releases | 📋 Planned | Verify authenticity |
| Docker Images | 📋 Planned | Container deployment |
| Ansible Playbooks | 📋 Planned | Automated setup |
| Feature | Status | Description |
|---|---|---|
| Stress Testing Framework | 📋 Planned | Load testing tools |
| Penetration Testing Guide | 📋 Planned | Security audit checklist |
| Threat Model Documentation | ✅ v0.7 | Detailed security analysis |
No Warranty: This software is provided "AS IS" without warranty of any kind, express or implied. The authors make no representations about the suitability of this software for any particular purpose.
No Guarantee of Security: While this guide aims to enhance privacy through Tor integration, no system is 100% secure. The authors do not guarantee that this configuration will protect against all threats, attacks, or surveillance methods.
Experimental Features: Features marked as "experimental" (such as Multi-SMP with 10 servers) may be unstable and are not recommended for production use without thorough testing.
Legal Compliance: Users are responsible for ensuring their use of this software complies with all applicable local, state, national, and international laws and regulations.
Limitation of Liability: In no event shall the authors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages arising from the use of this software.
Your Responsibility: By using this software, you accept full responsibility for:
- Proper configuration and maintenance
- Security of your systems and data
- Compliance with applicable laws
- Any consequences of system failures or security breaches
No Legal Advice: This documentation does not constitute legal advice. Consult with a qualified legal professional regarding the legality of operating anonymous infrastructure in your jurisdiction.
Third-Party Software: This guide integrates with third-party software (Tor, SimpleX) which have their own licenses, terms of service, and security considerations. The authors are not responsible for issues arising from third-party components.
🆕 Added:
- Section 4 expanded: Dual Tor Architecture now part of Core Setup (not optional!)
- Appendix F: Reference & Troubleshooting for Dual Tor (background info, verification, upgrade path)
- Stability Warning: Multi-SMP (10 servers) marked experimental (recommend 1-3 for production)
- Disclaimer: Legal and liability information added
- Architecture diagrams showing Dual Tor setup
🔧 Fixed:
socks_mode: onioninstead ofalways(major stability fix!)- systemd dependencies now include
tor@tor2.service - All sed commands updated to properly handle socks_mode
- Structure fix: Dual Tor moved from optional Appendix to required Core Setup
📝 Updated:
- Section 4: Now installs BOTH Tor instances (tor@default + tor@tor2)
- Section 5: Clarified SOCKSPort 0 purpose (references Section 4)
- Section 7.1: socks_mode: onion emphasis
- Section 9: tor@tor2 systemd dependencies
- Section 11: Verification for both Tor instances
- Appendix A: Experimental warning, 1-3 server recommendation, updated architecture diagram
- Appendix F: Changed from "Setup Guide" to "Reference & Troubleshooting"
- Troubleshooting: SocksErrorGeneralServerFailure fix
- Quick Reference: Commands for both Tor instances
- Roadmap: Updated status for Dual Tor and socks_mode fix
- Security Model: Added Isolation layer
- ADDED: Appendix E - Tor Watchdog (automatic recovery from connectivity issues)
- ADDED: Known Issue banner about Tor connectivity fluctuations
- ADDED: Troubleshooting section for Tor connectivity issues (guard nodes, circuit timeouts)
- ADDED: Troubleshooting for partial server connectivity
- UPDATED: Security Model table with Availability row
- UPDATED: Threat Model with Tor network instability consideration
- UPDATED: Table of Contents with Appendix E
- UPDATED: Roadmap with Tor Watchdog status
- FIXED: CRITICAL - SOCKS proxy must be in
[PROXY]section, NOT[TRANSPORT] - FIXED: Private Message Routing now works correctly with Tor-only servers
- UPDATED: Section 7.1 with correct SOCKS proxy configuration
- UPDATED: Appendix A.7 with correct Multi-SMP SOCKS configuration
- UPDATED: Troubleshooting section with detailed Private Routing error fixes
- ADDED: Client-side Private Routing configuration instructions (Section 12.3)
- ADDED: Verification commands to check
[PROXY]section configuration
- ADDED: Pre-built ARM64 binaries (Option A in Section 6)
- ADDED: SHA256 checksum verification
- ADDED: Closed User Group Architecture documentation
- ADDED: Server configuration guide (Closed vs. Hybrid mode)
- ADDED: Appendix B - SSH over Tor (Tor-only administration)
- ADDED: Threat Model & Limitations section
- ADDED: GitHub badges for project status
- ADDED: Discovery problem explanation
- UPDATED: Section 6 restructured with two installation options
- UPDATED: Entire guide refocused on closed user group concept
- UPDATED: Hidden Services count to 12 (added SSH)
- UPDATED: Firewall configuration for Tor-only access
- UPDATED: Troubleshooting with checksum verification errors
- ADDED: Comprehensive introduction with use cases
- ADDED: SOCKS proxy configuration for Private Routing
- ADDED: Roadmap section with future development plans
- IMPROVED: Troubleshooting section with Private Routing errors
- ADDED: Appendix A - Multi-SMP for Private Message Routing (10 servers)
- ADDED: Optional Hardening section structure
- FIXED: sed command now handles both
https: onandhttps: 443formats - FIXED: Explicit
mkdirfor files directory in section 8.2 - ADDED: Verification step for files directory
- ADDED:
binutils-goldto dependencies (fixes linker error) - ADDED:
CAP_NET_BIND_SERVICEfor XFTP (fixes port 443 binding) - CHANGED: SMP uses port 5223 only (avoids comma in client link)
- ADDED: Explicit
chmod 600for key files - ADDED: Comprehensive troubleshooting section
- Fixed port conflict between SMP and XFTP
- Initial release
This guide is released under AGPL-3.0.
SimpleX software is licensed under AGPL-3.0.
Pre-built binaries are unmodified builds from simplex-chat/simplexmq.
Found a bug? Have an improvement?
- Open an issue on GitHub
- Submit a pull request
- Contact via SimpleX (server addresses available on request)
Built with 🔐 by cannatoshi
"The best hiding place is one nobody knows exists."
"Two Tors are better than one." 🧅🧅