Add three-tier deployment infrastructure
Some checks failed
CI / lint (push) Failing after 55s
CI / typecheck (push) Failing after 31s

Platform pivot from Raspberry Pi to three-tier model:
- Tier 1: Bootable Debian Live USB for field reporters
- Tier 2: Docker/K8s org server for newsrooms
- Tier 3: Docker/K8s federation relay for VPS

Tier 1 — Live USB (deploy/live-usb/):
- build.sh: live-build based image builder for amd64
- Package list: Python + system deps + minimal GUI (openbox + Firefox)
- Install hook: creates venv, pip installs soosef[web,cli,attest,...]
- Hardening hook: disable swap/coredumps, UFW, auto-login to web UI
- systemd service with security hardening (NoNewPrivileges, ProtectSystem)
- Auto-opens Firefox kiosk to http://127.0.0.1:5000 on boot

Tier 2+3 — Docker (deploy/docker/):
- Multi-stage Dockerfile with two targets:
  - server: full web UI + stego + attestation + federation (Tier 2)
  - relay: lightweight FastAPI attestation API only (Tier 3)
- docker-compose.yml with both services and persistent volumes
- .dockerignore for clean builds

Kubernetes (deploy/kubernetes/):
- namespace.yaml, server-deployment.yaml, relay-deployment.yaml
- PVCs, services, health checks, resource limits
- Single-writer strategy (Recreate, not RollingUpdate) for SQLite safety
- README with architecture diagram and deployment instructions

Config presets (deploy/config-presets/):
- low-threat.json: press freedom country (no killswitch, 30min sessions)
- medium-threat.json: restricted press (48h deadman, USB monitoring)
- high-threat.json: conflict zone (12h deadman, tamper monitoring, 5min sessions)
- critical-threat.json: targeted surveillance (127.0.0.1 only, 6h deadman, 3min sessions)

Deployment guide rewritten for three-tier model with RPi as legacy appendix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aaron D. Lee 2026-04-01 22:52:38 -04:00
parent 2a6900abed
commit 496198d49a
19 changed files with 1304 additions and 266 deletions

View File

@ -0,0 +1,46 @@
# SooSeF Threat Level Configuration Presets
Select a preset based on your operational environment. Copy the appropriate
JSON file to `~/.soosef/config.json` (or let the setup wizard choose one).
## Presets
### low-threat.json — Press Freedom Country
Nordics, New Zealand, Canada. Risk is accidental data loss, not adversarial seizure.
- No killswitch or dead man's switch
- Relaxed session timeouts (30 min)
- Backup reminders every 14 days
- Chain enabled for provenance integrity
### medium-threat.json — Restricted Press
Turkey, Hungary, India. Risk of legal pressure, device seizure at borders.
- Killswitch available, dead man's switch at 48h/4h grace
- USB monitoring enabled
- Cover name: "Office Document Manager"
- Backup reminders every 7 days
### high-threat.json — Active Conflict Zone
Syria, Myanmar, Ethiopia, Iran. Risk of raids, equipment seizure, physical coercion.
- 5-minute session timeout
- Dead man's switch at 12h/1h grace
- Tamper monitoring enabled
- Cover name: "Local Inventory Tracker"
- Daily backup reminders
### critical-threat.json — Targeted Surveillance
Specific journalist or org targeted by state actor (Pegasus-level).
- Web UI bound to 127.0.0.1 only (access via SSH tunnel)
- 3-minute session timeout
- Dead man's switch at 6h/1h grace
- Cover name: "System Statistics"
- All monitoring enabled
- Consider: full-disk encryption, remove SSH after setup, Tor hidden service
## Usage
```bash
# Copy preset to config location
cp deploy/config-presets/high-threat.json ~/.soosef/config.json
# Or via CLI (future: soosef init --threat-level high)
```

View File

@ -0,0 +1,20 @@
{
"host": "127.0.0.1",
"port": 5000,
"https_enabled": true,
"auth_enabled": true,
"session_timeout_minutes": 3,
"login_lockout_attempts": 3,
"login_lockout_minutes": 60,
"killswitch_enabled": true,
"deadman_enabled": true,
"deadman_interval_hours": 6,
"deadman_grace_hours": 1,
"deadman_warning_webhook": "",
"usb_monitoring_enabled": true,
"tamper_monitoring_enabled": true,
"chain_enabled": true,
"chain_auto_wrap": true,
"backup_reminder_days": 1,
"cover_name": "System Statistics"
}

View File

@ -0,0 +1,20 @@
{
"host": "0.0.0.0",
"port": 5000,
"https_enabled": true,
"auth_enabled": true,
"session_timeout_minutes": 5,
"login_lockout_attempts": 3,
"login_lockout_minutes": 30,
"killswitch_enabled": true,
"deadman_enabled": true,
"deadman_interval_hours": 12,
"deadman_grace_hours": 1,
"deadman_warning_webhook": "",
"usb_monitoring_enabled": true,
"tamper_monitoring_enabled": true,
"chain_enabled": true,
"chain_auto_wrap": true,
"backup_reminder_days": 1,
"cover_name": "Local Inventory Tracker"
}

View File

@ -0,0 +1,20 @@
{
"host": "0.0.0.0",
"port": 5000,
"https_enabled": true,
"auth_enabled": true,
"session_timeout_minutes": 30,
"login_lockout_attempts": 10,
"login_lockout_minutes": 5,
"killswitch_enabled": false,
"deadman_enabled": false,
"deadman_interval_hours": 24,
"deadman_grace_hours": 2,
"deadman_warning_webhook": "",
"usb_monitoring_enabled": false,
"tamper_monitoring_enabled": false,
"chain_enabled": true,
"chain_auto_wrap": true,
"backup_reminder_days": 14,
"cover_name": ""
}

View File

@ -0,0 +1,20 @@
{
"host": "0.0.0.0",
"port": 5000,
"https_enabled": true,
"auth_enabled": true,
"session_timeout_minutes": 15,
"login_lockout_attempts": 5,
"login_lockout_minutes": 15,
"killswitch_enabled": true,
"deadman_enabled": true,
"deadman_interval_hours": 48,
"deadman_grace_hours": 4,
"deadman_warning_webhook": "",
"usb_monitoring_enabled": true,
"tamper_monitoring_enabled": false,
"chain_enabled": true,
"chain_auto_wrap": true,
"backup_reminder_days": 7,
"cover_name": "Office Document Manager"
}

View File

@ -0,0 +1,10 @@
.git
.claude
__pycache__
*.pyc
.coverage
.pytest_cache
docs
deploy/live-usb
test_data
*.egg-info

80
deploy/docker/Dockerfile Normal file
View File

@ -0,0 +1,80 @@
# SooSeF Federation Server
# Multi-stage build for minimal image size.
#
# Tier 2: Org server (full features — web UI, attestation, federation, stego)
# docker build -t soosef-server .
# docker run -v soosef-data:/data -p 5000:5000 -p 8000:8000 soosef-server
#
# Tier 3: Federation relay (attestation + federation only, no stego, no web UI)
# docker build --target relay -t soosef-relay .
# docker run -v relay-data:/data -p 8000:8000 soosef-relay
# === Stage 1: Build dependencies ===
FROM python:3.12-slim-bookworm AS builder
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc g++ gfortran \
libjpeg62-turbo-dev zlib1g-dev libffi-dev libssl-dev \
libopenblas-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
COPY . .
# Install into a virtual environment for clean copying
RUN python -m venv /opt/soosef-env \
&& /opt/soosef-env/bin/pip install --no-cache-dir \
".[web,cli,attest,stego-dct,api,federation]"
# === Stage 2: Federation relay (minimal) ===
FROM python:3.12-slim-bookworm AS relay
RUN apt-get update && apt-get install -y --no-install-recommends \
libjpeg62-turbo libopenblas0 \
&& rm -rf /var/lib/apt/lists/* \
&& useradd -m -s /bin/bash soosef
COPY --from=builder /opt/soosef-env /opt/soosef-env
ENV PATH="/opt/soosef-env/bin:$PATH" \
SOOSEF_DATA_DIR=/data \
PYTHONUNBUFFERED=1
VOLUME /data
EXPOSE 8000
USER soosef
# Federation relay: only the verisoo API with federation endpoints
CMD ["uvicorn", "soosef.verisoo.api:app", "--host", "0.0.0.0", "--port", "8000"]
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"
# === Stage 3: Full org server ===
FROM python:3.12-slim-bookworm AS server
RUN apt-get update && apt-get install -y --no-install-recommends \
libjpeg62-turbo libopenblas0 \
&& rm -rf /var/lib/apt/lists/* \
&& useradd -m -s /bin/bash soosef
COPY --from=builder /opt/soosef-env /opt/soosef-env
# Copy frontend templates and static assets
COPY frontends/ /opt/soosef-env/lib/python3.12/site-packages/frontends/
ENV PATH="/opt/soosef-env/bin:$PATH" \
SOOSEF_DATA_DIR=/data \
PYTHONUNBUFFERED=1
VOLUME /data
EXPOSE 5000 8000
USER soosef
# Init on first run, then start web UI + federation API
CMD ["sh", "-c", "soosef init 2>/dev/null; soosef serve --host 0.0.0.0 --no-https"]
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')"

View File

@ -0,0 +1,51 @@
# SooSeF Docker Compose — Three-Tier Deployment
#
# Tier 2 (Org Server): Full web UI + attestation + federation
# Tier 3 (Federation Relay): Lightweight attestation API only
#
# Usage:
# Full org server: docker compose up server
# Federation relay only: docker compose up relay
# Both (e.g., testing): docker compose up
services:
# === Tier 2: Organizational Server ===
# Full SooSeF instance with web UI, stego, attestation, federation.
# Deploy on a mini PC in the newsroom or a trusted VPS.
server:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile
target: server
ports:
- "5000:5000" # Web UI (Flask/Waitress)
- "8000:8000" # Federation API (FastAPI/uvicorn)
volumes:
- server-data:/data
environment:
- SOOSEF_DATA_DIR=/data
- VERISOO_GOSSIP_INTERVAL=60
restart: unless-stopped
# === Tier 3: Federation Relay ===
# Lightweight relay for cross-organization attestation sync.
# Deploy on a VPS in a friendly jurisdiction (Iceland, Switzerland).
# Stores only attestation records — no key material, no stego, no web UI.
relay:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile
target: relay
ports:
- "8001:8000" # Federation API
volumes:
- relay-data:/data
environment:
- SOOSEF_DATA_DIR=/data
restart: unless-stopped
volumes:
server-data:
driver: local
relay-data:
driver: local

View File

@ -0,0 +1,52 @@
# SooSeF Kubernetes Deployment
## Architecture
```
Field Devices (Tier 1)
(Bootable USB + laptop)
|
| LAN / sneakernet
v
┌───────────────────────┐
│ Org Server (Tier 2) │ <-- server-deployment.yaml
│ Full web UI + stego │
│ + attestation + fed │
│ Newsroom mini PC │
└───────────┬───────────┘
|
| gossip / federation API
v
┌───────────────────────┐
│ Fed Relay (Tier 3) │ <-- relay-deployment.yaml
│ Attestation API only │
│ VPS (Iceland, CH) │
│ Zero key knowledge │
└───────────────────────┘
```
## Quick Start
```bash
# Build images
docker build -t soosef-server --target server -f deploy/docker/Dockerfile .
docker build -t soosef-relay --target relay -f deploy/docker/Dockerfile .
# Deploy to Kubernetes
kubectl apply -f deploy/kubernetes/namespace.yaml
kubectl apply -f deploy/kubernetes/server-deployment.yaml
kubectl apply -f deploy/kubernetes/relay-deployment.yaml
```
## Notes
- **Single writer**: Both deployments use `replicas: 1` with `Recreate` strategy.
SooSeF uses SQLite and append-only binary logs that require single-writer access.
Do not scale horizontally.
- **PVCs**: Both deployments require persistent volumes. The server needs 10Gi,
the relay needs 5Gi. Adjust based on expected attestation volume.
- **Security**: The relay stores only attestation records (image hashes + signatures).
It never sees encryption keys, plaintext messages, or original images.
If the relay is seized, the attacker gets cryptographic hashes — nothing actionable.
- **Ingress**: Not included. Configure your own ingress controller with TLS termination.
The federation API should be TLS-encrypted in transit.

View File

@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: soosef
labels:
app.kubernetes.io/name: soosef

View File

@ -0,0 +1,85 @@
# SooSeF Federation Relay — Lightweight attestation sync relay.
# Deploy on a VPS in a favorable jurisdiction for geographic redundancy.
# Stores only attestation records — zero knowledge of encryption keys.
apiVersion: apps/v1
kind: Deployment
metadata:
name: soosef-relay
namespace: soosef
labels:
app.kubernetes.io/name: soosef
app.kubernetes.io/component: relay
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: soosef
app.kubernetes.io/component: relay
template:
metadata:
labels:
app.kubernetes.io/name: soosef
app.kubernetes.io/component: relay
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: relay
image: soosef-relay:latest
ports:
- containerPort: 8000
name: federation
env:
- name: SOOSEF_DATA_DIR
value: /data
volumeMounts:
- name: data
mountPath: /data
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 30
volumes:
- name: data
persistentVolumeClaim:
claimName: relay-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: relay-data
namespace: soosef
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: Service
metadata:
name: soosef-relay
namespace: soosef
spec:
selector:
app.kubernetes.io/name: soosef
app.kubernetes.io/component: relay
ports:
- name: federation
port: 8000
targetPort: 8000
type: ClusterIP

View File

@ -0,0 +1,97 @@
# SooSeF Org Server — Full deployment with persistent storage.
# For newsroom or trusted infrastructure deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: soosef-server
namespace: soosef
labels:
app.kubernetes.io/name: soosef
app.kubernetes.io/component: server
spec:
replicas: 1 # Single writer — do not scale horizontally
strategy:
type: Recreate # Not RollingUpdate — SQLite + append-only logs need single writer
selector:
matchLabels:
app.kubernetes.io/name: soosef
app.kubernetes.io/component: server
template:
metadata:
labels:
app.kubernetes.io/name: soosef
app.kubernetes.io/component: server
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: soosef
image: soosef-server:latest
ports:
- containerPort: 5000
name: web
- containerPort: 8000
name: federation
env:
- name: SOOSEF_DATA_DIR
value: /data
- name: VERISOO_GOSSIP_INTERVAL
value: "60"
volumeMounts:
- name: data
mountPath: /data
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "2000m"
livenessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 15
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 10
periodSeconds: 10
volumes:
- name: data
persistentVolumeClaim:
claimName: soosef-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: soosef-data
namespace: soosef
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: soosef-server
namespace: soosef
spec:
selector:
app.kubernetes.io/name: soosef
app.kubernetes.io/component: server
ports:
- name: web
port: 5000
targetPort: 5000
- name: federation
port: 8000
targetPort: 8000
type: ClusterIP

47
deploy/live-usb/build.sh Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env bash
# Build a bootable Debian Live USB image with SooSeF pre-installed.
#
# Prerequisites:
# apt install live-build
#
# Usage:
# cd deploy/live-usb
# sudo ./build.sh
#
# Output: live-image-amd64.hybrid.iso (flash to USB with dd or Balena Etcher)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SOOSEF_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
echo "=== SooSeF Live USB Image Builder ==="
echo "Source: $SOOSEF_ROOT"
echo
cd "$SCRIPT_DIR"
# Clean previous builds
lb clean 2>/dev/null || true
# Configure live-build
lb config \
--distribution bookworm \
--architectures amd64 \
--binary-images iso-hybrid \
--memtest none \
--bootappend-live "boot=live components locales=en_US.UTF-8 keyboard-layouts=us" \
--apt-indices false \
--security true \
--updates true
# Build
echo "Building image (this takes 10-20 minutes)..."
lb build
echo
echo "=== Build complete ==="
echo "Image: $(ls -lh live-image-*.iso 2>/dev/null || echo 'Check for .iso file')"
echo
echo "Flash to USB:"
echo " sudo dd if=live-image-amd64.hybrid.iso of=/dev/sdX bs=4M status=progress"
echo " (replace /dev/sdX with your USB device)"

View File

@ -0,0 +1,26 @@
#!/bin/bash
# Install SooSeF and all dependencies into the live image.
# This runs inside the chroot during image build.
set -euo pipefail
echo "=== Installing SooSeF ==="
# Create soosef user
useradd -m -s /bin/bash -G sudo soosef
echo "soosef:soosef" | chpasswd
# Create virtual environment
python3 -m venv /opt/soosef-env
source /opt/soosef-env/bin/activate
# Install soosef with all extras (pre-built wheels from PyPI)
pip install --no-cache-dir "soosef[web,cli,attest,stego-dct,stego-audio,fieldkit]"
# Verify installation
python3 -c "import soosef; print(f'SooSeF {soosef.__version__} installed')"
python3 -c "from soosef.stegasoo import encode; print('stegasoo OK')"
python3 -c "from soosef.verisoo import Attestation; print('verisoo OK')"
deactivate
echo "=== SooSeF installation complete ==="

View File

@ -0,0 +1,39 @@
#!/bin/bash
# Security hardening for the live image.
set -euo pipefail
echo "=== Applying security hardening ==="
# Disable core dumps (Python doesn't zero memory — core dumps leak keys)
echo "* hard core 0" >> /etc/security/limits.conf
echo "fs.suid_dumpable = 0" >> /etc/sysctl.d/99-soosef.conf
echo "kernel.core_pattern=|/bin/false" >> /etc/sysctl.d/99-soosef.conf
# Disable swap (keys persist in swap pages)
systemctl mask swap.target || true
echo "vm.swappiness = 0" >> /etc/sysctl.d/99-soosef.conf
# Enable UFW with deny-all + allow web UI
ufw default deny incoming
ufw default allow outgoing
ufw allow 5000/tcp comment "SooSeF Web UI"
ufw allow 22/tcp comment "SSH"
ufw --force enable || true
# Disable unnecessary services
systemctl disable bluetooth.service 2>/dev/null || true
systemctl disable avahi-daemon.service 2>/dev/null || true
systemctl disable cups.service 2>/dev/null || true
# Enable SooSeF service
systemctl enable soosef.service
# Auto-login to openbox (so the browser opens without login prompt)
mkdir -p /etc/lightdm/lightdm.conf.d
cat > /etc/lightdm/lightdm.conf.d/50-autologin.conf << 'EOF'
[Seat:*]
autologin-user=soosef
autologin-user-timeout=0
EOF
echo "=== Hardening complete ==="

View File

@ -0,0 +1,28 @@
[Unit]
Description=SooSeF Security Fieldkit
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=soosef
Group=soosef
WorkingDirectory=/home/soosef
Environment=PATH=/opt/soosef-env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Environment=SOOSEF_DATA_DIR=/home/soosef/.soosef
ExecStartPre=/opt/soosef-env/bin/soosef init --no-identity --no-channel
ExecStart=/opt/soosef-env/bin/soosef serve --host 0.0.0.0 --no-https
Restart=on-failure
RestartSec=5
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/soosef/.soosef
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,4 @@
# SooSeF Live USB — auto-open web UI in Firefox
# Wait for the SooSeF server to start, then open the browser
sleep 5
firefox-esr --kiosk http://127.0.0.1:5000 &

View File

@ -0,0 +1,41 @@
## System essentials
python3
python3-pip
python3-venv
python3-dev
## Build dependencies for Python packages with C extensions
libjpeg62-turbo-dev
zlib1g-dev
libffi-dev
libssl-dev
gfortran
libopenblas-dev
## SooSeF runtime dependencies
gpsd
gpsd-clients
cryptsetup
ufw
shred
## Useful tools
curl
wget
git
htop
usbutils
pciutils
## GUI (minimal — just a browser for the web UI)
xorg
openbox
firefox-esr
lightdm
## Firmware for common laptop hardware
firmware-linux-free
firmware-misc-nonfree
firmware-iwlwifi
firmware-realtek
firmware-atheros

File diff suppressed because it is too large Load Diff