fieldwitness/docs/deployment.md
Aaron D. Lee 428750e971
Some checks failed
CI / lint (push) Failing after 1m4s
CI / typecheck (push) Failing after 33s
Update all documentation for post-consolidation feature set
README.md (608 lines):
- Added 11 new feature sections: extract-then-strip EXIF, federation,
  timestamp anchoring, selective disclosure, evidence packages, cold
  archives, source drop box, key rotation/recovery, cover mode
- Expanded steganography (transport-aware, carrier tracking), attestation
  (non-image files, investigation namespaces, derivation lineage),
  fieldkit (forensic scrub, webhook, self-uninstall)
- Added Cross-Domain Applications section (human rights, research,
  elections, supply chain, art, whistleblowing, environment)
- Updated CLI reference with chain anchor/disclose/export commands
- Updated architecture with all new modules and data directory layout

CLAUDE.md (155 lines):
- Added metadata.py, evidence.py, archive.py, carrier_tracker.py,
  anchors.py, exchange.py, dropbox blueprint to architecture tree
- Added 7 new design decisions (extract-then-strip, CSRF exemption,
  client-side hashing, ImageHashes generalization, lazy paths,
  two-way federation, chain record types)

docs/deployment.md (1139 lines):
- Added 5 new operational sections: source drop box setup, chain
  anchoring procedures, cross-org federation, evidence/archive
  workflows, cover/duress mode
- Updated killswitch section with full 10-step destruction sequence
- Updated config table with all new fields
- Added 5 new troubleshooting entries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:36:58 -04:00

36 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
    anchors/            External timestamp anchors
  auth/                 Web UI user database (SQLite)
  certs/                Self-signed TLS certificates
  fieldkit/             Killswitch, deadman, tamper, USB, geofence state
  temp/                 Ephemeral upload/processing files
    dropbox/            Source drop box submissions
  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 after exceeding failed login attempts.
default_embed_mode auto Default steganographic embedding mode for Stegasoo.
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. soosef status reports overdue backups.
cover_name "" If set, used as the CN in the self-signed SSL certificate instead of "localhost". See Section 14 (Cover/Duress Mode).
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,
  "deadman_warning_webhook": "https://hooks.example.com/alert",
  "killswitch_enabled": true,
  "backup_reminder_days": 3,
  "cover_name": "Local Inventory Manager"
}

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 via deadman_warning_webhook) during the grace period, then execute a full purge if the grace period expires. The webhook must be a public URL -- SSRF protection blocks private/internal IP ranges.

For cron-based enforcement outside the web server (e.g., on a headless node), use:

soosef fieldkit check-deadman

Exit codes: 0 = not armed or not overdue, 1 = unexpected error, 2 = killswitch fired.

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 snapshot

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.

8.5 Killswitch

The killswitch destroys all key material and data in a deliberate order designed to maximize what is gone before any interruption. The destruction sequence is:

  1. Ed25519 identity keys (most critical -- without these, signed attestations cannot be forged and encrypted data is unrecoverable)
  2. AES-256-GCM channel key
  3. Flask session secret
  4. Auth database (user accounts)
  5. Attestation log and chain data
  6. Temp files and audit log
  7. Configuration
  8. System journal entries for the soosef unit
  9. Deep forensic scrub (see below)
  10. Self-uninstall of the soosef pip package

Trigger manually:

soosef fieldkit purge --confirm CONFIRM-PURGE

Deep forensic scrub. When the killswitch fires with ALL scope, it performs a deep forensic scrub that removes traces of SooSeF beyond the ~/.soosef/ directory:

  • Python bytecache: removes all __pycache__ directories and .pyc files for soosef, stegasoo, and verisoo from site-packages
  • pip dist-info: removes package metadata directories that would reveal what was installed
  • pip download cache: removes cached wheels and source distributions under ~/.cache/pip/ matching soosef/stegasoo/verisoo
  • Shell history: rewrites ~/.bash_history, ~/.zsh_history, and fish history to remove all lines containing "soosef"
  • Self-uninstall: runs pip uninstall -y soosef to remove the package from the virtual environment

After a full purge, the system will show minimal evidence that SooSeF was ever installed. Note that this is best-effort -- filesystem journal entries, inode metadata, and flash wear-leveling remnants may still exist. For complete deniability, use full-disk encryption (LUKS) and physically destroy the storage media.


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 export /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 import /media/usb/soosef-backup.enc

9.3 Key rotation

Rotate the identity keypair (old key is archived, not destroyed):

soosef keys rotate-identity

Rotate the channel key:

soosef keys rotate-channel

After rotating keys, take a fresh backup immediately. Notify all collaborators of the new identity fingerprint so they can update their trusted-key lists.

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. Source Drop Box

The source drop box provides a SecureDrop-like anonymous file intake that runs inside SooSeF. Sources do not need a SooSeF account -- they receive a one-time upload URL and submit files through their browser.

10.1 Creating tokens

An admin creates a time-limited upload token through the web UI at /dropbox/admin or through the admin panel. Each token has:

  • Label: a human-readable name for the source (stored server-side only)
  • Expiry: how many hours the link is valid (default: 24)
  • Max files: maximum number of uploads allowed on this link (default: 10)

Tokens are 32-byte cryptographically random URL-safe strings. Once created, the admin receives a URL of the form https://<host>:<port>/dropbox/upload/<token>.

10.2 Sharing URLs securely

Share the upload URL with the source over an already-secure channel:

  • In-person on paper (best for high-risk sources)
  • Encrypted messaging (Signal, Wire)
  • Verbal dictation over a secure voice call

Never send drop box URLs over unencrypted email or SMS.

10.3 What happens on upload

When a source uploads files:

  1. The browser computes SHA-256 fingerprints client-side (via SubtleCrypto) before upload so the source has a verifiable record of what they submitted
  2. EXIF metadata is extracted for evidentiary fields (GPS, timestamp) and then stripped from the stored copy to protect source device information
  3. The original bytes are attested (signed) before stripping, so the attestation hash matches what the source actually submitted
  4. The source receives a receipt code (HMAC-derived from the file hash and token) for each file
  5. The token usage counter increments; once max files is reached, the link stops accepting

10.4 Receipt verification

Sources can verify their submission was received by posting their receipt code to /dropbox/verify-receipt. This returns the filename, SHA-256, and reception timestamp if the receipt is valid.

10.5 Operational security for the drop box

  • No SooSeF branding: the upload page is a minimal HTML form with no identifying marks, styled generically
  • No authentication required: the source never creates an account or reveals identity information
  • Token self-destruction: tokens are deleted from the SQLite database after expiry; expired tokens are cleaned up on every admin page load
  • Revocation: admins can revoke tokens immediately from /dropbox/admin
  • Tor compatibility: the upload page is a self-contained HTML page with inline JavaScript (SubtleCrypto only) and no external resources. It works over Tor Browser with JavaScript enabled. No CDN, no fonts, no analytics
  • No IP logging: SooSeF does not log source IP addresses. Ensure your reverse proxy (if any) also does not log access. If running behind Tor, the source's real IP is never visible to the server
  • Receipt codes are deterministic: the receipt is an HMAC of the file's SHA-256 keyed by the token, so the source can independently verify it corresponds to their file

If operating in a high-risk environment, consider running SooSeF as a Tor hidden service (.onion address). Configure a torrc hidden service pointing to 127.0.0.1:5000 and share the .onion URL instead of a LAN address.

10.6 Drop box file storage

Uploaded files are stored in ~/.soosef/temp/dropbox/ with filenames derived from the SHA-256 prefix. This directory has mode 0700. Token metadata and receipts are stored in a SQLite database at ~/.soosef/auth/dropbox.db.


11. Chain Anchoring

Chain anchoring externally proves that your attestation chain existed before a given time. A single anchor for the chain head implicitly timestamps every record that preceded it, because the chain is append-only with hash linkage.

11.1 When to anchor

Anchor your chain:

  • Before sharing evidence with a third party (proves the chain existed before disclosure)
  • At regular intervals (daily or weekly) to establish a timeline
  • Before and after major investigations or events
  • Before key rotation (locks the existing chain state)

11.2 Automated anchoring (RFC 3161 TSA)

If the device has internet access (even temporarily), submit the chain head to a Timestamping Authority:

soosef chain anchor --tsa https://freetsa.org/tsr

This sends the chain head digest to the TSA, receives a signed timestamp token, and saves both the anchor and the TSA response as a JSON file under ~/.soosef/chain/anchors/.

The TSA token is a cryptographically signed proof from a third party that the hash existed at the timestamp. This is legally stronger than a self-asserted timestamp.

11.3 Manual anchoring

Without --tsa, the command exports the anchor hash for manual external submission:

soosef chain anchor

This prints a compact text block containing the chain ID, head index, record count, and digest hash. Publish this text to any external witness:

  • Tweet or public social media post (timestamped by the platform)
  • Email to a trusted third party (timestamped by the mail server)
  • Newspaper classified advertisement
  • Blockchain transaction (e.g., Bitcoin OP_RETURN)
  • Notarized document

The anchor file is saved locally regardless of whether a TSA was used.

11.4 Airgapped anchoring procedure

For fully airgapped deployments:

  1. Run soosef chain anchor on the airgapped device
  2. Copy the printed anchor text to a USB drive (text file, photograph of screen, or paper transcription)
  3. On an internet-connected device, publish the anchor text to one or more external witnesses
  4. Document the publication (URL, screenshot, transaction ID) and store it alongside the USB key backup

11.5 Verifying anchors

To verify that the current chain state matches a previously created anchor:

soosef chain verify

This checks all hash linkage and signatures in the chain. If the chain has been tampered with since the anchor was created, verification will fail.


12. Cross-Organization Federation

Federation allows multiple SooSeF instances to exchange attestation records for collaborative investigations. Bundles are self-authenticating: each record carries the signer's public key, so the importer can verify signatures against their trust store.

12.1 Exchanging trust keys

Before two organizations can exchange attestation bundles, they must trust each other's identity keys.

On Organization A:

# Export public key
cp ~/.soosef/identity/public.pem /media/usb/org-a-pubkey.pem

On Organization B:

# Import Org A's key and verify fingerprint
soosef keys trust --import /media/usb/org-a-pubkey.pem

Always verify fingerprints out-of-band (in person, over a known-secure voice channel). Repeat in both directions so each organization trusts the other.

12.2 Exporting attestation bundles

Export a JSON bundle containing attestation records and chain data:

soosef chain export --output /media/usb/investigation-bundle.zip

To export only records from a specific index range:

soosef chain export --start 100 --end 200 --output /media/usb/partial-bundle.zip

The export includes:

  • Attestation records with full signatures
  • Chain records with hash linkage
  • The signer's public key
  • A standalone verification script (requires only Python + cryptography)
  • A human-readable README

12.3 Importing attestation bundles

On the receiving organization's SooSeF instance:

  • Records are imported into the local attestation log with a federated_from metadata tag
  • Records signed by untrusted fingerprints are rejected (unless trust-on-first-use is used)
  • Duplicate records (matching SHA-256) are skipped automatically

12.4 Delivery acknowledgments

When a bundle is imported and the receiving instance has a chain store and private key, SooSeF automatically creates a delivery acknowledgment record in the local chain. This records the bundle hash, sender fingerprint, and count of records received. The acknowledgment provides a cryptographic receipt that the bundle was delivered and ingested.

To produce evidence for a court order or legal discovery request without revealing the entire chain:

soosef chain disclose --indices 42,43,44 --output disclosure.json

This exports a proof bundle where the selected records are shown in full and all other records appear only as hashes. A third party can verify that the selected records are part of an unbroken hash chain without seeing the contents of other records.

12.6 Federation on airgapped networks

All federation is designed for offline/sneakernet operation:

  1. Export the bundle to a USB drive on the sending instance
  2. Physically carry the USB to the receiving instance
  3. Import the bundle
  4. Export the delivery acknowledgment back on USB if needed

No network connectivity is required at any point.


13. Evidence Packages and Cold Archives

SooSeF provides two export formats for preserving evidence outside the running instance.

13.1 Evidence packages

An evidence package is a self-contained ZIP that bundles everything needed for independent verification of specific attested images. Use evidence packages when you need to hand off a subset of evidence to a third party (lawyer, court, partner organization).

Contents of an evidence package:

  • images/ -- original image files
  • manifest.json -- attestation records and chain data for those images
  • public_key.pem -- signer's Ed25519 public key
  • verify.py -- standalone verification script (requires only Python 3.11+ and cryptography)
  • README.txt -- human-readable instructions

The package is self-contained. No SooSeF installation is required to verify the evidence. The standalone verify.py script checks image SHA-256 hashes against attestation records and verifies chain hash linkage.

When to create evidence packages:

  • Before handing evidence to a legal team
  • When sharing with a partner organization that does not run SooSeF
  • For court submission (the self-contained verifier is the key feature)
  • Before any action that might destroy the running instance (travel through hostile checkpoints, anticipated raids)

13.2 Cold archives

A cold archive is a full snapshot of the entire SooSeF evidence store, designed for long-term preservation. It follows OAIS (ISO 14721) alignment: the archive is self-describing, includes its own verification code, and documents the cryptographic algorithms used.

Contents of a cold archive:

  • chain/ -- raw append-only hash chain binary, state checkpoint, and anchor files
  • attestations/ -- full verisoo attestation log and LMDB index
  • keys/public.pem -- signer's public key
  • keys/bundle.enc -- encrypted key bundle (optional, password-protected)
  • keys/trusted/ -- trusted collaborator public keys
  • manifest.json -- archive metadata and integrity hashes
  • verify.py -- standalone verification script
  • ALGORITHMS.txt -- documents all cryptographic algorithms and formats used (Ed25519, SHA-256, AES-256-GCM, Argon2id, CBOR, etc.) so the archive remains verifiable even if SooSeF no longer exists
  • README.txt -- human-readable description

To restore a cold archive on a fresh SooSeF instance:

soosef archive import <archive.zip>

When to create cold archives:

  • At regular intervals (weekly or monthly) as part of your backup strategy
  • Before key rotation
  • Before traveling with the device
  • Before anticipated risk events
  • When archiving a completed investigation

For legal discovery and court proceedings:

  1. Use soosef chain disclose for selective disclosure (Section 12.5) when you must respond to a specific request without revealing the full chain
  2. Use evidence packages for handing specific images and their attestations to counsel
  3. Use cold archives when full preservation is required

All three formats include standalone verification scripts so that the receiving party does not need to install SooSeF.

13.4 Long-term archival best practices

  • Store cold archives on at least two separate physical media (USB drives, optical discs)
  • Keep one copy offsite (safe deposit box, trusted third party)
  • Include the encrypted key bundle in the archive (set a strong passphrase and store it separately from the archive media)
  • Write the passphrase on paper and store it in a different physical location from the archive media
  • Periodically verify archive integrity: unzip and run python verify.py
  • The ALGORITHMS.txt file documents everything needed to write a verifier from scratch, even decades from now

14. Cover/Duress Mode

Cover mode disguises a SooSeF installation so that casual inspection of the device does not immediately reveal it as a security toolkit.

14.1 Renaming the data directory

By default, SooSeF stores everything under ~/.soosef/. To use an inconspicuous name, set the SOOSEF_DATA_DIR environment variable:

export SOOSEF_DATA_DIR=~/.local/share/inventory
soosef init

All SooSeF commands respect this variable. Add it to the soosef user's shell profile or the systemd service file:

Environment="SOOSEF_DATA_DIR=/home/soosef/.local/share/inventory"

You can also pass --data-dir to any command:

soosef --data-dir ~/.local/share/inventory serve --host 0.0.0.0

14.2 Cover name for SSL certificates

Set cover_name in config to change the Common Name (CN) in the self-signed SSL certificate. Without this, the certificate CN defaults to "localhost". With a cover name, a browser inspector sees a plausible-looking certificate:

{
  "cover_name": "Local Inventory Manager"
}

Delete ~/.soosef/certs/cert.pem and restart the server to regenerate the certificate with the new CN.

14.3 Portable USB operation

SooSeF can run entirely from a USB drive:

  1. Install SooSeF into a virtualenv on the USB drive
  2. Set SOOSEF_DATA_DIR to a directory on the USB
  3. Run soosef serve from the USB

When you remove the USB, no trace of SooSeF remains on the host machine (assuming no swap, no core dumps, and the host filesystem was not used for temp files).

Combine with the cover_name setting and a renamed data directory for maximum deniability.


15. 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.


16. 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. Use --json for machine-readable output.

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

  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:

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/

Drop box tokens expire immediately

Token expiry is checked against UTC. If the Pi's system clock is wrong, tokens may appear expired as soon as they are created. Verify the clock:

date -u

On airgapped systems without NTP, set the clock manually before creating tokens:

sudo date -s "2026-04-01 12:00:00"

Chain anchor TSA submission fails

TSA submission requires network access. On an airgapped device, use manual anchoring instead (soosef chain anchor without --tsa). If the TSA URL is unreachable, the anchor is still saved locally -- only the external timestamp token is missing.

SSL certificate shows wrong name

If you set cover_name after the certificate was already generated, delete the old certificate and restart:

rm ~/.soosef/certs/cert.pem ~/.soosef/certs/key.pem
sudo systemctl restart soosef

Account lockout after repeated failed logins

After login_lockout_attempts (default: 5) failed login attempts, the account is locked for login_lockout_minutes (default: 15) minutes. Wait for the lockout to expire, or restart the server to clear lockout state.

Evidence package verify.py fails

The standalone verification script requires Python 3.11+ and the cryptography package. Install it in a fresh virtualenv on the verifying machine:

python3 -m venv verify-env
source verify-env/bin/activate
pip install cryptography
python verify.py