Restructure around a single entry point (automations.sh) with a Gum wizard and a self-extracting bundle for repo-less installs. Add scripts/oslib.sh so the provisioning scripts (setup-host, harden-ssh, harden-jumphost, sshuser) run on Alpine/Debian/Alma; seed root keys from globals/. - ntfy SSH-login alerts (user, source IP, key, region, jump target) via pam_exec - daily auto-updates with AUTO_REBOOT=idle (reboots only when no SSH active) and opt-in Alpine stable-branch upgrades - generic + per-deployment cloud-init; Gitea release workflow on tag - README/LICENSE/.gitignore/.gitattributes (force LF); repo URLs -> Gitea
SimpleX Relay Server Deployment
Complete automated deployment for SimpleX Chat relay servers on Alpine Linux with post-quantum SSH hardening, Tor hidden services, and encrypted backups.
Quick Start
1. Clone This Repository
The installer fetches its scripts straight from this monorepo's layout:
automations/
├── scripts/
│ └── harden-ssh.sh # generic SSH hardening (PQ KEX + Ed25519)
├── deployments/simplex/
│ ├── deploy-simplex.sh # SMP + XFTP + Tor deployment
│ ├── backup.sh # age-encrypted backup creation
│ ├── restore.sh # disaster recovery from backup
│ ├── install-simplex.sh # master installer (cloud-init compatible)
│ ├── cloud-init.yml # example cloud-init configuration
│ └── README.md # this file
└── globals/
└── age-pubkey.txt # your age public key(s) for backups
Point REPO_URL at your fork of this repo. The installer resolves
harden-ssh.sh under scripts/ and the simplex scripts under
deployments/simplex/ automatically (overridable via HARDEN_PATH /
SIMPLEX_PATH).
2. Generate Backup Keys
# Generate age keypair for backups
age-keygen -o backup-private-key.txt
# Save the public key to the shared globals/ folder
echo "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p" > globals/age-pubkey.txt
# Store the private key securely (NOT in your repo)
cp backup-private-key.txt ~/safe-location/
3. Deploy via Cloud-Init
Create a cloud instance with this user-data:
#cloud-config
runcmd:
- |
curl -fsSL https://git.anomalous.dev/57_Wolve/automations/raw/branch/main/deployments/simplex/install-simplex.sh | \
REPO_URL=https://git.anomalous.dev/57_Wolve/automations.git \
DOMAIN=relay.example.com \
ACME_EMAIL=admin@example.com \
XFTP_QUOTA=100gb \
SSH_PORT=2222 \
ALLOWED_IP=1.2.3.4 \
bash
4. Manual Deployment
# On a fresh Alpine Linux host:
curl -fsSL https://git.anomalous.dev/57_Wolve/automations/raw/branch/main/deployments/simplex/install-simplex.sh | \
REPO_URL=https://git.anomalous.dev/57_Wolve/automations.git \
DOMAIN=relay.example.com \
ACME_EMAIL=admin@example.com \
XFTP_QUOTA=50gb \
SSH_PORT=2222 \
bash
Configuration Options
| Variable | Required | Default | Description |
|---|---|---|---|
REPO_URL |
✅ | Git repository URL containing deployment scripts | |
DOMAIN |
✅ | Apex domain (creates smp.DOMAIN, xftp.DOMAIN) | |
ACME_EMAIL |
✅ | Email for Let's Encrypt registration | |
XFTP_QUOTA |
50gb | Disk quota for file storage | |
SSH_PORT |
22 | SSH port (recommend changing from default) | |
ALLOWED_IP |
Your IP to whitelist in sshguard | ||
KEY_TYPE |
rsa4096 | Caddy TLS key type (rsa4096, p384, ed25519) | |
SMP_PASS |
Optional password for SMP queue creation | ||
XFTP_PASS |
Optional password for XFTP uploads | ||
SKIP_PROMPTS |
0 | Set to 1 for non-interactive installation |
What Gets Deployed
Security Features
- Post-quantum SSH: Hybrid KEX (mlkem768x25519, sntrup761x25519) + Ed25519 keys
- Minimal attack surface: Only SSH terminal + SFTP, no forwarding/tunnels
- Firewall: awall with explicit allow-list for required ports only
- Brute-force protection: sshguard with progressive IP bans
SimpleX Infrastructure
- SMP server: Message relay on port 5223
- XFTP server: File transfer on port 5443
- Tor hidden services: .onion addresses for both servers
- TLS termination: Caddy with Let's Encrypt and strong crypto
- Docker compose: Orchestrated deployment with health checks
Operational Features
- Encrypted backups: All private keys backed up with age encryption
- CA key removal: Private keys removed from disk after backup
- Service monitoring: OpenRC integration with auto-restart
- Address discovery: Scripts to show server fingerprints and .onion URLs
Server Addresses
After deployment, your relay will be accessible at:
# Clearnet
smp://FINGERPRINT@smp.yourdomain.com
xftp://FINGERPRINT@xftp.yourdomain.com
# Tor (clients with Orbot/Tor)
smp://FINGERPRINT@smp.yourdomain.com,ONIONADDRESS.onion
xftp://FINGERPRINT@xftp.yourdomain.com,ONIONADDRESS.onion
Get the full addresses with: cd /opt/simplex && ./print-addresses.sh
Backup & Recovery
Creating Backups
# Manual backup (on the server)
AGE_RECIPIENT_FILE=/path/to/age-pubkey.txt bash backup.sh
# Or with a single recipient
AGE_RECIPIENT=age1ql3z7... bash backup.sh
Disaster Recovery
# 1. Fresh Alpine install
# 2. Upload restore script and backup
scp restore.sh backup-20250101-120000.tar.gz.age root@new-host:/root/
# 3. Restore (preserves all server identities)
ssh root@new-host
bash restore.sh backup-20250101-120000.tar.gz.age
# 4. Update DNS to point at new host
DNS Configuration
Create these DNS records before deployment:
smp.yourdomain.com. 300 IN A 1.2.3.4
xftp.yourdomain.com. 300 IN A 1.2.3.4
Replace 1.2.3.4 with your server's IP address.
Architecture
Internet
↓
[Caddy :80,:443] ← Let's Encrypt ACME
↓
[Docker Network]
├─ [SMP Server :5223] ← Tor Hidden Service
└─ [XFTP Server :5443] ← Tor Hidden Service
- Ports 80/443: Caddy (HTTP redirect + ACME + info pages)
- Port 5223: SMP protocol (TCP, server's own TLS)
- Port 5443: XFTP protocol (TCP, server's own TLS)
- Port 22/2222: SSH (PQ KEX + Ed25519 only)
File Locations
- Deployment:
/opt/simplex/(docker-compose.yml, configs, scripts) - Server keys:
/opt/simplex/{smp,xftp}_configs/(CA keys removed after backup) - Tor keys:
/opt/simplex/{smp,xftp}_tor/(hidden service identity) - SSH config:
/etc/ssh/(hardened sshd_config + Ed25519 host key) - Firewall:
/etc/awall/optional/(JSON policy files) - Backups:
/tmp/simplex-backup-YYYYMMDD-HHMMSS.tar.gz.age
Security Notes
What's Quantum-Safe
- Session keys: PQ hybrid KEX protects against "store now, decrypt later"
- Authentication: Ed25519 keys (classical, but strongest practical choice today)
Network Hardening
- Only required ports open (22/2222, 80, 443, 5223, 5443)
- SSH locked to key-only auth, no forwarding, rate-limited
- sshguard blocks brute-force attempts with progressive delays
Key Management
- CA private keys backed up encrypted, then removed from disk
- Tor onion keys preserved for stable .onion addresses
- SSH host key preserved for stable fingerprint
- All backups encrypted with age (modern, simple, secure)
Troubleshooting
Check Service Status
cd /opt/simplex
docker compose ps # Container status
docker compose logs -f smp-server # SMP logs
docker compose logs -f xftp-server # XFTP logs
./print-addresses.sh # Show server addresses
SSH Issues
# Test SSH config
sshd -t
# View SSH attempts
journalctl -u sshd -f
# Check sshguard blocks
iptables -L sshguard -n
Firewall Issues
awall list # Show firewall policies
awall translate # Test policy compilation
iptables -L -n # Show active rules
Certificate Issues
docker exec simplex-caddy caddy list-certificates
docker logs simplex-caddy
Contributing
- Fork this repository
- Make your changes
- Test on a fresh Alpine instance
- Submit a pull request
License
This deployment is provided as-is for educational and operational use. The SimpleX Chat software itself is licensed under AGPL-3.0.