- README.md: full project overview with features, install extras, CLI reference, web UI routes, config table, architecture diagrams, security model, /health API, and development setup - CLAUDE.md: updated for monorepo — reflects inlined subpackages, new import patterns, pip extras, and added modules - docs/deployment.md: practical RPi deployment guide covering hardware, OS setup, security hardening (swap/coredumps/firewall), installation, systemd service, config reference, fieldkit setup, key management, operational security limitations, troubleshooting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
17 KiB
SooSeF Deployment Guide
Deploying SooSeF on a Raspberry Pi for airgapped LAN use.
This guide is for field deployers: IT staff at NGOs, technically competent journalists, and anyone setting up a shared SooSeF instance on a local network with no internet access.
1. Hardware Requirements
Minimum:
- Raspberry Pi 4 Model B, 2 GB RAM (4 GB recommended)
- 32 GB microSD card (Class 10 / A2 recommended)
- USB-C power supply (5V 3A, official RPi PSU recommended)
- Ethernet cable or pre-configured Wi-Fi for LAN
Optional:
- USB GPS module (e.g., VK-162) for geofencing
- Momentary push button + 10k pull-down resistor on GPIO pin 17 for hardware killswitch
- USB drive for airgap package transfer and key backups
- Case with passive cooling (no fan noise in sensitive environments)
If you plan to use the hardware killswitch button, wire it between GPIO 17 and 3.3V with
a 10k pull-down to GND. The default requires a 5-second hold to trigger (configurable via
gpio_killswitch_hold_seconds in config).
2. OS Setup
Install Raspberry Pi OS Lite (64-bit, Bookworm or later). The desktop environment is unnecessary and wastes resources.
After first boot:
# Set hostname
sudo hostnamectl set-hostname soosef-node
# Create a dedicated service user
sudo useradd -m -s /bin/bash soosef
sudo passwd soosef
# Enable SSH (if not already)
sudo systemctl enable --now ssh
# Update system packages (do this before going airgapped)
sudo apt update && sudo apt upgrade -y
Set a strong password. If possible, use SSH key authentication and disable password login
in /etc/ssh/sshd_config (PasswordAuthentication no).
3. Security Hardening
3.1 Disable or encrypt swap
Python does not zero memory when objects are garbage-collected. Cryptographic keys, passwords, and plaintext can persist in swap long after the process exits. On an SD card, this data may survive even after the swap file is deleted (wear leveling).
Option A: Disable swap entirely (recommended if you have 4 GB+ RAM)
sudo dphys-swapfile swapoff
sudo systemctl disable dphys-swapfile
sudo rm /var/swap
Option B: Encrypted swap (if you need swap on 2 GB models)
sudo dphys-swapfile swapoff
sudo systemctl disable dphys-swapfile
# Use dm-crypt with a random key (regenerated each boot)
echo "swap /dev/mmcblk0p3 /dev/urandom swap,cipher=aes-xts-plain64,size=256" | sudo tee -a /etc/crypttab
echo "/dev/mapper/swap none swap sw 0 0" | sudo tee -a /etc/fstab
Adjust the device path to match your partition layout.
3.2 Disable core dumps
A core dump from the SooSeF process would contain key material in plaintext.
echo "* hard core 0" | sudo tee -a /etc/security/limits.conf
echo "kernel.core_pattern=/dev/null" | sudo tee -a /etc/sysctl.d/99-soosef.conf
sudo sysctl -p /etc/sysctl.d/99-soosef.conf
3.3 Firewall
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 5000/tcp # SooSeF web UI
sudo ufw enable
If running fully airgapped, also deny outgoing:
sudo ufw default deny outgoing
3.4 Disable unnecessary services
sudo systemctl disable bluetooth
sudo systemctl disable avahi-daemon
sudo systemctl disable triggerhappy
4. Installation
4.1 System dependencies
sudo apt install -y \
python3 python3-pip python3-venv python3-dev \
libjpeg-dev libjpeg62-turbo-dev zlib1g-dev \
libffi-dev libssl-dev \
shred coreutils
libjpeg-dev is required for Pillow and jpeglib (DCT steganography). libffi-dev is
required for argon2-cffi.
4.2 Create virtual environment
sudo -u soosef -i # Switch to the soosef user
python3 -m venv ~/soosef-env
source ~/soosef-env/bin/activate
pip install --upgrade pip wheel
4.3 Install SooSeF
If the Pi has internet access (pre-deployment):
pip install "soosef[web,cli,fieldkit]"
For hardware killswitch support via GPIO:
pip install "soosef[rpi]"
The rpi extra includes web, cli, fieldkit, and gpiozero.
For airgapped install (no internet on the Pi):
On an internet-connected machine with the same architecture (aarch64 for RPi 4):
mkdir soosef-wheels
pip download "soosef[rpi]" -d soosef-wheels/
Copy the soosef-wheels/ directory to a USB drive, then on the Pi:
pip install --no-index --find-links /media/usb/soosef-wheels/ "soosef[rpi]"
4.4 Verify installation
soosef --version
5. Initial Setup
5.1 Initialize SooSeF
soosef init
This creates the ~/.soosef/ directory structure:
~/.soosef/
config.json Unified configuration
identity/ Ed25519 signing keypair (verisoo)
private.pem
public.pem
identity.meta.json
stegasoo/ Stegasoo state
channel.key AES-256-GCM channel key
attestations/ Verisoo attestation log and index
chain/ Hash chain data
auth/ Web UI user database (SQLite)
certs/ Self-signed TLS certificates
fieldkit/ Killswitch, deadman, tamper, USB, geofence state
temp/ Ephemeral upload/processing files
instance/ Flask session data
audit.jsonl Append-only audit trail
The identity/ and auth/ directories are created with mode 0700.
soosef init generates:
- An Ed25519 identity keypair (for signing attestations)
- A channel key (for steganographic encoding)
- A default
config.json
5.2 First user setup
Start the server and create the first admin user through the web UI:
soosef serve --host 0.0.0.0 --no-https
Navigate to http://<pi-ip>:5000 from a device on the same LAN. The web UI will prompt
you to create the first user account.
After creating the admin account, stop the server (Ctrl+C) and restart with HTTPS (see Section 6).
6. Running
6.1 Basic usage
# LAN-only, no HTTPS (acceptable if the network is physically isolated)
soosef serve --host 0.0.0.0 --no-https
# With self-signed HTTPS (recommended)
soosef serve --host 0.0.0.0
# Custom port
soosef serve --host 0.0.0.0 --port 8443
On first HTTPS start, SooSeF auto-generates a self-signed certificate at
~/.soosef/certs/cert.pem. Browsers will show a certificate warning -- this is expected
for self-signed certs. Instruct users to accept the warning or distribute the cert file
to client devices.
SooSeF uses Waitress (pure Python, no C dependencies) as its production server with 4
worker threads by default. Adjust with --workers.
6.2 systemd service
Create /etc/systemd/system/soosef.service:
[Unit]
Description=SooSeF Security Fieldkit
After=network.target
[Service]
Type=simple
User=soosef
Group=soosef
WorkingDirectory=/home/soosef
Environment="PATH=/home/soosef/soosef-env/bin:/usr/bin"
ExecStart=/home/soosef/soosef-env/bin/soosef serve --host 0.0.0.0 --workers 4
Restart=on-failure
RestartSec=5
# Hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/soosef/.soosef
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now soosef
sudo journalctl -u soosef -f # Watch logs
Add --no-https to ExecStart if running on a physically isolated LAN where TLS is
unnecessary.
7. Configuration
Configuration lives at ~/.soosef/config.json. Edit it directly or use the web admin
panel. All fields have sensible defaults -- you only need to set what you want to change.
| Field | Default | Description |
|---|---|---|
host |
127.0.0.1 |
Bind address. Set to 0.0.0.0 for LAN access. |
port |
5000 |
TCP port for the web UI. |
https_enabled |
true |
Enable self-signed HTTPS. |
auth_enabled |
true |
Require login. Do not disable this. |
max_upload_mb |
50 |
Maximum file upload size in MB. |
session_timeout_minutes |
15 |
Idle session expiry. Lower is safer. |
login_lockout_attempts |
5 |
Failed logins before lockout. |
login_lockout_minutes |
15 |
Lockout duration. |
killswitch_enabled |
false |
Enable software killswitch. |
deadman_enabled |
false |
Enable dead man's switch. |
deadman_interval_hours |
24 |
Hours between required check-ins. |
deadman_grace_hours |
2 |
Grace period after missed check-in before purge. |
deadman_warning_webhook |
"" |
URL to POST a JSON warning when check-in is overdue. Must be a public URL (SSRF protection blocks private IPs). |
usb_monitoring_enabled |
false |
Monitor for unauthorized USB devices. |
tamper_monitoring_enabled |
false |
File integrity monitoring. |
chain_enabled |
true |
Wrap attestations in a hash chain. |
chain_auto_wrap |
true |
Automatically chain verisoo attestations. |
backup_reminder_days |
7 |
Warn if no backup in this many days. |
gpio_killswitch_pin |
17 |
GPIO pin for hardware killswitch button. |
gpio_killswitch_hold_seconds |
5.0 |
Required hold time to trigger hardware killswitch. |
Example minimal config for a field deployment:
{
"host": "0.0.0.0",
"port": 5000,
"https_enabled": true,
"session_timeout_minutes": 10,
"deadman_enabled": true,
"deadman_interval_hours": 12,
"deadman_grace_hours": 1,
"killswitch_enabled": true,
"backup_reminder_days": 3
}
8. Fieldkit Setup
8.1 Dead man's switch
The dead man's switch requires periodic check-ins. If you miss a check-in, SooSeF sends a warning during the grace period. If the grace period expires without a check-in, the killswitch fires automatically and destroys all key material and data.
Arm it:
soosef fieldkit deadman arm --interval 12 --grace 1
This requires a check-in every 12 hours, with a 1-hour grace period.
Check in:
soosef fieldkit checkin
You can also check in through the web UI at /fieldkit.
Check status:
soosef status
The dead man's switch enforcement loop runs as a background thread inside soosef serve,
checking every 60 seconds. It will send a webhook warning (if configured) during the grace
period, then execute a full purge if the grace period expires.
Disarm:
soosef fieldkit deadman disarm
8.2 Geofence
If you have a USB GPS module, you can set a geographic boundary. SooSeF will trigger the killswitch if the device moves outside the fence.
soosef fieldkit geofence set --lat 50.4501 --lon 30.5234 --radius 5000
Coordinates are in decimal degrees, radius in meters.
8.3 USB whitelist
Record currently connected USB devices as the trusted baseline:
soosef fieldkit usb learn
When monitoring is enabled, SooSeF will alert (or trigger killswitch, depending on config) if an unknown USB device is connected.
8.4 Tamper baseline
Record file integrity baselines for critical files:
soosef fieldkit tamper baseline
SooSeF monitors for unexpected changes to tracked files when tamper monitoring is enabled.
9. Key Management
SooSeF manages two separate key domains:
- Ed25519 identity key (
~/.soosef/identity/) -- used for signing attestations. This is your provenance identity. - AES-256-GCM channel key (
~/.soosef/stegasoo/channel.key) -- used for steganographic encoding/decoding. Shared with anyone who needs to read your stego messages.
These are separate security concerns and are never merged.
9.1 Backup
Back up keys regularly. SooSeF warns if no backup has been taken within the
backup_reminder_days window (default: 7 days).
soosef keys backup --output /media/usb/soosef-backup.enc
This creates an encrypted bundle. You will be prompted for a passphrase. Store the USB drive physically separate from the Pi.
9.2 Restore
soosef keys restore --input /media/usb/soosef-backup.enc
9.3 Key rotation
Generate a new channel key (the old one is overwritten):
soosef init --no-identity
Generate a new identity (the old one is overwritten -- all previous attestations will reference the old fingerprint):
soosef init --no-channel
After rotating keys, take a fresh backup immediately.
9.4 Trusting collaborator keys
Import a collaborator's public key so you can verify their attestations:
soosef keys trust --import /media/usb/collaborator-pubkey.pem
Verify the fingerprint out-of-band (in person, over a secure channel) before trusting.
10. Operational Security Notes
SooSeF is a tool, not a shield. Understand what it cannot do.
What SooSeF does not protect against
- Physical coercion. If someone forces you to unlock the device or reveal passwords, no software can help. The killswitch is for situations where you can act before interception, not during.
- Social engineering. SooSeF cannot prevent users from being tricked into revealing credentials or disabling security features.
- Leaving the browser open. The session timeout helps, but if someone walks up to an unlocked browser session, they have access. Train users to close the browser or lock the screen.
- Compromised client devices. SooSeF secures the server. If a user's laptop has malware, their browser session is compromised regardless of what the server does.
Shred limitations on flash storage
The killswitch uses shred on Linux (3-pass overwrite + zero). On spinning disks, this
is effective. On SD cards and SSDs, it is not reliable because:
- Flash translation layers remap physical blocks. Overwritten data may persist on remapped blocks.
- Wear leveling distributes writes across the flash, meaning the original block may be preserved.
SooSeF's defense against this is cryptographic erasure: destroy the keys first, then the data. Even if fragments of encrypted data survive on flash, they are useless without the keys. The killswitch destroys keys before anything else, and keys are small enough to fit in a single flash block.
This is why full-disk encryption matters -- see below.
Full-disk encryption (LUKS)
For serious deployments, encrypt the entire SD card (or at least the data partition) with LUKS. This way, even if the SD card is physically seized:
- All data at rest is encrypted
- Shred limitations are irrelevant because the underlying storage is encrypted
- Power-off = data inaccessible (assuming the LUKS passphrase is strong)
Setting up LUKS on Raspberry Pi OS is beyond the scope of this guide, but the short
version is: create an encrypted partition, mount it at /home/soosef/.soosef, and
configure auto-unlock via a keyfile on a separate USB (remove the USB after boot in
hostile environments).
Memory considerations
Python does not securely zero memory. Key material, passwords, and plaintext may persist in process memory and swap. The mitigations in Section 3 (disable swap, disable core dumps) reduce the window, but a memory dump of the running process would expose secrets. This is a fundamental limitation of Python-based security tools.
11. Troubleshooting
Health check
SooSeF exposes a /health endpoint on the web UI. Hit it from the LAN to verify the
server is running:
curl -k https://<pi-ip>:5000/health
The -k flag skips certificate verification for self-signed certs.
System status
soosef status
This checks identity key, channel key, trusted keys, dead man's switch state, geofence, chain status, and backup status.
Common issues
scipy fails to build on Raspberry Pi
scipy requires Fortran and BLAS libraries. On Raspberry Pi OS:
sudo apt install -y gfortran libopenblas-dev
If the build still fails (common on low-memory Pis), increase swap temporarily during install, then disable it again:
sudo dphys-swapfile swapon
pip install scipy
sudo dphys-swapfile swapoff
Or use pre-built wheels:
pip install --prefer-binary scipy
Argon2 OOM on low-memory Pi (2 GB)
Argon2 password hashing is memory-intensive by design. If the server crashes during login on a 2 GB Pi with other services running, either:
- Free memory by disabling unnecessary services
- Reduce Argon2 memory cost (not recommended -- weakens password hashing)
The default Argon2 parameters use ~64 MB per hash operation. With 2 GB total RAM and a running server, this is tight but workable if nothing else is competing for memory.
Web UI unreachable from LAN
- Check that
hostis set to0.0.0.0in config, not127.0.0.1 - Check firewall:
sudo ufw status-- port 5000 must be allowed - Check the service is running:
sudo systemctl status soosef - Check the Pi's IP:
ip addr show
Certificate warnings in browser
Expected with self-signed certificates. Users must click through the warning. To avoid
this, distribute ~/.soosef/certs/cert.pem to client devices and install it as a
trusted certificate.
Dead man's switch fires unexpectedly
The switch checks every 60 seconds. If the Pi loses power or the service crashes and
does not restart within interval_hours + grace_hours, the switch will fire on the next
start. Make sure the systemd service is set to Restart=on-failure and the Pi has
reliable power.
If you need to perform maintenance, disarm the switch first:
soosef fieldkit deadman disarm
Re-arm when maintenance is complete.
Permission errors on ~/.soosef/
The identity/, auth/, and certs/ directories are mode 0700. If running under a
different user than the one who ran soosef init, you will get permission denied errors.
Always run SooSeF as the same user.
ls -la ~/.soosef/