Update all documentation for post-consolidation feature set
Some checks failed
CI / lint (push) Failing after 1m4s
CI / typecheck (push) Failing after 33s

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>
This commit is contained in:
Aaron D. Lee 2026-04-01 21:36:58 -04:00
parent fef552b9c1
commit 428750e971
3 changed files with 709 additions and 53 deletions

View File

@ -34,6 +34,9 @@ src/soosef/ Core library
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
metadata.py Extract-then-strip EXIF pipeline with field classification
evidence.py Self-contained evidence package export (ZIP with verify.py)
archive.py Cold archive export for long-term preservation (OAIS-aligned)
stegasoo/ Steganography engine (inlined from stegasoo) stegasoo/ Steganography engine (inlined from stegasoo)
encode.py / decode.py Core encode/decode API encode.py / decode.py Core encode/decode API
@ -54,6 +57,7 @@ src/soosef/ Core library
constants.py Magic bytes, version constants constants.py Magic bytes, version constants
cli.py Stegasoo-specific CLI commands cli.py Stegasoo-specific CLI commands
api.py / api_auth.py Stegasoo REST API + auth api.py / api_auth.py Stegasoo REST API + auth
carrier_tracker.py Carrier image reuse tracking (warns on reuse)
image_utils.py / audio_utils.py / video_utils.py image_utils.py / audio_utils.py / video_utils.py
keygen.py / qr_utils.py / recovery.py / debug.py / utils.py keygen.py / qr_utils.py / recovery.py / debug.py / utils.py
platform_presets.py Social-media-aware encoding presets platform_presets.py Social-media-aware encoding presets
@ -75,10 +79,12 @@ src/soosef/ Core library
api.py Verisoo REST API api.py Verisoo REST API
federation/ Federated attestation chain system federation/ Federated attestation chain system
chain.py Hash chain construction chain.py Hash chain construction + key rotation/recovery/delivery-ack records
entropy.py Entropy source for chain seeds entropy.py Entropy source for chain seeds
models.py ChainEntry, ChainState dataclasses models.py ChainEntry, ChainState dataclasses
serialization.py CBOR chain serialization + export serialization.py CBOR chain serialization + export
anchors.py RFC 3161 timestamps + manual chain anchors
exchange.py Cross-org attestation bundle export/import
keystore/ Unified key management keystore/ Unified key management
manager.py Owns all key material (channel keys + Ed25519 identity) manager.py Owns all key material (channel keys + Ed25519 identity)
@ -106,6 +112,9 @@ frontends/web/ Unified Flask web UI
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)
dropbox.py /dropbox/* (token-gated anonymous source upload)
templates/dropbox/
admin.html Drop box admin panel
frontends/cli/ CLI package init (main entry point is src/soosef/cli.py) frontends/cli/ CLI package init (main entry point is src/soosef/cli.py)
``` ```
@ -123,14 +132,23 @@ Stegasoo and Verisoo are inlined subpackages, not separate pip packages:
- **Two key domains, never merged**: Stegasoo AES-256-GCM (derived from factors) and - **Two key domains, never merged**: Stegasoo AES-256-GCM (derived from factors) and
Verisoo Ed25519 (signing identity) are separate security concerns Verisoo Ed25519 (signing identity) are separate security concerns
- **Extract-then-strip model**: Stego strips all EXIF (carrier is vessel); attestation
extracts evidentiary EXIF then strips dangerous fields (device serial, etc.)
- **subprocess_stego.py copies verbatim** from stegasoo — it's a crash-safety boundary - **subprocess_stego.py copies verbatim** from stegasoo — it's a crash-safety boundary
- **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, dropbox — clean route separation
- **Flask-WTF**: CSRF protection on all form endpoints - **Flask-WTF**: CSRF protection on all form endpoints; drop box is CSRF-exempt (sources
don't have sessions)
- **Client-side SHA-256**: Drop box upload page uses SubtleCrypto for pre-upload hashing
- **Waitress**: Production WSGI server (replaces dev-only Flask server) - **Waitress**: Production WSGI server (replaces dev-only Flask server)
- **FastAPI option**: `soosef.api` provides a REST API alternative to the Flask web UI - **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` - **Pluggable backends**: Stego backends (LSB, DCT) registered via `backends/registry.py`
- **ImageHashes generalized**: phash/dhash now optional, enabling non-image attestation
- **Lazy path resolution**: Modules use `import soosef.paths as _paths` for --data-dir support
- **Two-way federation**: Delivery acknowledgment records enable handshake proof
- **Chain record types**: CONTENT_TYPE_KEY_ROTATION, CONTENT_TYPE_KEY_RECOVERY,
CONTENT_TYPE_DELIVERY_ACK (in federation/chain.py)
## Code conventions ## Code conventions

203
README.md
View File

@ -14,10 +14,13 @@
SooSeF combines steganography, provenance attestation, and field security tools into a SooSeF combines steganography, provenance attestation, and field security tools into a
single package designed for airgapped and resource-constrained environments. It lets you: single package designed for airgapped and resource-constrained environments. It lets you:
- **Hide messages** in images, audio, and video using multiple steganographic techniques - **Hide messages** in images, audio, and video using multiple steganographic techniques, with transport-aware encoding for lossy channels (WhatsApp, Signal, Telegram)
- **Prove authenticity** of photos and documents with Ed25519 signatures and Merkle trees - **Prove authenticity** of photos, documents, and arbitrary files with Ed25519 signatures, Merkle-style hash chains, and RFC 3161 trusted timestamps
- **Protect data in the field** with a killswitch, dead man's switch, tamper detection, USB device whitelisting, and GPS geofencing - **Protect data in the field** with a killswitch (including deep forensic scrub and self-uninstall), dead man's switch with webhook warnings, tamper detection, USB device whitelisting, and GPS geofencing
- **Manage cryptographic keys** with identity rotation, channel key generation, and encrypted key bundle export/import - **Manage cryptographic keys** with identity rotation, channel key generation, encrypted key bundle export/import, QR code sharing, trust store management, and identity recovery from chain
- **Federate attestations** across organizations with signed exchange bundles, delivery acknowledgments, selective disclosure for legal discovery, and investigation namespaces
- **Accept anonymous submissions** through a SecureDrop-style source drop box with token-gated uploads, client-side SHA-256 hashing, and automatic EXIF extraction/stripping
- **Preserve evidence long-term** with self-contained evidence packages and OAIS-aligned cold archives that include standalone verification scripts and algorithm documentation
Stegasoo (steganography, v4.3.0) and Verisoo (attestation, v0.1.0) are included as 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 subpackages (`import soosef.stegasoo`, `import soosef.verisoo`). Everything ships as one
@ -47,32 +50,111 @@ channel key, writes a default config, and starts an HTTPS web UI on
- **DCT encoding** -- frequency-domain hiding in JPEG images (requires `stego-dct` extra) - **DCT encoding** -- frequency-domain hiding in JPEG images (requires `stego-dct` extra)
- **Audio steganography** -- hide data in WAV/FLAC audio (requires `stego-audio` extra) - **Audio steganography** -- hide data in WAV/FLAC audio (requires `stego-audio` extra)
- **Video steganography** -- frame-level encoding - **Video steganography** -- frame-level encoding
- **Transport-aware encoding** -- `--transport whatsapp|signal|telegram|discord|email|direct` auto-selects the right encoding mode and carrier resolution for lossy messaging platforms. WhatsApp/Signal/Telegram force DCT/JPEG mode and pre-resize the carrier to survive recompression
- **Carrier reuse tracking** -- warns when a carrier image has been used before, since comparing two versions of the same carrier trivially reveals steganographic modification
- AES-256-GCM encryption with Argon2id key derivation - AES-256-GCM encryption with Argon2id key derivation
- EXIF stripping on encode to prevent metadata leakage - EXIF stripping on encode to prevent metadata leakage
- Compression support (zstandard, optional LZ4) - Compression support (zstandard, optional LZ4)
### Attestation (Verisoo) ### Attestation (Verisoo)
- Ed25519 digital signatures for images and files - Ed25519 digital signatures for images and arbitrary files (CSV, documents, sensor data)
- Perceptual hashing (ImageHash) for tamper-evident photo attestation - Perceptual hashing (pHash, dHash) for tamper-evident photo attestation; SHA-256-only mode for non-image files
- LMDB-backed attestation storage - LMDB-backed attestation storage
- Append-only hash chain (CBOR-encoded) with Merkle tree verification - Append-only hash chain (CBOR-encoded) with Merkle tree verification
- Batch attestation for directories - Batch attestation for directories
- **Investigation namespaces** -- tag and filter attestations by case or project
- **Derived work lineage** -- parent-child attestation tracking for editorial workflows
- **Chain position proof** -- verification receipts include the record's position in the hash chain
### Extract-Then-Strip EXIF Pipeline
Resolves the tension between steganography (strip everything to protect sources) and
attestation (preserve everything to prove provenance):
1. Extract all EXIF metadata from the original image bytes
2. Classify fields as **evidentiary** (GPS coordinates, timestamp -- valuable for provenance) or **dangerous** (device serial number, firmware version -- could identify the source)
3. Preserve evidentiary fields in the attestation record
4. Strip all metadata from the stored/display copy
### Cross-Organization Federation
- **Attestation exchange** -- export signed bundles of attestation records and chain data for offline transfer to partner organizations
- **Delivery acknowledgments** -- when an organization imports a bundle, a `soosef/delivery-ack-v1` chain record is signed and can be shared back, creating a two-way federation handshake
- **Trust store** -- import collaborator Ed25519 public keys; only records signed by trusted keys are imported during federation
- **Investigation filtering** -- export/import only records tagged with a specific investigation
### External Timestamp Anchoring
Two mechanisms to externally prove that the chain head existed before a given time:
- **RFC 3161 TSA** -- automated submission to any RFC 3161 Timestamping Authority (e.g., FreeTSA). The signed timestamp token is saved alongside the chain
- **Manual anchors** -- export the chain head hash as a compact string for manual submission to any external witness (blockchain transaction, newspaper classified, tweet, email to a TSA)
A single anchor for the chain head implicitly timestamps every record that preceded it, because the chain is append-only with hash linkage.
### Selective Disclosure
Produce verifiable proofs for specific chain records while keeping others redacted. Selected records are included in full; non-selected records appear only as hashes. A third party can verify that the disclosed records are part of an unbroken chain without seeing the contents of other records. Designed for legal discovery, court orders, and FOIA responses.
### Evidence Packages
Self-contained ZIP bundles for handing evidence to lawyers, courts, or archives:
- Original images
- Attestation records with full Ed25519 signatures
- Chain segment with hash linkage
- Signer's public key
- `verify.py` -- standalone verification script that requires only Python 3.11+ and the `cryptography` pip package (no SooSeF installation needed)
- Human-readable README
### Cold Archive
Full-state export for long-term evidence preservation (10+ year horizon), aligned with OAIS (ISO 14721):
- Raw chain binary and state checkpoint
- Attestation log and LMDB index
- External timestamp anchors
- Public key and trusted collaborator keys
- Encrypted key bundle (optional, password-protected)
- `ALGORITHMS.txt` documenting every cryptographic algorithm, parameter, and format used
- `verify.py` standalone verifier
- `manifest.json` with SHA-256 integrity hashes of key files
### Source Drop Box
SecureDrop-style anonymous intake built into the SooSeF web UI:
- Admin creates a time-limited upload token with a configurable file limit
- Source opens the token URL (no account or SooSeF branding -- source safety)
- **Client-side SHA-256** via SubtleCrypto runs in the browser before upload, so the source can independently verify what they submitted
- Files are run through the extract-then-strip EXIF pipeline and auto-attested on receipt
- Source receives HMAC-derived receipt codes that prove delivery
- Tokens and receipts are stored in SQLite; tokens auto-expire
### Key Rotation and Recovery
- **Key rotation** -- both identity (Ed25519) and channel (AES) keys can be rotated. The chain records the rotation as a `soosef/key-rotation-v1` record signed by the OLD key, creating a cryptographic trust chain
- **Identity recovery** -- after device loss, a new key can be introduced via a `soosef/key-recovery-v1` chain record. The record carries the old fingerprint and optional cosigner fingerprints for audit
- **Channel key only export** -- share just the channel key (not identity keys) with collaborators via encrypted file or QR code (`soosef-channel:` URI scheme)
- **Backup tracking** -- records when the last backup was taken and warns when overdue
### Fieldkit ### Fieldkit
- **Killswitch** -- emergency destruction of all data under `~/.soosef/` - **Killswitch** -- emergency destruction of all data under `~/.soosef/`, ordered by sensitivity (keys first, then data, then logs). Includes:
- **Dead man's switch** -- automated purge if check-in is missed - **Deep forensic scrub** -- removes `__pycache__`, `.pyc`, pip `dist-info`, pip download cache, and scrubs shell history entries containing "soosef"
- **Self-uninstall** -- runs `pip uninstall -y soosef` as the final step
- **System log clearing** -- best-effort journald vacuum on Linux
- **Dead man's switch** -- automated purge if check-in is missed, with a configurable grace period. During the grace period, a **webhook warning** is sent (POST to a configured URL) and a local warning file is written before the killswitch fires
- **Tamper detection** -- file integrity monitoring with baseline snapshots - **Tamper detection** -- file integrity monitoring with baseline snapshots
- **USB whitelist** -- block or alert on unauthorized USB devices (Linux/pyudev) - **USB whitelist** -- block or alert on unauthorized USB devices (Linux/pyudev)
- **Geofence** -- GPS boundary enforcement with configurable radius - **Geofence** -- GPS boundary enforcement with configurable radius. Supports live GPS via **gpsd** (`get_current_location()` connects to `127.0.0.1:2947`)
- **Hardware killswitch** -- GPIO pin monitoring for Raspberry Pi physical button (configurable pin and hold duration)
### Key Management ### Cover / Duress Mode
- Two separate key domains: AES-256-GCM channel keys (stego) and Ed25519 identity keys (attestation) - **Configurable certificate CN** -- set `cover_name` in config to replace "SooSeF Local" in the self-signed TLS certificate
- Key rotation for both identity and channel keys - **Portable data directory** -- set `SOOSEF_DATA_DIR` to relocate all state to an arbitrary path (e.g., an innocuously named directory on a USB stick). All paths resolve lazily from `BASE_DIR`, so runtime overrides propagate correctly
- Encrypted key bundle export/import for secure backup and transfer
- QR code generation for key sharing
--- ---
@ -100,8 +182,8 @@ pip install "soosef[dev]" # All + pytest, black, ruff, mypy
| `stego-audio` | Audio steganography (pydub, soundfile, reedsolo) | | `stego-audio` | Audio steganography (pydub, soundfile, reedsolo) |
| `stego-compression` | LZ4 compression support | | `stego-compression` | LZ4 compression support |
| `attest` | Attestation features (imagehash, lmdb, exifread) | | `attest` | Attestation features (imagehash, lmdb, exifread) |
| `cli` | Click CLI with rich output and QR code support | | `cli` | Click CLI with rich output, QR code support, piexif |
| `web` | Flask web UI with Waitress/Gunicorn, includes attest + stego-dct | | `web` | Flask web UI with Waitress/Gunicorn, pyzbar QR scanning, includes attest + stego-dct |
| `api` | FastAPI REST API with uvicorn, includes stego-dct | | `api` | FastAPI REST API with uvicorn, includes stego-dct |
| `fieldkit` | Tamper monitoring (watchdog) and USB whitelist (pyudev) | | `fieldkit` | Tamper monitoring (watchdog) and USB whitelist (pyudev) |
| `federation` | Peer-to-peer attestation federation (aiohttp) | | `federation` | Peer-to-peer attestation federation (aiohttp) |
@ -174,7 +256,14 @@ soosef stego encode cover.png -r shared_photo.jpg -m "Secret" -o stego_output.pn
# Encode a file instead of text # Encode a file instead of text
soosef stego encode cover.png -r shared_photo.jpg -f document.pdf soosef stego encode cover.png -r shared_photo.jpg -f document.pdf
# Dry run — check capacity without encoding # Transport-aware encoding (auto-selects DCT/JPEG and resizes for the platform)
soosef stego encode cover.jpg -r shared.jpg -m "Secret" --transport whatsapp
soosef stego encode cover.jpg -r shared.jpg -m "Secret" --transport signal
soosef stego encode cover.jpg -r shared.jpg -m "Secret" --transport telegram
soosef stego encode cover.jpg -r shared.jpg -m "Secret" --transport email
soosef stego encode cover.jpg -r shared.jpg -m "Secret" --transport direct
# Dry run -- check capacity without encoding
soosef stego encode cover.png -r shared_photo.jpg -m "Secret" --dry-run soosef stego encode cover.png -r shared_photo.jpg -m "Secret" --dry-run
# Decode a message from a stego image (same reference + passphrase + PIN) # Decode a message from a stego image (same reference + passphrase + PIN)
@ -242,7 +331,7 @@ soosef fieldkit usb check # Check for unauthorized USB devices
soosef keys show # Display current key info soosef keys show # Display current key info
soosef keys export -o backup.enc # Export encrypted key bundle soosef keys export -o backup.enc # Export encrypted key bundle
soosef keys import -b backup.enc # Import key bundle soosef keys import -b backup.enc # Import key bundle
soosef keys rotate-identity # Generate new Ed25519 identity soosef keys rotate-identity # Generate new Ed25519 identity (records rotation in chain)
soosef keys rotate-channel # Generate new channel key soosef keys rotate-channel # Generate new channel key
``` ```
@ -250,11 +339,20 @@ soosef keys rotate-channel # Generate new channel key
```bash ```bash
soosef chain status # Show chain head, length, integrity soosef chain status # Show chain head, length, integrity
soosef chain verify # Verify entire chain integrity soosef chain verify # Verify entire chain integrity (hashes + signatures)
soosef chain show INDEX # Show a specific chain record soosef chain show INDEX # Show a specific chain record
soosef chain log --count 20 # Show recent chain entries soosef chain log --count 20 # Show recent chain entries
soosef chain backfill # Backfill existing attestations into chain soosef chain backfill # Backfill existing attestations into chain
soosef chain export --start 0 --end 100 -o chain.cbor
# Evidence export
soosef chain export --start 0 --end 100 -o chain.zip
# Selective disclosure (for legal discovery / court orders)
soosef chain disclose -i 5,12,47 -o disclosure.json
# External timestamp anchoring
soosef chain anchor # Manual anchor (prints hash for tweet/email/blockchain)
soosef chain anchor --tsa https://freetsa.org/tsr # RFC 3161 automated anchor
``` ```
--- ---
@ -262,7 +360,7 @@ soosef chain export --start 0 --end 100 -o chain.cbor
## Web UI ## Web UI
Start with `soosef serve`. The web UI provides authenticated access to all features Start with `soosef serve`. The web UI provides authenticated access to all features
through Flask blueprints. through Flask blueprints. Served by **Waitress** (production WSGI server) by default.
### Routes ### Routes
@ -273,6 +371,7 @@ through Flask blueprints.
| fieldkit | `/fieldkit/*` | Killswitch, dead man's switch, status dashboard | | fieldkit | `/fieldkit/*` | Killswitch, dead man's switch, status dashboard |
| keys | `/keys/*` | Key management, rotation, export/import | | keys | `/keys/*` | Key management, rotation, export/import |
| admin | `/admin/*` | User management (multi-user auth via SQLite) | | admin | `/admin/*` | User management (multi-user auth via SQLite) |
| dropbox | `/dropbox/admin`, `/dropbox/upload/<token>` | Source drop box: token creation (admin), anonymous upload (source), receipt verification |
| health | `/health` | Capability reporting endpoint (see API section) | | health | `/health` | Capability reporting endpoint (see API section) |
<!-- TODO: screenshots --> <!-- TODO: screenshots -->
@ -307,6 +406,7 @@ SooSeF loads configuration from `~/.soosef/config.json`. All fields have sensibl
| `chain_enabled` | bool | `true` | Enable attestation hash chain | | `chain_enabled` | bool | `true` | Enable attestation hash chain |
| `chain_auto_wrap` | bool | `true` | Auto-wrap attestations in chain records | | `chain_auto_wrap` | bool | `true` | Auto-wrap attestations in chain records |
| `backup_reminder_days` | int | `7` | Days before backup reminder | | `backup_reminder_days` | int | `7` | Days before backup reminder |
| `cover_name` | string | `""` | If set, used for SSL cert CN instead of "SooSeF Local" (cover/duress mode) |
| `gpio_killswitch_pin` | int | `17` | Raspberry Pi GPIO pin for hardware killswitch | | `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 | | `gpio_killswitch_hold_seconds` | float | `5.0` | Hold duration to trigger hardware killswitch |
@ -314,7 +414,7 @@ SooSeF loads configuration from `~/.soosef/config.json`. All fields have sensibl
| Variable | Description | | Variable | Description |
|---|---| |---|---|
| `SOOSEF_DATA_DIR` | Override the data directory (default: `~/.soosef`) | | `SOOSEF_DATA_DIR` | Override the data directory (default: `~/.soosef`). Enables portable USB mode and cover/duress directory naming |
--- ---
@ -326,21 +426,31 @@ SooSeF loads configuration from `~/.soosef/config.json`. All fields have sensibl
src/soosef/ src/soosef/
__init__.py Package init, __version__ __init__.py Package init, __version__
cli.py Click CLI (entry point: soosef) cli.py Click CLI (entry point: soosef)
paths.py All path constants (single source of truth) paths.py All path constants (lazy resolution from BASE_DIR)
config.py Unified config loader (dataclass + JSON) config.py Unified config loader (dataclass + JSON)
exceptions.py SoosefError base exception exceptions.py SoosefError base exception
metadata.py Extract-then-strip EXIF pipeline
evidence.py Self-contained evidence package export
archive.py Cold archive for long-term preservation (OAIS-aligned)
stegasoo/ Steganography engine (subpackage) stegasoo/ Steganography engine (subpackage)
encode.py Transport-aware encoding (--transport flag)
carrier_tracker.py Carrier reuse tracking and warnings
verisoo/ Attestation engine (subpackage) verisoo/ Attestation engine (subpackage)
models.py ImageHashes (images + arbitrary files), AttestationRecord
keystore/ keystore/
manager.py Key material management (channel + identity) manager.py Key material management (channel + identity + trust store + backup)
models.py KeyBundle, IdentityBundle dataclasses models.py KeyBundle, IdentityBundle dataclasses
export.py Encrypted key bundle export/import export.py Full bundle export, channel-key-only export, QR code sharing
federation/
chain.py Append-only hash chain (key rotation, recovery, delivery ack, selective disclosure)
anchors.py RFC 3161 timestamps + manual chain anchors
exchange.py Cross-org attestation bundle export/import
fieldkit/ fieldkit/
killswitch.py Emergency data destruction killswitch.py Emergency data destruction (deep forensic scrub, self-uninstall)
deadman.py Dead man's switch deadman.py Dead man's switch (webhook warnings)
tamper.py File integrity monitoring tamper.py File integrity monitoring
usb_monitor.py USB device whitelist (Linux/pyudev) usb_monitor.py USB device whitelist (Linux/pyudev)
geofence.py GPS boundary enforcement geofence.py GPS boundary enforcement (gpsd integration)
frontends/web/ frontends/web/
app.py Flask app factory (create_app()) app.py Flask app factory (create_app())
@ -354,6 +464,7 @@ frontends/web/
fieldkit.py /fieldkit/* fieldkit.py /fieldkit/*
keys.py /keys/* keys.py /keys/*
admin.py /admin/* admin.py /admin/*
dropbox.py /dropbox/* (source drop box)
``` ```
### Data directory (`~/.soosef/`) ### Data directory (`~/.soosef/`)
@ -362,15 +473,17 @@ frontends/web/
~/.soosef/ ~/.soosef/
config.json Unified configuration config.json Unified configuration
audit.jsonl Append-only audit trail audit.jsonl Append-only audit trail
carrier_history.json Carrier reuse tracking database
identity/ Ed25519 keypair (private.pem, public.pem, identity.meta.json) identity/ Ed25519 keypair (private.pem, public.pem, identity.meta.json)
stegasoo/ Channel key (channel.key) stegasoo/ Channel key (channel.key)
attestations/ Verisoo attestation store (log.bin, index/, peers.json) attestations/ Verisoo attestation store (log.bin, index/, peers.json)
chain/ Hash chain (chain.bin, state.cbor) chain/ Hash chain (chain.bin, state.cbor, anchors/)
auth/ Web UI auth database (soosef.db) auth/ Web UI auth database (soosef.db, dropbox.db)
certs/ Self-signed TLS certificates certs/ Self-signed TLS certificates
fieldkit/ Fieldkit state (deadman.json, tamper/, usb/, geofence.json) fieldkit/ Fieldkit state (deadman.json, tamper/, usb/, geofence.json)
temp/ Ephemeral file storage temp/ Ephemeral file storage (dropbox uploads)
instance/ Flask instance (sessions, secret key) instance/ Flask instance (sessions, secret key)
trusted_keys/ Collaborator Ed25519 public keys (trust store)
``` ```
Sensitive directories (`identity/`, `auth/`, `certs/`, and the root) are created with Sensitive directories (`identity/`, `auth/`, `certs/`, and the root) are created with
@ -386,11 +499,14 @@ different security purposes and are kept strictly separate.
**Killswitch priority.** The killswitch destroys all data under `~/.soosef/`, including **Killswitch priority.** The killswitch destroys all data under `~/.soosef/`, including
the audit log. This is intentional -- in a field compromise scenario, data destruction the audit log. This is intentional -- in a field compromise scenario, data destruction
takes precedence over audit trail preservation. takes precedence over audit trail preservation. The deep forensic scrub extends beyond
the data directory to remove Python bytecache, pip metadata, pip download cache, shell
history entries, and the soosef package itself.
**Offline-first.** All static assets are vendored (no CDN calls). Pip wheels can be **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 bundled for fully airgapped installation. No network access is required for any core
functionality. functionality. RFC 3161 timestamping and webhook warnings are optional features that
gracefully degrade when offline.
**Web UI hardening:** **Web UI hardening:**
- CSRF protection via Flask-WTF - CSRF protection via Flask-WTF
@ -398,11 +514,32 @@ functionality.
- Login rate limiting with lockout (5 attempts, 15-minute lockout) - Login rate limiting with lockout (5 attempts, 15-minute lockout)
- HTTPS by default with auto-generated self-signed certificates - HTTPS by default with auto-generated self-signed certificates
- EXIF stripping on steganographic encode to prevent metadata leakage - EXIF stripping on steganographic encode to prevent metadata leakage
- Dead man's switch webhook SSRF protection (blocks private/internal targets)
**Subprocess isolation.** Steganographic operations run in a subprocess boundary **Subprocess isolation.** Steganographic operations run in a subprocess boundary
(`subprocess_stego.py`) to contain crashes and prevent memory corruption from affecting (`subprocess_stego.py`) to contain crashes and prevent memory corruption from affecting
the main web server process. the main web server process.
**Chain integrity.** The append-only hash chain uses Ed25519 signatures and SHA-256
linkage. Key rotation records create a verifiable trust chain; identity recovery records
are auditable. Selective disclosure uses Merkle-style proofs so third parties can verify
specific records without accessing the full chain.
---
## Cross-Domain Applications
While SooSeF was designed for journalist and NGO field security, the attestation chain,
federation, and evidence packaging capabilities apply to a range of domains:
- **Human rights documentation** -- field workers attest photos and videos of incidents with GPS and timestamps, federate evidence to international partners, and produce court-ready evidence packages
- **Research integrity** -- researchers attest datasets (CSV, sensor readings) at collection time, creating a tamper-evident chain of custody. `ImageHashes.from_file()` supports arbitrary file types via SHA-256
- **Election monitoring** -- observers attest ballot images and tally sheets with location metadata, anchor the chain to an RFC 3161 TSA for independent time proof, and use selective disclosure for audit requests
- **Supply chain verification** -- attest inspection photos, sensor data, and certificates of origin at each stage. Federation enables multi-party chains across organizations
- **Art authentication** -- attest high-resolution photographs of artworks with device and location metadata, creating provenance records that survive format conversion via perceptual hashing
- **Corporate whistleblowing** -- the source drop box accepts anonymous uploads with client-side hashing. Cover mode (`cover_name`, `SOOSEF_DATA_DIR`) disguises the installation. The killswitch provides emergency destruction if the instance is compromised
- **Environmental monitoring** -- attest sensor data, satellite imagery, and field photographs. Cold archives with `ALGORITHMS.txt` ensure evidence remains verifiable decades later
--- ---
## API ## API

View File

@ -207,10 +207,12 @@ This creates the `~/.soosef/` directory structure:
channel.key AES-256-GCM channel key channel.key AES-256-GCM channel key
attestations/ Verisoo attestation log and index attestations/ Verisoo attestation log and index
chain/ Hash chain data chain/ Hash chain data
anchors/ External timestamp anchors
auth/ Web UI user database (SQLite) auth/ Web UI user database (SQLite)
certs/ Self-signed TLS certificates certs/ Self-signed TLS certificates
fieldkit/ Killswitch, deadman, tamper, USB, geofence state fieldkit/ Killswitch, deadman, tamper, USB, geofence state
temp/ Ephemeral upload/processing files temp/ Ephemeral upload/processing files
dropbox/ Source drop box submissions
instance/ Flask session data instance/ Flask session data
audit.jsonl Append-only audit trail audit.jsonl Append-only audit trail
``` ```
@ -319,7 +321,8 @@ panel. All fields have sensible defaults -- you only need to set what you want t
| `max_upload_mb` | `50` | Maximum file upload size in MB. | | `max_upload_mb` | `50` | Maximum file upload size in MB. |
| `session_timeout_minutes` | `15` | Idle session expiry. Lower is safer. | | `session_timeout_minutes` | `15` | Idle session expiry. Lower is safer. |
| `login_lockout_attempts` | `5` | Failed logins before lockout. | | `login_lockout_attempts` | `5` | Failed logins before lockout. |
| `login_lockout_minutes` | `15` | Lockout duration. | | `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. | | `killswitch_enabled` | `false` | Enable software killswitch. |
| `deadman_enabled` | `false` | Enable dead man's switch. | | `deadman_enabled` | `false` | Enable dead man's switch. |
| `deadman_interval_hours` | `24` | Hours between required check-ins. | | `deadman_interval_hours` | `24` | Hours between required check-ins. |
@ -329,7 +332,8 @@ panel. All fields have sensible defaults -- you only need to set what you want t
| `tamper_monitoring_enabled` | `false` | File integrity monitoring. | | `tamper_monitoring_enabled` | `false` | File integrity monitoring. |
| `chain_enabled` | `true` | Wrap attestations in a hash chain. | | `chain_enabled` | `true` | Wrap attestations in a hash chain. |
| `chain_auto_wrap` | `true` | Automatically chain verisoo attestations. | | `chain_auto_wrap` | `true` | Automatically chain verisoo attestations. |
| `backup_reminder_days` | `7` | Warn if no backup in this many days. | | `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_pin` | `17` | GPIO pin for hardware killswitch button. |
| `gpio_killswitch_hold_seconds` | `5.0` | Required hold time to trigger hardware killswitch. | | `gpio_killswitch_hold_seconds` | `5.0` | Required hold time to trigger hardware killswitch. |
@ -344,8 +348,10 @@ Example minimal config for a field deployment:
"deadman_enabled": true, "deadman_enabled": true,
"deadman_interval_hours": 12, "deadman_interval_hours": 12,
"deadman_grace_hours": 1, "deadman_grace_hours": 1,
"deadman_warning_webhook": "https://hooks.example.com/alert",
"killswitch_enabled": true, "killswitch_enabled": true,
"backup_reminder_days": 3 "backup_reminder_days": 3,
"cover_name": "Local Inventory Manager"
} }
``` ```
@ -382,8 +388,18 @@ soosef status
``` ```
The dead man's switch enforcement loop runs as a background thread inside `soosef serve`, 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 checking every 60 seconds. It will send a webhook warning (if configured via
period, then execute a full purge if the grace period expires. `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:
```bash
soosef fieldkit check-deadman
```
Exit codes: 0 = not armed or not overdue, 1 = unexpected error, 2 = killswitch fired.
Disarm: Disarm:
@ -407,7 +423,7 @@ Coordinates are in decimal degrees, radius in meters.
Record currently connected USB devices as the trusted baseline: Record currently connected USB devices as the trusted baseline:
```bash ```bash
soosef fieldkit usb learn soosef fieldkit usb snapshot
``` ```
When monitoring is enabled, SooSeF will alert (or trigger killswitch, depending on config) When monitoring is enabled, SooSeF will alert (or trigger killswitch, depending on config)
@ -423,6 +439,48 @@ soosef fieldkit tamper baseline
SooSeF monitors for unexpected changes to tracked files when tamper monitoring is enabled. 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:
```bash
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 ## 9. Key Management
@ -443,7 +501,7 @@ Back up keys regularly. SooSeF warns if no backup has been taken within the
`backup_reminder_days` window (default: 7 days). `backup_reminder_days` window (default: 7 days).
```bash ```bash
soosef keys backup --output /media/usb/soosef-backup.enc soosef keys export /media/usb/soosef-backup.enc
``` ```
This creates an encrypted bundle. You will be prompted for a passphrase. Store the USB This creates an encrypted bundle. You will be prompted for a passphrase. Store the USB
@ -452,25 +510,25 @@ drive physically separate from the Pi.
### 9.2 Restore ### 9.2 Restore
```bash ```bash
soosef keys restore --input /media/usb/soosef-backup.enc soosef keys import /media/usb/soosef-backup.enc
``` ```
### 9.3 Key rotation ### 9.3 Key rotation
Generate a new channel key (the old one is overwritten): Rotate the identity keypair (old key is archived, not destroyed):
```bash ```bash
soosef init --no-identity soosef keys rotate-identity
``` ```
Generate a new identity (the old one is overwritten -- all previous attestations will Rotate the channel key:
reference the old fingerprint):
```bash ```bash
soosef init --no-channel soosef keys rotate-channel
``` ```
After rotating keys, take a fresh backup immediately. 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 ### 9.4 Trusting collaborator keys
@ -484,7 +542,401 @@ Verify the fingerprint out-of-band (in person, over a secure channel) before tru
--- ---
## 10. Operational Security Notes ## 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:
```bash
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:
```bash
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:
```bash
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:
```bash
# Export public key
cp ~/.soosef/identity/public.pem /media/usb/org-a-pubkey.pem
```
On Organization B:
```bash
# 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:
```bash
soosef chain export --output /media/usb/investigation-bundle.zip
```
To export only records from a specific index range:
```bash
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.
### 12.5 Selective disclosure for legal proceedings
To produce evidence for a court order or legal discovery request without revealing the
entire chain:
```bash
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:
```bash
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
### 13.3 Legal discovery workflow
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:
```bash
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:
```ini
Environment="SOOSEF_DATA_DIR=/home/soosef/.local/share/inventory"
```
You can also pass `--data-dir` to any command:
```bash
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:
```json
{
"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. SooSeF is a tool, not a shield. Understand what it cannot do.
@ -541,7 +993,7 @@ This is a fundamental limitation of Python-based security tools.
--- ---
## 11. Troubleshooting ## 16. Troubleshooting
### Health check ### Health check
@ -561,7 +1013,7 @@ soosef status
``` ```
This checks identity key, channel key, trusted keys, dead man's switch state, geofence, This checks identity key, channel key, trusted keys, dead man's switch state, geofence,
chain status, and backup status. chain status, and backup status. Use `--json` for machine-readable output.
### Common issues ### Common issues
@ -636,3 +1088,52 @@ Always run SooSeF as the same user.
```bash ```bash
ls -la ~/.soosef/ 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:
```bash
date -u
```
On airgapped systems without NTP, set the clock manually before creating tokens:
```bash
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:
```bash
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:
```bash
python3 -m venv verify-env
source verify-env/bin/activate
pip install cryptography
python verify.py
```