Add comprehensive documentation for v0.2.0
- 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>
This commit is contained in:
parent
b9a541efdb
commit
b7d4cbe286
95
CLAUDE.md
95
CLAUDE.md
@ -1,16 +1,14 @@
|
|||||||
# SooSeF — Claude Code Project Guide
|
# SooSeF — Claude Code Project Guide
|
||||||
|
|
||||||
SooSeF (Soo Security Fieldkit) is an offline-first security toolkit for journalists, NGOs,
|
SooSeF (Soo Security Fieldkit) is an offline-first security toolkit for journalists, NGOs,
|
||||||
and at-risk organizations. Part of the Soo Suite alongside Stegasoo and Verisoo.
|
and at-risk organizations. Monorepo consolidating Stegasoo and Verisoo as subpackages.
|
||||||
|
|
||||||
Version 0.1.0 · Python >=3.11 · MIT License
|
Version 0.2.0 · Python >=3.11 · MIT License
|
||||||
|
|
||||||
## Quick commands
|
## Quick commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Development install (requires stegasoo and verisoo installed first)
|
# Development install (single command — stegasoo and verisoo are inlined subpackages)
|
||||||
pip install -e /path/to/stegasoo[web,dct,audio]
|
|
||||||
pip install -e /path/to/verisoo[cli]
|
|
||||||
pip install -e ".[dev]"
|
pip install -e ".[dev]"
|
||||||
|
|
||||||
pytest # Run tests
|
pytest # Run tests
|
||||||
@ -19,19 +17,75 @@ ruff check src/ tests/ frontends/ --fix # Lint
|
|||||||
mypy src/ # Type check
|
mypy src/ # Type check
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Pip extras
|
||||||
|
|
||||||
|
`stego-dct`, `stego-audio`, `stego-compression`, `attest`, `cli`, `web`, `api`,
|
||||||
|
`fieldkit`, `federation`, `rpi`, `all`, `dev`
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
src/soosef/ Core library
|
src/soosef/ Core library
|
||||||
__init__.py Package init, __version__
|
__init__.py Package init, __version__
|
||||||
|
_availability.py Runtime checks for optional subpackages (has_stegasoo, has_verisoo)
|
||||||
|
api.py Optional unified FastAPI app (uvicorn soosef.api:app)
|
||||||
|
audit.py Audit logging
|
||||||
|
cli.py Click CLI entry point (soosef command)
|
||||||
paths.py All ~/.soosef/* path constants (single source of truth)
|
paths.py All ~/.soosef/* path constants (single source of truth)
|
||||||
config.py Unified config loader
|
config.py Unified config loader
|
||||||
exceptions.py SoosefError base exception
|
exceptions.py SoosefError base exception
|
||||||
keystore/ Unified key management
|
|
||||||
|
stegasoo/ Steganography engine (inlined from stegasoo)
|
||||||
|
encode.py / decode.py Core encode/decode API
|
||||||
|
generate.py Cover image generation
|
||||||
|
crypto.py AES-256-GCM encryption
|
||||||
|
channel.py Channel key derivation
|
||||||
|
steganography.py LSB steganography core
|
||||||
|
dct_steganography.py DCT-domain JPEG steganography
|
||||||
|
spread_steganography.py MDCT spread-spectrum audio steganography
|
||||||
|
audio_steganography.py Audio stego pipeline
|
||||||
|
video_steganography.py Video stego pipeline
|
||||||
|
backends/ Pluggable stego backend registry (lsb, dct, protocol)
|
||||||
|
batch.py Batch operations
|
||||||
|
compression.py Payload compression (zstd, lz4)
|
||||||
|
steganalysis.py Detection resistance analysis
|
||||||
|
validation.py Input validation
|
||||||
|
models.py Data models
|
||||||
|
constants.py Magic bytes, version constants
|
||||||
|
cli.py Stegasoo-specific CLI commands
|
||||||
|
api.py / api_auth.py Stegasoo REST API + auth
|
||||||
|
image_utils.py / audio_utils.py / video_utils.py
|
||||||
|
keygen.py / qr_utils.py / recovery.py / debug.py / utils.py
|
||||||
|
platform_presets.py Social-media-aware encoding presets
|
||||||
|
|
||||||
|
verisoo/ Provenance attestation engine (inlined from verisoo)
|
||||||
|
attestation.py Core attestation creation
|
||||||
|
verification.py Attestation verification
|
||||||
|
crypto.py Ed25519 signing
|
||||||
|
hashing.py Perceptual + cryptographic hashing
|
||||||
|
embed.py Attestation embedding in images
|
||||||
|
merkle.py Merkle tree for batch attestation
|
||||||
|
binlog.py Binary attestation log
|
||||||
|
lmdb_store.py LMDB-backed trust store
|
||||||
|
storage.py Attestation storage abstraction
|
||||||
|
federation.py Federated attestation exchange
|
||||||
|
models.py Attestation, Verification dataclasses
|
||||||
|
exceptions.py Verisoo-specific exceptions
|
||||||
|
cli.py Verisoo-specific CLI commands
|
||||||
|
api.py Verisoo REST API
|
||||||
|
|
||||||
|
federation/ Federated attestation chain system
|
||||||
|
chain.py Hash chain construction
|
||||||
|
entropy.py Entropy source for chain seeds
|
||||||
|
models.py ChainEntry, ChainState dataclasses
|
||||||
|
serialization.py CBOR chain serialization + export
|
||||||
|
|
||||||
|
keystore/ Unified key management
|
||||||
manager.py Owns all key material (channel keys + Ed25519 identity)
|
manager.py Owns all key material (channel keys + Ed25519 identity)
|
||||||
models.py KeyBundle, IdentityBundle dataclasses
|
models.py KeyBundle, IdentityBundle dataclasses
|
||||||
export.py Encrypted key bundle export/import
|
export.py Encrypted key bundle export/import
|
||||||
fieldkit/ Field security features
|
|
||||||
|
fieldkit/ Field security features
|
||||||
killswitch.py Emergency data destruction
|
killswitch.py Emergency data destruction
|
||||||
deadman.py Dead man's switch
|
deadman.py Dead man's switch
|
||||||
tamper.py File integrity monitoring
|
tamper.py File integrity monitoring
|
||||||
@ -40,27 +94,30 @@ src/soosef/ Core library
|
|||||||
|
|
||||||
frontends/web/ Unified Flask web UI
|
frontends/web/ Unified Flask web UI
|
||||||
app.py App factory (create_app())
|
app.py App factory (create_app())
|
||||||
auth.py SQLite3 multi-user auth (from stegasoo)
|
auth.py SQLite3 multi-user auth
|
||||||
temp_storage.py File-based temp storage with expiry
|
temp_storage.py File-based temp storage with expiry
|
||||||
subprocess_stego.py Crash-safe subprocess isolation for stegasoo
|
subprocess_stego.py Crash-safe subprocess isolation for stegasoo
|
||||||
|
stego_worker.py Background stego processing
|
||||||
|
stego_routes.py Stego route helpers
|
||||||
ssl_utils.py Self-signed HTTPS cert generation
|
ssl_utils.py Self-signed HTTPS cert generation
|
||||||
blueprints/
|
blueprints/
|
||||||
stego.py /encode, /decode, /generate (from stegasoo)
|
stego.py /encode, /decode, /generate
|
||||||
attest.py /attest, /verify (wraps verisoo)
|
attest.py /attest, /verify
|
||||||
fieldkit.py /fieldkit/* (killswitch, deadman, status)
|
fieldkit.py /fieldkit/* (killswitch, deadman, status)
|
||||||
keys.py /keys/* (unified key management)
|
keys.py /keys/* (unified key management)
|
||||||
admin.py /admin/* (user management)
|
admin.py /admin/* (user management)
|
||||||
|
|
||||||
frontends/cli/ CLI entry point
|
frontends/cli/ CLI package init (main entry point is src/soosef/cli.py)
|
||||||
main.py Click CLI wrapping stegasoo + verisoo + soosef commands
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dependency model
|
## Dependency model
|
||||||
|
|
||||||
Stegasoo and Verisoo are pip dependencies, not forks:
|
Stegasoo and Verisoo are inlined subpackages, not separate pip packages:
|
||||||
- `import stegasoo` for steganography
|
- `from soosef.stegasoo import encode` for steganography
|
||||||
- `import verisoo` for provenance attestation
|
- `from soosef.verisoo import Attestation` for provenance attestation
|
||||||
- SooSeF adds: unified web UI, key management, fieldkit features
|
- Never `import stegasoo` or `import verisoo` directly
|
||||||
|
- `_availability.py` provides `has_stegasoo()` / `has_verisoo()` for graceful degradation
|
||||||
|
when optional extras are not installed
|
||||||
|
|
||||||
## Key design decisions
|
## Key design decisions
|
||||||
|
|
||||||
@ -70,7 +127,11 @@ Stegasoo and Verisoo are pip dependencies, not forks:
|
|||||||
- **All state under ~/.soosef/** — one directory to back up, one to destroy
|
- **All state under ~/.soosef/** — one directory to back up, one to destroy
|
||||||
- **Offline-first**: All static assets vendored, no CDN. pip wheels bundled for airgap install
|
- **Offline-first**: All static assets vendored, no CDN. pip wheels bundled for airgap install
|
||||||
- **Flask blueprints**: stego, attest, fieldkit, keys, admin — clean route separation
|
- **Flask blueprints**: stego, attest, fieldkit, keys, admin — clean route separation
|
||||||
|
- **Flask-WTF**: CSRF protection on all form endpoints
|
||||||
|
- **Waitress**: Production WSGI server (replaces dev-only Flask server)
|
||||||
|
- **FastAPI option**: `soosef.api` provides a REST API alternative to the Flask web UI
|
||||||
|
- **Pluggable backends**: Stego backends (LSB, DCT) registered via `backends/registry.py`
|
||||||
|
|
||||||
## Code conventions
|
## Code conventions
|
||||||
|
|
||||||
Same as stegasoo: Black (100-char), Ruff, mypy, imperative commit messages.
|
Black (100-char), Ruff, mypy, imperative commit messages.
|
||||||
|
|||||||
422
README.md
422
README.md
@ -1,27 +1,417 @@
|
|||||||
# SooSeF — Soo Security Fieldkit
|
# SooSeF -- Soo Security Fieldkit
|
||||||
|
|
||||||
Offline-first security toolkit for journalists, NGOs, and at-risk organizations.
|
**Offline-first security toolkit for journalists, NGOs, and at-risk organizations.**
|
||||||
|
|
||||||
Part of the **Soo Suite**:
|
<!-- badges -->
|
||||||
- **Stegasoo** — hide encrypted messages in media (steganography)
|

|
||||||
- **Verisoo** — prove image provenance and authenticity (attestation)
|

|
||||||
- **SooSeF** — unified fieldkit with killswitch, dead man's switch, and key management
|

|
||||||
|
|
||||||
## Status
|
---
|
||||||
|
|
||||||
Pre-alpha. Phase 1 scaffolding complete.
|
## What is SooSeF?
|
||||||
|
|
||||||
## Install (development)
|
SooSeF combines steganography, provenance attestation, and field security tools into a
|
||||||
|
single package designed for airgapped and resource-constrained environments. It lets you:
|
||||||
|
|
||||||
```bash
|
- **Hide messages** in images, audio, and video using multiple steganographic techniques
|
||||||
pip install -e /path/to/stegasoo[web,dct,audio,cli]
|
- **Prove authenticity** of photos and documents with Ed25519 signatures and Merkle trees
|
||||||
pip install -e /path/to/verisoo[cli]
|
- **Protect data in the field** with a killswitch, dead man's switch, tamper detection, USB device whitelisting, and GPS geofencing
|
||||||
pip install -e ".[web,cli]"
|
- **Manage cryptographic keys** with identity rotation, channel key generation, and encrypted key bundle export/import
|
||||||
```
|
|
||||||
|
Stegasoo (steganography, v4.3.0) and Verisoo (attestation, v0.1.0) are included as
|
||||||
|
subpackages (`import soosef.stegasoo`, `import soosef.verisoo`). Everything ships as one
|
||||||
|
install: `pip install soosef`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
soosef init # Generate identity + channel key, create ~/.soosef/
|
pip install "soosef[web,cli]"
|
||||||
soosef serve # Start the web UI
|
soosef init
|
||||||
|
soosef serve
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This creates the `~/.soosef/` directory structure, generates an Ed25519 identity and
|
||||||
|
channel key, writes a default config, and starts an HTTPS web UI on
|
||||||
|
`https://127.0.0.1:5000`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Steganography (Stegasoo)
|
||||||
|
|
||||||
|
- **LSB encoding** -- bit-level message hiding in PNG images
|
||||||
|
- **DCT encoding** -- frequency-domain hiding in JPEG images (requires `stego-dct` extra)
|
||||||
|
- **Audio steganography** -- hide data in WAV/FLAC audio (requires `stego-audio` extra)
|
||||||
|
- **Video steganography** -- frame-level encoding
|
||||||
|
- AES-256-GCM encryption with Argon2id key derivation
|
||||||
|
- EXIF stripping on encode to prevent metadata leakage
|
||||||
|
- Compression support (zstandard, optional LZ4)
|
||||||
|
|
||||||
|
### Attestation (Verisoo)
|
||||||
|
|
||||||
|
- Ed25519 digital signatures for images and files
|
||||||
|
- Perceptual hashing (ImageHash) for tamper-evident photo attestation
|
||||||
|
- LMDB-backed attestation storage
|
||||||
|
- Append-only hash chain (CBOR-encoded) with Merkle tree verification
|
||||||
|
- Batch attestation for directories
|
||||||
|
|
||||||
|
### Fieldkit
|
||||||
|
|
||||||
|
- **Killswitch** -- emergency destruction of all data under `~/.soosef/`
|
||||||
|
- **Dead man's switch** -- automated purge if check-in is missed
|
||||||
|
- **Tamper detection** -- file integrity monitoring with baseline snapshots
|
||||||
|
- **USB whitelist** -- block or alert on unauthorized USB devices (Linux/pyudev)
|
||||||
|
- **Geofence** -- GPS boundary enforcement with configurable radius
|
||||||
|
|
||||||
|
### Key Management
|
||||||
|
|
||||||
|
- Two separate key domains: AES-256-GCM channel keys (stego) and Ed25519 identity keys (attestation)
|
||||||
|
- Key rotation for both identity and channel keys
|
||||||
|
- Encrypted key bundle export/import for secure backup and transfer
|
||||||
|
- QR code generation for key sharing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Basic install (core library only)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install soosef
|
||||||
|
```
|
||||||
|
|
||||||
|
### With extras
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install "soosef[web,cli]" # Web UI + CLI (most common)
|
||||||
|
pip install "soosef[all]" # Everything except dev tools
|
||||||
|
pip install "soosef[dev]" # All + pytest, black, ruff, mypy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available extras
|
||||||
|
|
||||||
|
| Extra | What it adds |
|
||||||
|
|---|---|
|
||||||
|
| `stego-dct` | DCT steganography (numpy, scipy, jpeglib, reedsolo) |
|
||||||
|
| `stego-audio` | Audio steganography (pydub, soundfile, reedsolo) |
|
||||||
|
| `stego-compression` | LZ4 compression support |
|
||||||
|
| `attest` | Attestation features (imagehash, lmdb, exifread) |
|
||||||
|
| `cli` | Click CLI with rich output and QR code support |
|
||||||
|
| `web` | Flask web UI with Waitress/Gunicorn, includes attest + stego-dct |
|
||||||
|
| `api` | FastAPI REST API with uvicorn, includes stego-dct |
|
||||||
|
| `fieldkit` | Tamper monitoring (watchdog) and USB whitelist (pyudev) |
|
||||||
|
| `federation` | Peer-to-peer attestation federation (aiohttp) |
|
||||||
|
| `rpi` | Raspberry Pi deployment (web + cli + fieldkit + gpiozero) |
|
||||||
|
| `all` | All of the above |
|
||||||
|
| `dev` | All + pytest, pytest-cov, black, ruff, mypy |
|
||||||
|
|
||||||
|
### Airgapped / Raspberry Pi install
|
||||||
|
|
||||||
|
Bundle wheels on a networked machine, then install offline:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On networked machine
|
||||||
|
pip download "soosef[rpi]" -d ./wheels
|
||||||
|
|
||||||
|
# Transfer ./wheels to target via USB
|
||||||
|
# On airgapped machine
|
||||||
|
pip install --no-index --find-links=./wheels "soosef[rpi]"
|
||||||
|
soosef init
|
||||||
|
soosef serve --host 0.0.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CLI Reference
|
||||||
|
|
||||||
|
All commands accept `--data-dir PATH` to override the default `~/.soosef` directory,
|
||||||
|
and `--json` for machine-readable output.
|
||||||
|
|
||||||
|
```
|
||||||
|
soosef [--data-dir PATH] [--json] COMMAND
|
||||||
|
```
|
||||||
|
|
||||||
|
### Core commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---|---|
|
||||||
|
| `soosef init` | Create directory structure, generate identity + channel key, write default config |
|
||||||
|
| `soosef serve` | Start the web UI (default: `https://127.0.0.1:5000`) |
|
||||||
|
| `soosef status` | Show instance status: identity, keys, chain, fieldkit, config |
|
||||||
|
|
||||||
|
### `soosef serve` options
|
||||||
|
|
||||||
|
| Option | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `--host` | `127.0.0.1` | Bind address |
|
||||||
|
| `--port` | `5000` | Bind port |
|
||||||
|
| `--no-https` | off | Disable HTTPS (use HTTP) |
|
||||||
|
| `--debug` | off | Use Flask dev server instead of Waitress |
|
||||||
|
| `--workers` | `4` | Number of Waitress/Gunicorn worker threads |
|
||||||
|
|
||||||
|
### Steganography commands (`soosef stego`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
soosef stego encode -i cover.png -m "secret message" -o output.png
|
||||||
|
soosef stego decode -i output.png
|
||||||
|
```
|
||||||
|
|
||||||
|
### Attestation commands (`soosef attest`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
soosef attest sign -i photo.jpg # Sign a file
|
||||||
|
soosef attest verify -i photo.jpg # Verify attestation
|
||||||
|
soosef attest batch ./photos/ --caption "Field report"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fieldkit commands (`soosef fieldkit`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
soosef fieldkit status # Show fieldkit state
|
||||||
|
soosef fieldkit checkin # Reset dead man's switch timer
|
||||||
|
soosef fieldkit check-deadman # Check if deadman timer has expired
|
||||||
|
soosef fieldkit purge --confirm # Activate killswitch (destroys all data)
|
||||||
|
soosef fieldkit geofence set --lat 48.8566 --lon 2.3522 --radius 1000
|
||||||
|
soosef fieldkit geofence check --lat 48.8600 --lon 2.3500
|
||||||
|
soosef fieldkit geofence clear
|
||||||
|
soosef fieldkit usb snapshot # Snapshot current USB devices as whitelist
|
||||||
|
soosef fieldkit usb check # Check for unauthorized USB devices
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key management commands (`soosef keys`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
soosef keys show # Display current key info
|
||||||
|
soosef keys export -o backup.enc # Export encrypted key bundle
|
||||||
|
soosef keys import -b backup.enc # Import key bundle
|
||||||
|
soosef keys rotate-identity # Generate new Ed25519 identity
|
||||||
|
soosef keys rotate-channel # Generate new channel key
|
||||||
|
```
|
||||||
|
|
||||||
|
### Chain commands (`soosef chain`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
soosef chain status # Show chain head, length, integrity
|
||||||
|
soosef chain verify # Verify entire chain integrity
|
||||||
|
soosef chain show INDEX # Show a specific chain record
|
||||||
|
soosef chain log --count 20 # Show recent chain entries
|
||||||
|
soosef chain backfill # Backfill existing attestations into chain
|
||||||
|
soosef chain export --start 0 --end 100 -o chain.cbor
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Web UI
|
||||||
|
|
||||||
|
Start with `soosef serve`. The web UI provides authenticated access to all features
|
||||||
|
through Flask blueprints.
|
||||||
|
|
||||||
|
### Routes
|
||||||
|
|
||||||
|
| Blueprint | Routes | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| stego | `/encode`, `/decode`, `/generate` | Steganography operations |
|
||||||
|
| attest | `/attest`, `/verify` | Attestation signing and verification |
|
||||||
|
| fieldkit | `/fieldkit/*` | Killswitch, dead man's switch, status dashboard |
|
||||||
|
| keys | `/keys/*` | Key management, rotation, export/import |
|
||||||
|
| admin | `/admin/*` | User management (multi-user auth via SQLite) |
|
||||||
|
| health | `/health` | Capability reporting endpoint (see API section) |
|
||||||
|
|
||||||
|
<!-- TODO: screenshots -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
SooSeF loads configuration from `~/.soosef/config.json`. All fields have sensible defaults.
|
||||||
|
`soosef init` writes the default config file.
|
||||||
|
|
||||||
|
### Config fields
|
||||||
|
|
||||||
|
| Field | Type | Default | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `host` | string | `127.0.0.1` | Web UI bind address |
|
||||||
|
| `port` | int | `5000` | Web UI bind port |
|
||||||
|
| `https_enabled` | bool | `true` | Enable HTTPS with self-signed cert |
|
||||||
|
| `auth_enabled` | bool | `true` | Require login for web UI |
|
||||||
|
| `max_upload_mb` | int | `50` | Maximum upload size in MB |
|
||||||
|
| `session_timeout_minutes` | int | `15` | Session expiry |
|
||||||
|
| `login_lockout_attempts` | int | `5` | Failed logins before lockout |
|
||||||
|
| `login_lockout_minutes` | int | `15` | Lockout duration |
|
||||||
|
| `default_embed_mode` | string | `auto` | Stegasoo encoding mode |
|
||||||
|
| `killswitch_enabled` | bool | `false` | Enable killswitch functionality |
|
||||||
|
| `deadman_enabled` | bool | `false` | Enable dead man's switch |
|
||||||
|
| `deadman_interval_hours` | int | `24` | Check-in interval |
|
||||||
|
| `deadman_grace_hours` | int | `2` | Grace period after missed check-in |
|
||||||
|
| `deadman_warning_webhook` | string | `""` | URL to POST warning before auto-purge |
|
||||||
|
| `usb_monitoring_enabled` | bool | `false` | Enable USB device whitelist enforcement |
|
||||||
|
| `tamper_monitoring_enabled` | bool | `false` | Enable file integrity monitoring |
|
||||||
|
| `chain_enabled` | bool | `true` | Enable attestation hash chain |
|
||||||
|
| `chain_auto_wrap` | bool | `true` | Auto-wrap attestations in chain records |
|
||||||
|
| `backup_reminder_days` | int | `7` | Days before backup reminder |
|
||||||
|
| `gpio_killswitch_pin` | int | `17` | Raspberry Pi GPIO pin for hardware killswitch |
|
||||||
|
| `gpio_killswitch_hold_seconds` | float | `5.0` | Hold duration to trigger hardware killswitch |
|
||||||
|
|
||||||
|
### Environment variables
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|---|---|
|
||||||
|
| `SOOSEF_DATA_DIR` | Override the data directory (default: `~/.soosef`) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Source layout
|
||||||
|
|
||||||
|
```
|
||||||
|
src/soosef/
|
||||||
|
__init__.py Package init, __version__
|
||||||
|
cli.py Click CLI (entry point: soosef)
|
||||||
|
paths.py All path constants (single source of truth)
|
||||||
|
config.py Unified config loader (dataclass + JSON)
|
||||||
|
exceptions.py SoosefError base exception
|
||||||
|
stegasoo/ Steganography engine (subpackage)
|
||||||
|
verisoo/ Attestation engine (subpackage)
|
||||||
|
keystore/
|
||||||
|
manager.py Key material management (channel + identity)
|
||||||
|
models.py KeyBundle, IdentityBundle dataclasses
|
||||||
|
export.py Encrypted key bundle export/import
|
||||||
|
fieldkit/
|
||||||
|
killswitch.py Emergency data destruction
|
||||||
|
deadman.py Dead man's switch
|
||||||
|
tamper.py File integrity monitoring
|
||||||
|
usb_monitor.py USB device whitelist (Linux/pyudev)
|
||||||
|
geofence.py GPS boundary enforcement
|
||||||
|
|
||||||
|
frontends/web/
|
||||||
|
app.py Flask app factory (create_app())
|
||||||
|
auth.py SQLite3 multi-user auth
|
||||||
|
temp_storage.py File-based temp storage with expiry
|
||||||
|
subprocess_stego.py Crash-safe subprocess isolation
|
||||||
|
ssl_utils.py Self-signed HTTPS cert generation
|
||||||
|
blueprints/
|
||||||
|
stego.py /encode, /decode, /generate
|
||||||
|
attest.py /attest, /verify
|
||||||
|
fieldkit.py /fieldkit/*
|
||||||
|
keys.py /keys/*
|
||||||
|
admin.py /admin/*
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data directory (`~/.soosef/`)
|
||||||
|
|
||||||
|
```
|
||||||
|
~/.soosef/
|
||||||
|
config.json Unified configuration
|
||||||
|
audit.jsonl Append-only audit trail
|
||||||
|
identity/ Ed25519 keypair (private.pem, public.pem, identity.meta.json)
|
||||||
|
stegasoo/ Channel key (channel.key)
|
||||||
|
attestations/ Verisoo attestation store (log.bin, index/, peers.json)
|
||||||
|
chain/ Hash chain (chain.bin, state.cbor)
|
||||||
|
auth/ Web UI auth database (soosef.db)
|
||||||
|
certs/ Self-signed TLS certificates
|
||||||
|
fieldkit/ Fieldkit state (deadman.json, tamper/, usb/, geofence.json)
|
||||||
|
temp/ Ephemeral file storage
|
||||||
|
instance/ Flask instance (sessions, secret key)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sensitive directories (`identity/`, `auth/`, `certs/`, and the root) are created with
|
||||||
|
`0700` permissions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Model
|
||||||
|
|
||||||
|
**Two key domains, never merged.** Stegasoo uses AES-256-GCM with keys derived via
|
||||||
|
Argon2id from user-supplied factors. Verisoo uses Ed25519 for signing. These serve
|
||||||
|
different security purposes and are kept strictly separate.
|
||||||
|
|
||||||
|
**Killswitch priority.** The killswitch destroys all data under `~/.soosef/`, including
|
||||||
|
the audit log. This is intentional -- in a field compromise scenario, data destruction
|
||||||
|
takes precedence over audit trail preservation.
|
||||||
|
|
||||||
|
**Offline-first.** All static assets are vendored (no CDN calls). Pip wheels can be
|
||||||
|
bundled for fully airgapped installation. No network access is required for any core
|
||||||
|
functionality.
|
||||||
|
|
||||||
|
**Web UI hardening:**
|
||||||
|
- CSRF protection via Flask-WTF
|
||||||
|
- Session timeout (default: 15 minutes)
|
||||||
|
- Login rate limiting with lockout (5 attempts, 15-minute lockout)
|
||||||
|
- HTTPS by default with auto-generated self-signed certificates
|
||||||
|
- EXIF stripping on steganographic encode to prevent metadata leakage
|
||||||
|
|
||||||
|
**Subprocess isolation.** Steganographic operations run in a subprocess boundary
|
||||||
|
(`subprocess_stego.py`) to contain crashes and prevent memory corruption from affecting
|
||||||
|
the main web server process.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### `/health` endpoint
|
||||||
|
|
||||||
|
The web UI exposes a `/health` endpoint that reports installed capabilities:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"capabilities": ["stego-lsb", "stego-dct", "attest", "fieldkit", "chain"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful for monitoring and for clients to discover which extras are installed.
|
||||||
|
|
||||||
|
### FastAPI (optional)
|
||||||
|
|
||||||
|
Install the `api` extra for a standalone FastAPI REST interface:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install "soosef[api]"
|
||||||
|
```
|
||||||
|
|
||||||
|
This provides `soosef.api` with a FastAPI application served by uvicorn, suitable for
|
||||||
|
programmatic integration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/alee/soosef.git
|
||||||
|
cd soosef
|
||||||
|
pip install -e ".[dev]"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest # Run tests with coverage
|
||||||
|
black src/ tests/ frontends/ # Format (100-char line length)
|
||||||
|
ruff check src/ tests/ frontends/ --fix # Lint
|
||||||
|
mypy src/ # Type check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code style
|
||||||
|
|
||||||
|
- Black with 100-character line length
|
||||||
|
- Ruff with E, F, I, N, W, UP rule sets
|
||||||
|
- mypy strict mode with missing imports ignored
|
||||||
|
- Imperative commit messages (e.g., "Add killswitch purge confirmation")
|
||||||
|
|
||||||
|
### Python support
|
||||||
|
|
||||||
|
Python 3.11, 3.12, 3.13, and 3.14.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License. See [LICENSE](LICENSE) for details.
|
||||||
|
|||||||
638
docs/deployment.md
Normal file
638
docs/deployment.md
Normal file
@ -0,0 +1,638 @@
|
|||||||
|
# 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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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)**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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)**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ufw default deny outgoing
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 Disable unnecessary services
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl disable bluetooth
|
||||||
|
sudo systemctl disable avahi-daemon
|
||||||
|
sudo systemctl disable triggerhappy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Installation
|
||||||
|
|
||||||
|
### 4.1 System dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install "soosef[web,cli,fieldkit]"
|
||||||
|
```
|
||||||
|
|
||||||
|
For hardware killswitch support via GPIO:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir soosef-wheels
|
||||||
|
pip download "soosef[rpi]" -d soosef-wheels/
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy the `soosef-wheels/` directory to a USB drive, then on the Pi:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install --no-index --find-links /media/usb/soosef-wheels/ "soosef[rpi]"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 Verify installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
soosef --version
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Initial Setup
|
||||||
|
|
||||||
|
### 5.1 Initialize SooSeF
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
soosef fieldkit deadman arm --interval 12 --grace 1
|
||||||
|
```
|
||||||
|
|
||||||
|
This requires a check-in every 12 hours, with a 1-hour grace period.
|
||||||
|
|
||||||
|
Check in:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
soosef fieldkit checkin
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also check in through the web UI at `/fieldkit`.
|
||||||
|
|
||||||
|
Check status:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
soosef keys restore --input /media/usb/soosef-backup.enc
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.3 Key rotation
|
||||||
|
|
||||||
|
Generate a new channel key (the old one is overwritten):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
soosef init --no-identity
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate a new identity (the old one is overwritten -- all previous attestations will
|
||||||
|
reference the old fingerprint):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -k https://<pi-ip>:5000/health
|
||||||
|
```
|
||||||
|
|
||||||
|
The `-k` flag skips certificate verification for self-signed certs.
|
||||||
|
|
||||||
|
### System status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dphys-swapfile swapon
|
||||||
|
pip install scipy
|
||||||
|
sudo dphys-swapfile swapoff
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use pre-built wheels:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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**
|
||||||
|
|
||||||
|
1. Check that `host` is set to `0.0.0.0` in config, not `127.0.0.1`
|
||||||
|
2. Check firewall: `sudo ufw status` -- port 5000 must be allowed
|
||||||
|
3. Check the service is running: `sudo systemctl status soosef`
|
||||||
|
4. 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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -la ~/.soosef/
|
||||||
|
```
|
||||||
Loading…
Reference in New Issue
Block a user