Early-stage software. Shurli is experimental and built with AI assistance. It will have bugs. Not recommended for production or safety-critical use. Read the disclaimer.
Relay Setup

Relay Setup

Complete guide to deploying the relay server on a fresh VPS (Ubuntu 22.04 / 24.04).

1. Initial VPS Setup

SSH into your fresh VPS and run the install script:

ssh root@YOUR_VPS_IP

# Short URL
curl -sSL get.shurli.io | sh

# Or use the full GitHub URL directly
curl -sSL https://raw.githubusercontent.com/shurlinet/shurli/dev/tools/install.sh | sh

Choose “Relay server” when prompted. The script handles everything: downloads the binary, verifies checksums, creates a secure service user, configures firewall, installs systemd service, and starts the relay.

For pre-release builds, set the environment variable before sh:

curl -sSL <URL> | SHURLI_DEV=1 sh
Alternative: clone repo and run setup script manually
apt update && apt upgrade -y
apt install -y git ufw

ufw allow OpenSSH
ufw default deny incoming
ufw default allow outgoing
ufw enable

git clone https://github.com/shurlinet/shurli.git
cd shurli
bash tools/relay-setup.sh

What the setup does

It will:

  1. Ask you to select an existing user or create a new one
  2. New user: creates the account, sets a password, copies your SSH keys, locks down home directory (700), and offers to harden SSH (disable password auth + root login)
  3. Existing user: audits sudo group, password, SSH keys, directory permissions, and SSH daemon config, and offers to fix any issues
  4. Continues with the full setup: builds binary, installs to /usr/local/bin/shurli, creates data directory at /etc/shurli/relay/, installs systemd service, starts the relay in foreground for first-time identity setup (create new or recover from existing seed phrase), enables the service, configures firewall, tunes QUIC buffers

If creating a new user, test SSH access in a separate terminal before closing the root session:

ssh newuser@YOUR_VPS_IP
sudo whoami    # should print: root

Configure

Relay commands auto-detect the config at /etc/shurli/relay/ and auto-escalate to the service user when needed, so you can run them from any directory as any sudo-capable user:

# These work from any directory, as any user with sudo access
shurli relay info
shurli relay invite create --ttl 24h

# Edit config if needed (defaults are good - port 7777, gating enabled)
sudo nano /etc/shurli/relay/relay-server.yaml

Then restart the service to pick up config changes (config-level changes like ports, transport, and ZKP enablement require a restart):

sudo systemctl restart shurli-relay

Note: Auth changes (shurli relay authorize / shurli relay deauthorize) apply immediately via live reload - no restart needed.

Add peers to the relay

Option A: Invite codes (recommended)

Generate invite codes and share them with your peers:

# Generate an invite code (valid for 24 hours)
shurli relay invite create --ttl 24h

# Each person joins with one command on their device:
shurli join <invite-code> --relay <IP:PORT>
# Peer ID: <shown in invite output>

The join --relay command handles everything on a fresh device: creates identity (new or recover from seed phrase), sets password, writes config, connects to relay, starts daemon, and offers systemd service installation on Linux.

Invite codes handle authorization automatically. Everyone who joins through the same relay is mutually authorized and can verify each other with shurli verify <name>.

# List active invites
shurli relay invite list

# Revoke an invite
shurli relay invite revoke <group-id>

Option B: Manual authorization

If you already know the peer IDs, add them directly:

# Using the CLI (works from any directory)
shurli relay authorize <peer-id> --comment "home-node"
shurli relay authorize <peer-id> --comment "client-node"

# Or edit the file directly (one peer ID per line)
sudo nano /etc/shurli/relay/relay_authorized_keys
12D3KooWARqzAAN9es44ACsL7W82tfbpiMVPfSi1M5czHHYPk5fY  # home-node
12D3KooWNq8c1fNjXwhRoWxSXT419bumWQFoTbowCwHEa96RJRg6  # client-node

After manual edits, trigger a live reload:

shurli relay authorize <any-peer-id>   # triggers reload of the full file
# Or restart if you prefer:
sudo systemctl restart shurli-relay

2. Verify

Run the health check anytime:

cd ~/shurli
bash tools/relay-setup.sh --check

You should see all [OK] items:

Binary:
  [OK]   shurli binary exists at /usr/local/bin/shurli
  [OK]   shurli is executable

  [OK]   Data directory permissions: 700

Configuration:
  [OK]   relay-server.yaml exists
  [OK]   Connection gating is ENABLED
  [OK]   relay-server.yaml permissions: 600
  ...

Service:
  [OK]   shurli-relay service is enabled (starts on boot)
  [OK]   shurli-relay service is running
  [OK]   Service runs as non-root user: peerup
  ...

=== Summary: 21 passed, 1 warnings, 0 failures ===
All good, but review [WARN] items for best security.

3. Uninstall

To remove the systemd service, firewall rules, and system tuning:

cd ~/shurli
bash tools/relay-setup.sh --uninstall

This removes:

  • The systemd service (stopped, disabled, file deleted)
  • Firewall rules for port 7777
  • QUIC buffer tuning from /etc/sysctl.conf
  • Journald log rotation settings

It does not delete config, keys, or source code. To fully clean up:

sudo rm -rf /etc/shurli/relay  # Remove relay data
sudo rm /usr/local/bin/shurli  # Remove binary
rm -rf ~/shurli                # Remove source (only if you want to)

4. Useful Commands

# Service management
sudo systemctl status shurli-relay
sudo systemctl restart shurli-relay
sudo systemctl stop shurli-relay

# Follow logs
sudo journalctl -u shurli-relay -f

# Recent logs (last 50 lines)
sudo journalctl -u shurli-relay -n 50

# Check log disk usage
sudo journalctl --disk-usage

# Update relay server (after code changes)
cd ~/shurli
git pull
make install-relay
sudo systemctl restart shurli-relay

5. Security Checklist

ItemHow to verify
Non-root userwhoami (should NOT be root)
SSH key-only logingrep PasswordAuthentication /etc/ssh/sshd_config - no
Root login disabledgrep PermitRootLogin /etc/ssh/sshd_config - no
Firewall activesudo ufw status - active, default deny incoming
Only needed ports opensudo ufw status - 22/tcp (SSH) + 7777/tcp+udp
Connection gating ongrep enable_connection_gating /etc/shurli/relay/relay-server.yaml - true
Data directory permissionsls -ld /etc/shurli/relay/ - drwx------ (700, owner only)
Key file permissionsls -la /etc/shurli/relay/relay_node.key - -rw------- (600)
Log rotationgrep SystemMaxUse /etc/systemd/journald.conf - 500M
System updatessudo apt update && sudo apt upgrade

Or just run: bash tools/relay-setup.sh --check


File Layout

After setup, the relay data and binary are installed to system paths:

/usr/local/bin/shurli                    # Binary (single binary for all modes)

/etc/shurli/relay/                       # Data directory (700, owner only)
  relay-server.yaml                      # Config (created by shurli relay setup)
  relay_node.key                         # Identity key (auto-generated on first run)
  relay_vault.json                       # Sealed vault (auto-created on first run)
  relay_authorized_keys                  # Allowed peer IDs
  .session                               # Session token (machine-bound)
  .relay-admin.sock                      # Admin Unix socket (runtime)
  .relay-admin.cookie                    # Admin auth cookie (runtime)
  .relay-server.last-good.yaml           # Self-healing: last valid config backup

Source code (where you cloned the repo):

~/shurli/
  tools/relay-setup.sh             # Setup + health check script
  deploy/shurli-relay.service      # systemd service file
  configs/relay-server.sample.yaml # Sample configuration

Manual Installation (Any Linux/Unix)

The tools/relay-setup.sh script targets Debian/Ubuntu with apt and ufw. If you run a different distribution, or prefer to set things up yourself, here’s everything the script does broken into individual steps.

Prerequisites

Go (version from go.mod, currently 1.26.0+):

# Download from https://go.dev/dl/
wget https://go.dev/dl/go1.26.0.linux-amd64.tar.gz
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.26.0.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin

Native mDNS library (optional, enables LAN peer discovery):

DistributionPackageInstall
Debian/Ubuntulibavahi-compat-libdnssd-devsudo apt install libavahi-compat-libdnssd-dev
Fedora/RHEL/CentOSavahi-compat-libdns_sd-develsudo dnf install avahi-compat-libdns_sd-devel
Arch Linuxavahisudo pacman -S avahi
Alpine Linuxavahi-compat-libdns_sdsudo apk add avahi-compat-libdns_sd avahi-dev
openSUSEavahi-compat-mDNSResponder-develsudo zypper install avahi-compat-mDNSResponder-devel
FreeBSDavahi-libdnssudo pkg install avahi-libdns
macOSBuilt-inNo install needed (dns_sd is in libSystem)

Without this library, Shurli falls back to pure-Go mDNS (works but less reliable on systems with a running mDNS daemon). Build with CGO_ENABLED=0 to explicitly use the fallback.

QR code tool (optional, for relay-setup.sh --check display):

DistributionPackage
Debian/Ubuntuqrencode
Fedora/RHELqrencode
Archqrencode
Alpinelibqrencode
macOSbrew install qrencode

Build and install

git clone https://github.com/shurlinet/shurli.git
cd shurli

# Build and install to system paths
make install-relay

# This does:
#   1. Builds the binary with version embedding
#   2. Installs to /usr/local/bin/shurli
#   3. Creates /etc/shurli/relay/ with config files
#   4. Installs the systemd service (does not enable or start)

Configure

# Edit if needed (defaults: port 7777, gating enabled)
sudo nano /etc/shurli/relay/relay-server.yaml

Service user (if not using the setup script)

The relay runs as whatever user you choose. You can use your existing SSH user or create a dedicated one:

# Option A: Use your existing user (recommended for single-admin VPS)
# No extra setup needed - make install-relay defaults to your user

# Option B: Create a dedicated user
sudo useradd -r -m -s /bin/bash shurli
sudo chown -R shurli:shurli /etc/shurli/relay
sudo chmod 700 /etc/shurli/relay
make install-relay SERVICE_USER=shurli

Network tuning (QUIC performance)

# Increase UDP buffer sizes for QUIC
echo 'net.core.rmem_max=7500000' | sudo tee -a /etc/sysctl.conf
echo 'net.core.wmem_max=7500000' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Firewall

Open port 7777 for TCP and UDP:

FirewallCommands
ufw (Ubuntu)sudo ufw allow 7777/tcp && sudo ufw allow 7777/udp
firewalld (Fedora/RHEL)sudo firewall-cmd --permanent --add-port=7777/tcp && sudo firewall-cmd --permanent --add-port=7777/udp && sudo firewall-cmd --reload
iptablessudo iptables -A INPUT -p tcp --dport 7777 -j ACCEPT && sudo iptables -A INPUT -p udp --dport 7777 -j ACCEPT
nftablesAdd tcp dport 7777 accept and udp dport 7777 accept to your input chain
pf (FreeBSD/macOS)Add pass in proto { tcp, udp } to port 7777 to /etc/pf.conf

Also check your VPS provider’s security groups/firewall rules in their web console.

Install systemd service (manual)

sudo cp deploy/shurli-relay.service /etc/systemd/system/shurli-relay.service

# Update User/Group to match your service user (default in file is "shurli")
sudo sed -i 's/^User=.*/User=YOUR_USER/' /etc/systemd/system/shurli-relay.service
sudo sed -i 's/^Group=.*/Group=YOUR_USER/' /etc/systemd/system/shurli-relay.service

sudo systemctl daemon-reload
sudo systemctl enable --now shurli-relay

For non-systemd systems (Alpine with OpenRC, FreeBSD with rc.d, etc.), create a service script that runs:

/usr/local/bin/shurli relay serve --config /etc/shurli/relay/relay-server.yaml

The binary is self-contained. It reads config from the working directory or the path specified by --config.

Log rotation

For systemd systems, limit journal size:

sudo mkdir -p /etc/systemd/journald.conf.d
echo -e "[Journal]\nSystemMaxUse=500M\nMaxFileSec=7day" | sudo tee /etc/systemd/journald.conf.d/shurli-relay.conf
sudo systemctl restart systemd-journald

Verify

# Check service is running
sudo systemctl status shurli-relay

# Check it's listening
ss -tlnp | grep 7777

# Check logs
sudo journalctl -u shurli-relay -n 20

# Or run the health check script (even on non-Ubuntu, it reports useful info)
bash tools/relay-setup.sh --check

Troubleshooting

IssueSolution
Service fails to startsudo journalctl -u shurli-relay -n 30 for error logs
“Permission denied” on key filechmod 600 /etc/shurli/relay/relay_node.key
Peers can’t connectUse shurli relay invite create to generate invite codes, or check relay_authorized_keys has their peer IDs
Random peers connectingVerify enable_connection_gating: true in config
High log disk usagesudo journalctl --vacuum-size=200M to trim now
Port not reachablesudo ufw status and check VPS provider firewall/security group
“Permission denied” on relay commandsRelay commands auto-escalate to the config owner via sudo. Ensure your user has passwordless sudo or enter the password when prompted.
Service runs as rootbash tools/relay-setup.sh --uninstall then re-run bash tools/relay-setup.sh (as root it will guide you through user setup)
dns_sd.h: No such file or directoryInstall the avahi compat library for your distro (see Prerequisites above)
CGo build fails / not wantedBuild with CGO_ENABLED=0 go build ... to use pure-Go fallback (no native mDNS)