Rebrand SooSeF to FieldWitness

Complete project rebrand for better positioning in the press freedom
and digital security space. FieldWitness communicates both field
deployment and evidence testimony — appropriate for the target audience
of journalists, NGOs, and human rights organizations.

Rename mapping:
- soosef → fieldwitness (package, CLI, all imports)
- soosef.stegasoo → fieldwitness.stego
- soosef.verisoo → fieldwitness.attest
- ~/.soosef/ → ~/.fwmetadata/ (innocuous data dir name)
- SOOSEF_DATA_DIR → FIELDWITNESS_DATA_DIR
- SoosefConfig → FieldWitnessConfig
- SoosefError → FieldWitnessError

Also includes:
- License switch from MIT to GPL-3.0
- C2PA bridge module (Phase 0-2 MVP): cert.py, export.py, vendor_assertions.py
- README repositioned to lead with provenance/federation, stego backgrounded
- Threat model skeleton at docs/security/threat-model.md
- Planning docs: docs/planning/c2pa-integration.md, docs/planning/gtm-feasibility.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aaron D. Lee
2026-04-02 15:05:13 -04:00
parent 6325e86873
commit 490f9d4a1d
188 changed files with 4588 additions and 2017 deletions

View File

@@ -10,7 +10,7 @@ The attestation chain is an append-only sequence of signed records stored locall
offline device. Each record includes a hash of the previous record, forming a tamper-evident
chain analogous to git commits or blockchain blocks.
The chain wraps existing Verisoo attestation records. A Verisoo record's serialized bytes
The chain wraps existing Attest attestation records. A Attest record's serialized bytes
become the input to `content_hash`, preserving the original attestation while adding
ordering, entropy witnesses, and chain integrity guarantees.
@@ -24,8 +24,8 @@ ordering, entropy witnesses, and chain integrity guarantees.
| `record_id` | 1 | byte string | 16 bytes | UUID v7 (RFC 9562). Time-ordered unique identifier. |
| `chain_index` | 2 | unsigned int | 8 bytes max | Monotonically increasing, 0-based. Genesis record is index 0. |
| `prev_hash` | 3 | byte string | 32 bytes | SHA-256 of `canonical_bytes(previous_record)`. Genesis: `0x00 * 32`. |
| `content_hash` | 4 | byte string | 32 bytes | SHA-256 of the wrapped content (e.g., Verisoo record bytes). |
| `content_type` | 5 | text string | variable | MIME-like type identifier. `"verisoo/attestation-v1"` for Verisoo records. |
| `content_hash` | 4 | byte string | 32 bytes | SHA-256 of the wrapped content (e.g., Attest record bytes). |
| `content_type` | 5 | text string | variable | MIME-like type identifier. `"attest/attestation-v1"` for Attest records. |
| `metadata` | 6 | CBOR map | variable | Extensible key-value map. See §2.1. |
| `claimed_ts` | 7 | integer | 8 bytes max | Unix timestamp in microseconds (µs). Signed integer to handle pre-epoch dates. |
| `entropy_witnesses` | 8 | CBOR map | variable | System entropy snapshot. See §3. |
@@ -41,7 +41,7 @@ The `metadata` field is an open CBOR map with text string keys. Defined keys:
| `"backfilled"` | bool | `true` if this record was created by the backfill migration |
| `"caption"` | text | Human-readable description of the attested content |
| `"location"` | text | Location name associated with the attestation |
| `"original_ts"` | integer | Original Verisoo timestamp (µs) if different from `claimed_ts` |
| `"original_ts"` | integer | Original Attest timestamp (µs) if different from `claimed_ts` |
| `"tags"` | array of text | User-defined classification tags |
Applications may add custom keys. Unknown keys must be preserved during serialization.
@@ -214,24 +214,24 @@ This file is a performance optimization — the canonical state is always deriva
### 6.3 File Locations
```
~/.soosef/chain/
~/.fwmetadata/chain/
chain.bin Append-only record log
state.cbor Chain state checkpoint
```
Paths are defined in `src/soosef/paths.py`.
Paths are defined in `src/fieldwitness/paths.py`.
## 7. Migration from Verisoo-Only Attestations
## 7. Migration from Attest-Only Attestations
Existing Verisoo attestations in `~/.soosef/attestations/` are not modified. The chain
is a parallel structure. Migration is performed by the `soosef chain backfill` command:
Existing Attest attestations in `~/.fwmetadata/attestations/` are not modified. The chain
is a parallel structure. Migration is performed by the `fieldwitness chain backfill` command:
1. Iterate all records in Verisoo's `LocalStorage` (ordered by timestamp)
1. Iterate all records in Attest's `LocalStorage` (ordered by timestamp)
2. For each record, compute `content_hash = SHA-256(record.to_bytes())`
3. Create a chain record with:
- `content_type = "verisoo/attestation-v1"`
- `claimed_ts` set to the original Verisoo timestamp
- `metadata = {"backfilled": true, "original_ts": <verisoo_timestamp>}`
- `content_type = "attest/attestation-v1"`
- `claimed_ts` set to the original Attest timestamp
- `metadata = {"backfilled": true, "original_ts": <attest_timestamp>}`
- Entropy witnesses collected at migration time (not original time)
4. Append to chain
@@ -245,8 +245,8 @@ The `content_type` field identifies what was hashed into `content_hash`. Defined
| Content Type | Description |
|---|---|
| `verisoo/attestation-v1` | Verisoo `AttestationRecord` serialized bytes |
| `soosef/raw-file-v1` | Raw file bytes (for non-image attestations, future) |
| `soosef/metadata-only-v1` | No file content; metadata-only attestation (future) |
| `attest/attestation-v1` | Attest `AttestationRecord` serialized bytes |
| `fieldwitness/raw-file-v1` | Raw file bytes (for non-image attestations, future) |
| `fieldwitness/metadata-only-v1` | No file content; metadata-only attestation (future) |
New content types may be added without changing the record format version.

View File

@@ -22,7 +22,7 @@ version, structured binary payload.
```
Offset Size Field
────── ───────── ──────────────────────────────────────
0 8 magic: b"SOOSEFX1"
0 8 magic: b"FIELDWITNESSX1"
8 1 version: uint8 (1)
9 4 summary_len: uint32 BE
13 var chain_summary: CBOR (see §3)
@@ -39,7 +39,7 @@ All multi-byte integers are big-endian. The total bundle size is:
### Parsing Without Decryption
To audit a bundle without decryption, read:
1. Magic (8 bytes) — verify `b"SOOSEFX1"`
1. Magic (8 bytes) — verify `b"FIELDWITNESSX1"`
2. Version (1 byte) — verify `1`
3. Summary length (4 bytes BE) — read the next N bytes as CBOR
4. Chain summary — verify signature, inspect metadata
@@ -132,7 +132,7 @@ For each recipient, the DEK is wrapped using X25519 ECDH + HKDF + AES-256-GCM:
2. derived_key = HKDF-SHA256(
ikm=shared_secret,
salt=bundle_id, # binds to this specific bundle
info=b"soosef-dek-wrap-v1",
info=b"fieldwitness-dek-wrap-v1",
length=32
)
3. wrapped_dek = AES-256-GCM_Encrypt(
@@ -185,7 +185,7 @@ A recipient decrypts a bundle:
2. Find own pubkey in recipients array
3. shared_secret = X25519_ECDH(recipient_x25519_private, sender_x25519_public)
(sender_x25519_public derived from summary.signer_pubkey)
4. derived_key = HKDF-SHA256(shared_secret, salt=bundle_id, info=b"soosef-dek-wrap-v1")
4. derived_key = HKDF-SHA256(shared_secret, salt=bundle_id, info=b"fieldwitness-dek-wrap-v1")
5. dek = AES-256-GCM_Decrypt(derived_key, wrap_nonce, wrapped_dek, aad=bundle_id)
6. compressed = AES-256-GCM_Decrypt(dek, nonce, ciphertext, aad=summary_bytes)
7. records_cbor = zstd.decompress(compressed)
@@ -240,11 +240,11 @@ These are two different trees:
## 6. Steganographic Embedding
Bundles can optionally be embedded in JPEG images using stegasoo's DCT steganography:
Bundles can optionally be embedded in JPEG images using stego's DCT steganography:
```
1. bundle_bytes = create_export_bundle(chain, start, end, private_key, recipients)
2. stego_image = stegasoo.encode(
2. stego_image = stego.encode(
carrier=carrier_image,
reference=reference_image,
file_data=bundle_bytes,
@@ -256,14 +256,14 @@ Bundles can optionally be embedded in JPEG images using stegasoo's DCT steganogr
Extraction:
```
1. result = stegasoo.decode(
1. result = stego.decode(
carrier=stego_image,
reference=reference_image,
passphrase=passphrase,
channel_key=channel_key
)
2. bundle_bytes = result.file_data
3. assert bundle_bytes[:8] == b"SOOSEFX1"
3. assert bundle_bytes[:8] == b"FIELDWITNESSX1"
```
### 6.1 Capacity Considerations
@@ -299,7 +299,7 @@ recipient, the creator needs only their public key (no shared secret setup requi
Recipients' Ed25519 public keys can be obtained via:
- Direct exchange (QR code, USB transfer, verbal fingerprint verification)
- Federation server identity registry (when available)
- Verisoo's existing `peers.json` file
- Attest's existing `peers.json` file
### 7.3 Self-Encryption
@@ -310,7 +310,7 @@ This allows them to decrypt their own exports (e.g., when restoring from backup)
| Error | Cause | Response |
|---|---|---|
| Bad magic | Not a SOOSEFX1 bundle | Reject with `ExportError("not a SooSeF export bundle")` |
| Bad magic | Not a FIELDWITNESSX1 bundle | Reject with `ExportError("not a FieldWitness export bundle")` |
| Bad version | Unsupported format version | Reject with `ExportError("unsupported bundle version")` |
| Signature invalid | Tampered summary or wrong signer | Reject with `ExportError("bundle signature verification failed")` |
| No matching recipient | Decryptor's key not in recipients list | Reject with `ExportError("not an authorized recipient")` |

View File

@@ -19,7 +19,7 @@ the underlying attestation data.
| Term | Definition |
|---|---|
| **Bundle** | An encrypted export bundle (SOOSEFX1 format) containing chain records |
| **Bundle** | An encrypted export bundle (FIELDWITNESSX1 format) containing chain records |
| **STH** | Signed Tree Head — a server's signed commitment to its current Merkle tree state |
| **Receipt** | A server-signed proof that a bundle was included in its log at a specific time |
| **Inclusion proof** | Merkle path from a leaf (bundle hash) to the tree root |
@@ -106,7 +106,7 @@ POST /v1/submit
**Request body**: Raw bundle bytes (application/octet-stream)
**Processing**:
1. Verify magic bytes `b"SOOSEFX1"` and version
1. Verify magic bytes `b"FIELDWITNESSX1"` and version
2. Parse chain summary
3. Verify `bundle_sig` against `signer_pubkey`
4. Compute `bundle_hash = SHA-256(0x00 || bundle_bytes)`
@@ -215,7 +215,7 @@ GET /v1/entries?start={s}&end={e}
0: tree_index, # uint
1: bundle_hash, # bytes[32]
2: chain_summary, # CBOR map (from bundle, unencrypted)
3: encrypted_blob, # bytes — full SOOSEFX1 bundle
3: encrypted_blob, # bytes — full FIELDWITNESSX1 bundle
4: receipt_ts, # int — Unix µs when received
}
```
@@ -488,8 +488,8 @@ CREATE INDEX idx_bundles_receipt_ts ON bundles(receipt_ts);
"server_id": "my-server.example.org",
"host": "0.0.0.0",
"port": 8443,
"data_dir": "/var/lib/soosef-federation",
"identity_key_path": "/etc/soosef-federation/identity/private.pem",
"data_dir": "/var/lib/fieldwitness-federation",
"identity_key_path": "/etc/fieldwitness-federation/identity/private.pem",
"peers": [
{
"url": "https://peer1.example.org:8443",

View File

@@ -6,7 +6,7 @@
## 1. Problem Statement
SooSeF operates offline-first: devices create Ed25519-signed attestations without network
FieldWitness operates offline-first: devices create Ed25519-signed attestations without network
access. This creates two fundamental challenges:
1. **Timestamp credibility** — An offline device's clock is untrusted. An adversary with
@@ -55,7 +55,7 @@ protecting content confidentiality even from the distribution infrastructure.
────────────── ─────── ────────
┌──────────┐ ┌──────────────┐ ┌──────────┐ USB/SD ┌──────────┐ ┌────────────┐
Verisoo │────>│ Hash Chain │────>│ Export │───────────────>│ Loader │────>│ Federation │
Attest │────>│ Hash Chain │────>│ Export │───────────────>│ Loader │────>│ Federation │
│ Attest │ │ (Layer 1) │ │ Bundle │ │ (App) │ │ Server │
└──────────┘ └──────────────┘ │ (Layer 2)│ └────┬─────┘ └─────┬──────┘
└──────────┘ │ │
@@ -80,8 +80,8 @@ Each attestation is wrapped in a chain record that includes:
- Entropy witnesses (system uptime, kernel state) that make timestamp fabrication expensive
- An Ed25519 signature over the entire record
The chain lives on the offline device at `~/.soosef/chain/`. It wraps existing Verisoo
attestation records — the Verisoo record's bytes become the `content_hash` input.
The chain lives on the offline device at `~/.fwmetadata/chain/`. It wraps existing Attest
attestation records — the Attest record's bytes become the `content_hash` input.
**See**: [chain-format.md](chain-format.md)
@@ -94,7 +94,7 @@ A range of chain records is packaged into a portable bundle:
4. An unencrypted `chain_summary` (record count, hash range, Merkle root, signature) allows
auditing without decryption
Bundles can optionally be embedded in JPEG images via stegasoo's DCT steganography,
Bundles can optionally be embedded in JPEG images via stego's DCT steganography,
making them indistinguishable from normal photos on a USB stick.
**See**: [export-bundle.md](export-bundle.md)
@@ -123,13 +123,13 @@ The loader never needs signing keys — bundles are already signed. It is a tran
## 4. Key Domains
SooSeF maintains strict separation between two cryptographic domains:
FieldWitness maintains strict separation between two cryptographic domains:
| Domain | Algorithm | Purpose | Key Location |
|---|---|---|---|
| **Signing** | Ed25519 | Attestation signatures, chain records, bundle summaries | `~/.soosef/identity/` |
| **Signing** | Ed25519 | Attestation signatures, chain records, bundle summaries | `~/.fwmetadata/identity/` |
| **Encryption** | X25519 + AES-256-GCM | Bundle payload encryption (envelope) | Derived from Ed25519 via birational map |
| **Steganography** | AES-256-GCM (from factors) | Stegasoo channel encryption | `~/.soosef/stegasoo/channel.key` |
| **Steganography** | AES-256-GCM (from factors) | Stego channel encryption | `~/.fwmetadata/stego/channel.key` |
The signing and encryption domains share a key lineage (Ed25519 → X25519 derivation) but
serve different purposes. The steganography domain remains fully independent — it protects
@@ -215,7 +215,7 @@ more credible timestamps. Frequent USB sync trips shrink the window.
## 10. File Layout
```
src/soosef/federation/
src/fieldwitness/federation/
__init__.py
models.py Chain record and state dataclasses
serialization.py CBOR canonical encoding
@@ -241,7 +241,7 @@ src/soosef/federation/
permissions.py Access control
config.py Server configuration
~/.soosef/
~/.fwmetadata/
chain/ Local hash chain
chain.bin Append-only record log
state.cbor Chain state checkpoint

View File

@@ -1,10 +1,10 @@
# SooSeF Deployment Guide
# FieldWitness Deployment Guide
Three-tier deployment model for field journalism, organizational evidence management,
and cross-organization federation.
This guide is for field deployers: IT staff at NGOs, technically competent journalists,
and anyone setting up SooSeF for operational use. Read the tier descriptions first, then
and anyone setting up FieldWitness for operational use. Read the tier descriptions first, then
jump to the section that matches your deployment.
---
@@ -28,7 +28,7 @@ LUKS-encrypted persistent partition for keys, config, and attestations. Amnesic
pull the USB and the host machine retains nothing.
**Tier 2 -- Org Server.** A persistent Docker deployment on a mini PC in the newsroom or a
trusted VPS. Runs the full SooSeF web UI (port 5000) and the federation API (port 8000).
trusted VPS. Runs the full FieldWitness web UI (port 5000) and the federation API (port 8000).
Stores attestations, manages keys, and synchronizes with federation relays.
**Tier 3 -- Federation Relay.** A lightweight Docker container on a VPS in a jurisdiction
@@ -48,8 +48,8 @@ actionable.
common, replaceable, good Linux support -- but any laptop that boots from USB works)
- A build machine with `live-build` installed (any Debian/Ubuntu system)
The resulting USB image is a Debian Live system with SooSeF pre-installed. No pip, no
terminal, no manual setup. The reporter boots it and gets a working SooSeF instance.
The resulting USB image is a Debian Live system with FieldWitness pre-installed. No pip, no
terminal, no manual setup. The reporter boots it and gets a working FieldWitness instance.
### 1.2 Building the USB image
@@ -85,29 +85,29 @@ When a reporter boots from the USB:
1. GRUB loads the Debian Live system
2. A minimal desktop environment starts (no login prompt)
3. Firefox opens automatically, pointed at `https://127.0.0.1:5000`
4. The SooSeF web UI prompts for first-user setup (on first boot) or login
4. The FieldWitness web UI prompts for first-user setup (on first boot) or login
No terminal interaction required for normal operation.
### 1.4 Persistent encrypted storage
The USB image includes a LUKS-encrypted persistent partition. On first boot, the reporter
sets a passphrase. All SooSeF state lives on this partition:
sets a passphrase. All FieldWitness state lives on this partition:
```
/persistent/
.soosef/ Keys, config, attestations, chain data, auth
.fwmetadata/ Keys, config, attestations, chain data, auth
```
On subsequent boots, the system prompts for the LUKS passphrase to unlock the persistent
partition. If the passphrase is not entered (or wrong), SooSeF starts in a fresh,
partition. If the passphrase is not entered (or wrong), FieldWitness starts in a fresh,
ephemeral state -- useful for crossing borders with a "clean" appearance.
### 1.5 Amnesic operation
The live system runs from RAM. When the USB is removed:
- The host laptop retains zero SooSeF data (no files, no swap traces, no browser cache)
- The host laptop retains zero FieldWitness data (no files, no swap traces, no browser cache)
- The host's own storage is never written to
- RAM contents are gone on power-off
@@ -162,17 +162,17 @@ cd deploy/docker
docker compose up server -d
```
This starts the full SooSeF server with:
This starts the full FieldWitness server with:
- **Port 5000**: Web UI (Flask/Waitress) -- stego, attestation, key management, admin
- **Port 8000**: Federation API (FastAPI/uvicorn) -- cross-org attestation sync
The Docker image is a multi-stage build (`deploy/docker/Dockerfile`, target: `server`).
It installs SooSeF into a Python 3.12 virtualenv, copies frontend assets, and runs
`soosef init` on first start followed by `soosef serve`.
It installs FieldWitness into a Python 3.12 virtualenv, copies frontend assets, and runs
`fieldwitness init` on first start followed by `fieldwitness serve`.
Data is persisted in a Docker volume (`server-data`) mounted at `/data` inside the
container. The `SOOSEF_DATA_DIR` environment variable points SooSeF at this volume.
container. The `FIELDWITNESS_DATA_DIR` environment variable points FieldWitness at this volume.
### 2.3 Docker Compose reference
@@ -187,8 +187,8 @@ services:
volumes:
- server-data:/data
environment:
- SOOSEF_DATA_DIR=/data
- VERISOO_GOSSIP_INTERVAL=60
- FIELDWITNESS_DATA_DIR=/data
- FIELDWITNESS_GOSSIP_INTERVAL=60
relay: # Tier 3: Federation relay
ports:
@@ -196,7 +196,7 @@ services:
volumes:
- relay-data:/data
environment:
- SOOSEF_DATA_DIR=/data
- FIELDWITNESS_DATA_DIR=/data
```
Adjust port mappings and volume drivers as needed for your environment.
@@ -207,8 +207,8 @@ For organizations already running Kubernetes:
```bash
# Build images
docker build -t soosef-server --target server -f deploy/docker/Dockerfile .
docker build -t soosef-relay --target relay -f deploy/docker/Dockerfile .
docker build -t fieldwitness-server --target server -f deploy/docker/Dockerfile .
docker build -t fieldwitness-relay --target relay -f deploy/docker/Dockerfile .
# Deploy
kubectl apply -f deploy/kubernetes/namespace.yaml
@@ -218,7 +218,7 @@ kubectl apply -f deploy/kubernetes/server-deployment.yaml
Important constraints from `deploy/kubernetes/README.md`:
- **Single writer only.** Both deployments use `replicas: 1` with `Recreate` strategy.
SooSeF uses SQLite and append-only binary logs that require single-writer access. Do not
FieldWitness uses SQLite and append-only binary logs that require single-writer access. Do not
scale horizontally.
- **Persistent volumes required.** The server needs 10Gi, the relay needs 5Gi. Adjust
based on expected attestation volume.
@@ -234,21 +234,21 @@ be handled by:
- A cloud load balancer (if on a VPS)
For a simple mini-PC newsroom setup without a reverse proxy, override the CMD to remove
`--no-https` and let SooSeF generate a self-signed certificate.
`--no-https` and let FieldWitness generate a self-signed certificate.
### 2.6 Backups
The Docker volume contains all SooSeF state. Back it up:
The Docker volume contains all FieldWitness state. Back it up:
```bash
# Stop the container, snapshot the volume, restart
docker compose stop server
docker run --rm -v server-data:/data -v /backup:/backup \
busybox tar czf /backup/soosef-$(date +%Y%m%d).tar.gz -C /data .
busybox tar czf /backup/fieldwitness-$(date +%Y%m%d).tar.gz -C /data .
docker compose start server
```
Or use `soosef keys export` from inside the container for key-only backups.
Or use `fieldwitness keys export` from inside the container for key-only backups.
---
@@ -279,7 +279,7 @@ This starts the relay with:
- Data volume: `relay-data` at `/data`
The Docker image uses the `relay` target from the same multi-stage Dockerfile. The relay
runs only `uvicorn soosef.verisoo.api:app` -- the minimal federation endpoint.
runs only `uvicorn fieldwitness.attest.api:app` -- the minimal federation endpoint.
### 3.3 Kubernetes deployment
@@ -320,11 +320,11 @@ attestations.
## 4. Threat Level Configuration Presets
SooSeF ships four configuration presets at `deploy/config-presets/`. Choose one based on
FieldWitness ships four configuration presets at `deploy/config-presets/`. Choose one based on
your operational threat environment and copy it to your config location.
```bash
cp deploy/config-presets/high-threat.json ~/.soosef/config.json
cp deploy/config-presets/high-threat.json ~/.fwmetadata/config.json
```
### 4.1 Low Threat -- Press Freedom Country
@@ -407,34 +407,34 @@ measures that are outside the scope of this software.
### 4.5 Customizing presets
The presets are starting points. Override individual settings in `~/.soosef/config.json`
The presets are starting points. Override individual settings in `~/.fwmetadata/config.json`
after copying. The full configuration reference is in Section 8.
---
## 5. Initial Setup (All Tiers)
### 5.1 Initialize SooSeF
### 5.1 Initialize FieldWitness
On Tier 1 (USB), initialization happens automatically on first boot. On Tier 2/3 (Docker),
the container runs `soosef init` on first start. For manual installs:
the container runs `fieldwitness init` on first start. For manual installs:
```bash
soosef init
fieldwitness init
```
This creates the `~/.soosef/` directory structure:
This creates the `~/.fwmetadata/` directory structure:
```
~/.soosef/
~/.fwmetadata/
config.json Unified configuration
identity/ Ed25519 signing keypair (verisoo)
identity/ Ed25519 signing keypair (attest)
private.pem
public.pem
identity.meta.json
stegasoo/ Stegasoo state
stego/ Stego state
channel.key AES-256-GCM channel key
attestations/ Verisoo attestation log and index
attestations/ Attest attestation log and index
chain/ Hash chain data
anchors/ External timestamp anchors
auth/ Web UI user database (SQLite)
@@ -448,7 +448,7 @@ This creates the `~/.soosef/` directory structure:
The `identity/` and `auth/` directories are created with mode 0700.
`soosef init` generates:
`fieldwitness init` generates:
- An Ed25519 identity keypair (for signing attestations)
- A channel key (for steganographic encoding)
@@ -457,7 +457,7 @@ The `identity/` and `auth/` directories are created with mode 0700.
### 5.2 Apply a threat level preset
```bash
cp deploy/config-presets/<level>-threat.json ~/.soosef/config.json
cp deploy/config-presets/<level>-threat.json ~/.fwmetadata/config.json
```
See Section 4 for preset descriptions.
@@ -467,7 +467,7 @@ See Section 4 for preset descriptions.
Start the server and create the first admin user through the web UI:
```bash
soosef serve --host 0.0.0.0 --no-https
fieldwitness serve --host 0.0.0.0 --no-https
```
Navigate to `http://<host-ip>:5000` from a device on the same network. The web UI will
@@ -510,12 +510,12 @@ Adjust the device path to match your partition layout.
### 6.2 Disable core dumps
A core dump from the SooSeF process would contain key material in plaintext.
A core dump from the FieldWitness process would contain key material in plaintext.
```bash
echo "* hard core 0" | sudo tee -a /etc/security/limits.conf
echo "kernel.core_pattern=/dev/null" | sudo tee -a /etc/sysctl.d/99-soosef.conf
sudo sysctl -p /etc/sysctl.d/99-soosef.conf
echo "kernel.core_pattern=/dev/null" | sudo tee -a /etc/sysctl.d/99-fieldwitness.conf
sudo sysctl -p /etc/sysctl.d/99-fieldwitness.conf
```
### 6.3 Firewall
@@ -525,7 +525,7 @@ 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 allow 5000/tcp # FieldWitness web UI
sudo ufw allow 8000/tcp # Federation API (Tier 2 only)
sudo ufw enable
```
@@ -553,39 +553,39 @@ Adjust based on what your system has running. The goal is to minimize attack sur
```bash
# LAN-only, no HTTPS (acceptable if the network is physically isolated)
soosef serve --host 0.0.0.0 --no-https
fieldwitness serve --host 0.0.0.0 --no-https
# With self-signed HTTPS (recommended)
soosef serve --host 0.0.0.0
fieldwitness serve --host 0.0.0.0
# Custom port
soosef serve --host 0.0.0.0 --port 8443
fieldwitness 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
On first HTTPS start, FieldWitness auto-generates a self-signed certificate at
`~/.fwmetadata/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
FieldWitness uses Waitress (pure Python, no C dependencies) as its production server with 4
worker threads by default. Adjust with `--workers`.
### 7.2 systemd service (bare metal Tier 2)
Create `/etc/systemd/system/soosef.service`:
Create `/etc/systemd/system/fieldwitness.service`:
```ini
[Unit]
Description=SooSeF Security Fieldkit
Description=FieldWitness 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
User=fieldwitness
Group=fieldwitness
WorkingDirectory=/home/fieldwitness
Environment="PATH=/home/fieldwitness/fieldwitness-env/bin:/usr/bin"
ExecStart=/home/fieldwitness/fieldwitness-env/bin/fieldwitness serve --host 0.0.0.0 --workers 4
Restart=on-failure
RestartSec=5
@@ -593,7 +593,7 @@ RestartSec=5
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/soosef/.soosef
ReadWritePaths=/home/fieldwitness/.fieldwitness
PrivateTmp=yes
[Install]
@@ -604,8 +604,8 @@ Enable and start:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now soosef
sudo journalctl -u soosef -f # Watch logs
sudo systemctl enable --now fieldwitness
sudo journalctl -u fieldwitness -f # Watch logs
```
Add `--no-https` to `ExecStart` if running on a physically isolated LAN where TLS is
@@ -615,7 +615,7 @@ unnecessary.
## 8. Configuration Reference
Configuration lives at `~/.soosef/config.json`. Edit it directly or use the web admin
Configuration lives at `~/.fwmetadata/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 |
@@ -628,7 +628,7 @@ panel. All fields have sensible defaults -- you only need to set what you want t
| `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. |
| `default_embed_mode` | `auto` | Default steganographic embedding mode for Stego. |
| `killswitch_enabled` | `false` | Enable software killswitch. |
| `deadman_enabled` | `false` | Enable dead man's switch. |
| `deadman_interval_hours` | `24` | Hours between required check-ins. |
@@ -637,8 +637,8 @@ panel. All fields have sensible defaults -- you only need to set what you want t
| `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. |
| `chain_auto_wrap` | `true` | Automatically chain attest attestations. |
| `backup_reminder_days` | `7` | Warn if no backup in this many days. `fieldwitness status` reports overdue backups. |
| `cover_name` | `""` | If set, used as the CN in the self-signed SSL certificate instead of "localhost". See Section 15 (Cover/Duress Mode). |
Example minimal config for a high-threat field deployment:
@@ -669,14 +669,14 @@ deployment type. On Tier 1 (USB), the killswitch destroys the LUKS key. On Tier
### 9.1 Dead man's switch
The dead man's switch requires periodic check-ins. If you miss a check-in, SooSeF sends
The dead man's switch requires periodic check-ins. If you miss a check-in, FieldWitness sends
a warning during the grace period. If the grace period expires without a check-in, the
killswitch fires automatically and destroys all key material and data.
Arm it:
```bash
soosef fieldkit deadman arm --interval 12 --grace 1
fieldwitness fieldkit deadman arm --interval 12 --grace 1
```
This requires a check-in every 12 hours, with a 1-hour grace period.
@@ -684,7 +684,7 @@ This requires a check-in every 12 hours, with a 1-hour grace period.
Check in:
```bash
soosef fieldkit checkin
fieldwitness fieldkit checkin
```
You can also check in through the web UI at `/fieldkit`.
@@ -692,10 +692,10 @@ You can also check in through the web UI at `/fieldkit`.
Check status:
```bash
soosef status
fieldwitness 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 `fieldwitness 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
@@ -704,7 +704,7 @@ IP ranges.
For cron-based enforcement outside the web server (e.g., on a headless node), use:
```bash
soosef fieldkit check-deadman
fieldwitness fieldkit check-deadman
```
Exit codes: 0 = not armed or not overdue, 1 = unexpected error, 2 = killswitch fired.
@@ -712,16 +712,16 @@ Exit codes: 0 = not armed or not overdue, 1 = unexpected error, 2 = killswitch f
Disarm:
```bash
soosef fieldkit deadman disarm
fieldwitness fieldkit deadman disarm
```
### 9.2 Geofence
If you have a USB GPS module, you can set a geographic boundary. SooSeF will trigger the
If you have a USB GPS module, you can set a geographic boundary. FieldWitness will trigger the
killswitch if the device moves outside the fence.
```bash
soosef fieldkit geofence set --lat 50.4501 --lon 30.5234 --radius 5000
fieldwitness fieldkit geofence set --lat 50.4501 --lon 30.5234 --radius 5000
```
Coordinates are in decimal degrees, radius in meters. Most useful on Tier 1 field devices.
@@ -731,10 +731,10 @@ Coordinates are in decimal degrees, radius in meters. Most useful on Tier 1 fiel
Record currently connected USB devices as the trusted baseline:
```bash
soosef fieldkit usb snapshot
fieldwitness fieldkit usb snapshot
```
When monitoring is enabled, SooSeF will alert (or trigger killswitch, depending on config)
When monitoring is enabled, FieldWitness will alert (or trigger killswitch, depending on config)
if an unknown USB device is connected.
### 9.4 Tamper baseline
@@ -742,10 +742,10 @@ if an unknown USB device is connected.
Record file integrity baselines for critical files:
```bash
soosef fieldkit tamper baseline
fieldwitness fieldkit tamper baseline
```
SooSeF monitors for unexpected changes to tracked files when tamper monitoring is enabled.
FieldWitness monitors for unexpected changes to tracked files when tamper monitoring is enabled.
### 9.5 Killswitch
@@ -760,9 +760,9 @@ maximize what is gone before any interruption:
5. **Attestation log and chain data**
6. **Temp files and audit log**
7. **Configuration**
8. **System journal entries** for the soosef unit
8. **System journal entries** for the fieldwitness unit
9. **Deep forensic scrub** (see below)
10. **Self-uninstall** of the soosef pip package
10. **Self-uninstall** of the fieldwitness pip package
On Tier 1 (USB), steps 1-10 are replaced by LUKS header destruction, which is faster and
more reliable on flash media (see Section 1.6).
@@ -770,25 +770,25 @@ more reliable on flash media (see Section 1.6).
Trigger manually:
```bash
soosef fieldkit purge --confirm CONFIRM-PURGE
fieldwitness fieldkit purge --confirm CONFIRM-PURGE
```
**Deep forensic scrub (Tier 2 bare metal only).** When the killswitch fires with `ALL`
scope on a non-USB deployment, it performs a deep forensic scrub that removes traces of
SooSeF beyond the `~/.soosef/` directory:
FieldWitness beyond the `~/.fwmetadata/` directory:
- **Python bytecache**: removes all `__pycache__` directories and `.pyc` files for
soosef, stegasoo, and verisoo from site-packages
fieldwitness, stego, and attest 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
`~/.cache/pip/` matching fieldwitness/stego/attest
- **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
remove all lines containing "fieldwitness"
- **Self-uninstall**: runs `pip uninstall -y fieldwitness` to remove the package from the
virtual environment
After a full purge, the system will show minimal evidence that SooSeF was ever installed.
After a full purge, the system will show minimal evidence that FieldWitness 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 on Tier 2, use full-disk
encryption (LUKS) and physically destroy the storage media. On Tier 1, LUKS header
@@ -798,11 +798,11 @@ destruction handles this.
## 10. Key Management
SooSeF manages two separate key domains:
FieldWitness manages two separate key domains:
- **Ed25519 identity key** (`~/.soosef/identity/`) -- used for signing attestations.
- **Ed25519 identity key** (`~/.fwmetadata/identity/`) -- used for signing attestations.
This is your provenance identity.
- **AES-256-GCM channel key** (`~/.soosef/stegasoo/channel.key`) -- used for
- **AES-256-GCM channel key** (`~/.fwmetadata/stego/channel.key`) -- used for
steganographic encoding/decoding. Shared with anyone who needs to read your
stego messages.
@@ -810,11 +810,11 @@ These are separate security concerns and are never merged.
### 10.1 Backup
Back up keys regularly. SooSeF warns if no backup has been taken within the
Back up keys regularly. FieldWitness warns if no backup has been taken within the
`backup_reminder_days` window (default: 7 days).
```bash
soosef keys export /media/usb/soosef-backup.enc
fieldwitness keys export /media/usb/fieldwitness-backup.enc
```
This creates an encrypted bundle. You will be prompted for a passphrase. Store the USB
@@ -826,7 +826,7 @@ keys are gone. Back up to a second USB drive and store it in a separate location
### 10.2 Restore
```bash
soosef keys import /media/usb/soosef-backup.enc
fieldwitness keys import /media/usb/fieldwitness-backup.enc
```
### 10.3 Key rotation
@@ -834,13 +834,13 @@ soosef keys import /media/usb/soosef-backup.enc
Rotate the identity keypair (old key is archived, not destroyed):
```bash
soosef keys rotate-identity
fieldwitness keys rotate-identity
```
Rotate the channel key:
```bash
soosef keys rotate-channel
fieldwitness keys rotate-channel
```
After rotating keys, take a fresh backup immediately. Notify all collaborators of the
@@ -851,7 +851,7 @@ new identity fingerprint so they can update their trusted-key lists.
Import a collaborator's public key so you can verify their attestations:
```bash
soosef keys trust --import /media/usb/collaborator-pubkey.pem
fieldwitness keys trust --import /media/usb/collaborator-pubkey.pem
```
Verify the fingerprint out-of-band (in person, over a secure channel) before trusting.
@@ -861,7 +861,7 @@ Verify the fingerprint out-of-band (in person, over a secure channel) before tru
## 11. 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
FieldWitness. Sources do not need a FieldWitness account -- they receive a one-time upload URL and
submit files through their browser.
### 11.1 Creating tokens
@@ -908,7 +908,7 @@ the receipt is valid.
### 11.5 Operational security for the drop box
- **No SooSeF branding**: the upload page is a minimal HTML form with no identifying
- **No FieldWitness 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
@@ -918,21 +918,21 @@ the receipt is valid.
- **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
- **No IP logging**: FieldWitness 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
If operating in a high-risk environment, consider running FieldWitness 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.
### 11.6 Drop box file storage
Uploaded files are stored in `~/.soosef/temp/dropbox/` with filenames derived from the
Uploaded files are stored in `~/.fwmetadata/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`.
SQLite database at `~/.fwmetadata/auth/dropbox.db`.
---
@@ -957,11 +957,11 @@ 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
fieldwitness 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/`.
both the anchor and the TSA response as a JSON file under `~/.fwmetadata/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.
@@ -971,7 +971,7 @@ at the timestamp. This is legally stronger than a self-asserted timestamp.
Without `--tsa`, the command exports the anchor hash for manual external submission:
```bash
soosef chain anchor
fieldwitness chain anchor
```
This prints a compact text block containing the chain ID, head index, record count, and
@@ -989,7 +989,7 @@ The anchor file is saved locally regardless of whether a TSA was used.
For Tier 1 (USB) and other airgapped deployments:
1. Run `soosef chain anchor` on the airgapped device
1. Run `fieldwitness 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
@@ -1002,7 +1002,7 @@ For Tier 1 (USB) and other airgapped deployments:
To verify that the current chain state matches a previously created anchor:
```bash
soosef chain verify
fieldwitness chain verify
```
This checks all hash linkage and signatures in the chain. If the chain has been tampered
@@ -1012,7 +1012,7 @@ with since the anchor was created, verification will fail.
## 13. Cross-Organization Federation
Federation allows multiple SooSeF instances to exchange attestation records for
Federation allows multiple FieldWitness 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.
@@ -1031,14 +1031,14 @@ On Organization A:
```bash
# Export public key
cp ~/.soosef/identity/public.pem /media/usb/org-a-pubkey.pem
cp ~/.fwmetadata/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
fieldwitness keys trust --import /media/usb/org-a-pubkey.pem
```
Always verify fingerprints out-of-band (in person, over a known-secure voice channel).
@@ -1049,13 +1049,13 @@ Repeat in both directions so each organization trusts the other.
Export a JSON bundle containing attestation records and chain data:
```bash
soosef chain export --output /media/usb/investigation-bundle.zip
fieldwitness 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
fieldwitness chain export --start 100 --end 200 --output /media/usb/partial-bundle.zip
```
The export includes:
@@ -1068,7 +1068,7 @@ The export includes:
### 13.3 Importing attestation bundles
On the receiving organization's SooSeF instance:
On the receiving organization's FieldWitness 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)
@@ -1077,7 +1077,7 @@ On the receiving organization's SooSeF instance:
### 13.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
FieldWitness 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.
@@ -1087,7 +1087,7 @@ To produce evidence for a court order or legal discovery request without reveali
entire chain:
```bash
soosef chain disclose --indices 42,43,44 --output disclosure.json
fieldwitness 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
@@ -1110,7 +1110,7 @@ Tier 1 field devices.
## 14. Evidence Packages and Cold Archives
SooSeF provides two export formats for preserving evidence outside the running instance.
FieldWitness provides two export formats for preserving evidence outside the running instance.
### 14.1 Evidence packages
@@ -1127,21 +1127,21 @@ Contents of an evidence package:
`cryptography`)
- `README.txt` -- human-readable instructions
The package is self-contained. No SooSeF installation is required to verify the evidence.
The package is self-contained. No FieldWitness 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
- When sharing with a partner organization that does not run FieldWitness
- 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)
### 14.2 Cold archives
A cold archive is a full snapshot of the entire SooSeF evidence store, designed for
A cold archive is a full snapshot of the entire FieldWitness 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.
@@ -1149,7 +1149,7 @@ 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
- `attestations/` -- full attest 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
@@ -1157,13 +1157,13 @@ Contents of a cold archive:
- `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
FieldWitness no longer exists
- `README.txt` -- human-readable description
To restore a cold archive on a fresh SooSeF instance:
To restore a cold archive on a fresh FieldWitness instance:
```bash
soosef archive import <archive.zip>
fieldwitness archive import <archive.zip>
```
**When to create cold archives:**
@@ -1178,13 +1178,13 @@ soosef archive import <archive.zip>
For legal discovery and court proceedings:
1. Use `soosef chain disclose` for selective disclosure (Section 13.5) when you must
1. Use `fieldwitness chain disclose` for selective disclosure (Section 13.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.
not need to install FieldWitness.
### 14.4 Long-term archival best practices
@@ -1202,24 +1202,24 @@ not need to install SooSeF.
## 15. Cover/Duress Mode
Cover mode disguises a SooSeF installation so that casual inspection of the device does
Cover mode disguises a FieldWitness installation so that casual inspection of the device does
not immediately reveal it as a security toolkit.
### 15.1 Renaming the data directory
By default, SooSeF stores everything under `~/.soosef/`. To use an inconspicuous name,
set the `SOOSEF_DATA_DIR` environment variable:
By default, FieldWitness stores everything under `~/.fwmetadata/`. To use an inconspicuous name,
set the `FIELDWITNESS_DATA_DIR` environment variable:
```bash
export SOOSEF_DATA_DIR=~/.local/share/inventory
soosef init
export FIELDWITNESS_DATA_DIR=~/.local/share/inventory
fieldwitness init
```
All SooSeF commands respect this variable. Add it to the soosef user's shell profile or
All FieldWitness commands respect this variable. Add it to the fieldwitness user's shell profile or
the systemd service file:
```ini
Environment="SOOSEF_DATA_DIR=/home/soosef/.local/share/inventory"
Environment="FIELDWITNESS_DATA_DIR=/home/fieldwitness/.local/share/inventory"
```
In Docker deployments, set this in the environment section of the compose file. The
@@ -1237,7 +1237,7 @@ a browser inspector sees a plausible-looking certificate:
}
```
Delete `~/.soosef/certs/cert.pem` and restart the server to regenerate the certificate
Delete `~/.fwmetadata/certs/cert.pem` and restart the server to regenerate the certificate
with the new CN.
The threat level presets (Section 4) include appropriate cover names for each level.
@@ -1245,7 +1245,7 @@ The threat level presets (Section 4) include appropriate cover names for each le
### 15.3 Portable USB operation (Tier 1)
The Tier 1 bootable USB is the primary cover mechanism. When the USB is not inserted, the
host laptop shows no trace of SooSeF. The USB itself is a LUKS-encrypted partition that
host laptop shows no trace of FieldWitness. The USB itself is a LUKS-encrypted partition that
reveals nothing without the passphrase.
For additional cover, the USB can be labeled generically (e.g., "DEBIAN LIVE") and the
@@ -1255,19 +1255,19 @@ LUKS partition does not advertise its contents.
## 16. Operational Security Notes
SooSeF is a tool, not a shield. Understand what it cannot do.
FieldWitness is a tool, not a shield. Understand what it cannot do.
### What SooSeF does not protect against
### What FieldWitness 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
- **Social engineering.** FieldWitness 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
- **Compromised client devices.** FieldWitness secures the server. If a user's laptop has
malware, their browser session is compromised regardless of what the server does.
- **Tier 3 relay compromise.** If the relay is seized, attestation metadata (hashes,
signatures, timestamps) is exposed. This reveals that certain public keys attested
@@ -1285,7 +1285,7 @@ reliable** because:
- 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
FieldWitness'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.
@@ -1318,7 +1318,7 @@ This is a fundamental limitation of Python-based security tools.
### Health check
SooSeF exposes a `/health` endpoint on the web UI. Hit it to verify the server is running:
FieldWitness exposes a `/health` endpoint on the web UI. Hit it to verify the server is running:
```bash
# Tier 1 or bare metal
@@ -1336,7 +1336,7 @@ The `-k` flag skips certificate verification for self-signed certs.
### System status
```bash
soosef status
fieldwitness status
```
This checks identity key, channel key, trusted keys, dead man's switch state, geofence,
@@ -1348,7 +1348,7 @@ chain status, and backup status. Use `--json` for machine-readable output.
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` (bare metal) or
3. Check the service is running: `sudo systemctl status fieldwitness` (bare metal) or
`docker compose ps` (Docker)
4. Check the machine's IP: `ip addr show`
@@ -1361,13 +1361,13 @@ docker compose logs server
```
Common causes: port conflict (5000 or 8000 already in use), volume permission issues,
or missing initialization. The container runs `soosef init` on first start, which requires
or missing initialization. The container runs `fieldwitness init` on first start, which requires
write access to the `/data` volume.
**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
this, distribute `~/.fwmetadata/certs/cert.pem` to client devices and install it as a
trusted certificate.
**Dead man's switch fires unexpectedly**
@@ -1380,16 +1380,16 @@ start. Make sure the systemd service is set to `Restart=on-failure` (bare metal)
If you need to perform maintenance, disarm the switch first:
```bash
soosef fieldkit deadman disarm
fieldwitness fieldkit deadman disarm
```
Re-arm when maintenance is complete.
**Permission errors on ~/.soosef/**
**Permission errors on ~/.fwmetadata/**
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. In Docker, the container runs as the `soosef` user
different user than the one who ran `fieldwitness init`, you will get permission denied errors.
Always run FieldWitness as the same user. In Docker, the container runs as the `fieldwitness` user
created during image build.
**Drop box tokens expire immediately**
@@ -1410,7 +1410,7 @@ sudo date -s "2026-04-01 12:00:00"
**Chain anchor TSA submission fails**
TSA submission requires network access. On Tier 1 (USB) or other airgapped devices, use
manual anchoring instead (`soosef chain anchor` without `--tsa`). If the TSA URL is
manual anchoring instead (`fieldwitness chain anchor` without `--tsa`). If the TSA URL is
unreachable, the anchor is still saved locally -- only the external timestamp token is
missing.
@@ -1420,8 +1420,8 @@ If you set `cover_name` after the certificate was already generated, delete the
certificate and restart:
```bash
rm ~/.soosef/certs/cert.pem ~/.soosef/certs/key.pem
sudo systemctl restart soosef
rm ~/.fwmetadata/certs/cert.pem ~/.fwmetadata/certs/key.pem
sudo systemctl restart fieldwitness
```
**Account lockout after repeated failed logins**
@@ -1452,15 +1452,15 @@ python verify.py
**Kubernetes pod stuck in CrashLoopBackOff**
Check logs with `kubectl logs -n soosef <pod-name>`. Common cause: the PersistentVolumeClaim
is not bound. Verify with `kubectl get pvc -n soosef`. The server needs 10Gi and the relay
Check logs with `kubectl logs -n fieldwitness <pod-name>`. Common cause: the PersistentVolumeClaim
is not bound. Verify with `kubectl get pvc -n fieldwitness`. The server needs 10Gi and the relay
needs 5Gi.
---
## Appendix A: Legacy Raspberry Pi Deployment
The Raspberry Pi was the original deployment target for SooSeF. It remains a viable
The Raspberry Pi was the original deployment target for FieldWitness. It remains a viable
option for fixed installations (e.g., a permanently installed newsroom server that does
not need to be portable). The three-tier model supersedes the RPi as the primary
deployment for most use cases because:
@@ -1477,7 +1477,7 @@ If you still want to use a Raspberry Pi for a fixed Tier 2 server:
libjpeg62-turbo-dev zlib1g-dev libffi-dev libssl-dev gfortran libopenblas-dev`
- Follow the Tier 2 bare metal instructions (systemd service, hardening) in this guide
- For GPIO hardware killswitch support, install the `rpi` extra:
`pip install "soosef[rpi]"` (includes `gpiozero`)
`pip install "fieldwitness[rpi]"` (includes `gpiozero`)
- Wire a momentary push button between GPIO 17 and 3.3V with a 10k pull-down to GND.
Default: 5-second hold to trigger. Configurable via `gpio_killswitch_hold_seconds`

View File

@@ -1,20 +1,20 @@
# Evidence Guide
**Audience**: Journalists, investigators, and legal teams who need to create, export, and
verify evidence packages from SooSeF.
verify evidence packages from FieldWitness.
**Prerequisites**: A running SooSeF instance with at least one attested image or file.
**Prerequisites**: A running FieldWitness instance with at least one attested image or file.
Familiarity with basic CLI commands.
---
## Overview
SooSeF provides three mechanisms for preserving and sharing evidence outside a running
FieldWitness provides three mechanisms for preserving and sharing evidence outside a running
instance: evidence packages (for handing specific files to third parties), cold archives
(full-state preservation for 10+ year horizons), and selective disclosure (proving specific
records without revealing the rest of the chain). All three include standalone verification
scripts that require no SooSeF installation.
scripts that require no FieldWitness installation.
---
@@ -37,24 +37,24 @@ verification of specific attested images or files.
```bash
# Package specific images with their attestation records
$ soosef evidence export photo1.jpg photo2.jpg --output evidence_package.zip
$ fieldwitness evidence export photo1.jpg photo2.jpg --output evidence_package.zip
# Filter by investigation tag
$ soosef evidence export photo1.jpg --investigation "case-2026-001" \
$ fieldwitness evidence export photo1.jpg --investigation "case-2026-001" \
--output evidence_case001.zip
```
### When to create evidence packages
- Before handing evidence to a legal team or court
- When sharing with a partner organization that does not run SooSeF
- When sharing with a partner organization that does not run FieldWitness
- Before crossing a hostile checkpoint (create the package, send it to a trusted party,
then activate the killswitch if needed)
- When an investigation is complete and files must be archived independently
### Verifying an evidence package
The recipient does not need SooSeF. They need only Python 3.11+ and the `cryptography`
The recipient does not need FieldWitness. They need only Python 3.11+ and the `cryptography`
pip package:
```bash
@@ -75,9 +75,9 @@ The verification script checks:
## Cold Archives
A cold archive is a full snapshot of the entire SooSeF evidence store, designed for
A cold archive is a full snapshot of the entire FieldWitness evidence store, designed for
long-term preservation aligned with OAIS (ISO 14721). It is self-describing and includes
everything needed to verify the evidence decades later, even if SooSeF no longer exists.
everything needed to verify the evidence decades later, even if FieldWitness no longer exists.
### What is inside a cold archive
@@ -86,7 +86,7 @@ everything needed to verify the evidence decades later, even if SooSeF no longer
| `chain/chain.bin` | Raw append-only hash chain binary |
| `chain/state.cbor` | Chain state checkpoint |
| `chain/anchors/` | External timestamp anchor files (RFC 3161 tokens, manual anchors) |
| `attestations/log.bin` | Full verisoo attestation log |
| `attestations/log.bin` | Full attest attestation log |
| `attestations/index/` | LMDB index files |
| `keys/public.pem` | Signer's Ed25519 public key |
| `keys/bundle.enc` | Encrypted key bundle (optional, password-protected) |
@@ -100,10 +100,10 @@ everything needed to verify the evidence decades later, even if SooSeF no longer
```bash
# Full archive without encrypted key bundle
$ soosef archive export --output archive_20260401.zip
$ fieldwitness archive export --output archive_20260401.zip
# Include encrypted key bundle (will prompt for passphrase)
$ soosef archive export --include-keys --output archive_20260401.zip
$ fieldwitness archive export --include-keys --output archive_20260401.zip
```
> **Warning:** If you include the encrypted key bundle, store the passphrase separately
@@ -119,11 +119,11 @@ $ soosef archive export --include-keys --output archive_20260401.zip
### Restoring from a cold archive
On a fresh SooSeF instance:
On a fresh FieldWitness instance:
```bash
$ soosef init
$ soosef archive import archive_20260401.zip
$ fieldwitness init
$ fieldwitness archive import archive_20260401.zip
```
### Long-term archival best practices
@@ -133,7 +133,7 @@ $ soosef archive import archive_20260401.zip
3. Include the encrypted key bundle in the archive with a strong passphrase
4. Periodically verify archive integrity: unzip and run `python verify.py`
5. The `ALGORITHMS.txt` file documents every algorithm and parameter used, so a verifier
can be written from scratch even if SooSeF no longer exists
can be written from scratch even if FieldWitness no longer exists
### The ALGORITHMS.txt file
@@ -143,7 +143,7 @@ This file documents every cryptographic algorithm, parameter, and format used:
- **Hashing**: SHA-256 for content and chain linkage; pHash and dHash for perceptual image matching
- **Encryption (key bundle)**: AES-256-GCM with Argon2id key derivation (time_cost=4, memory_cost=256MB, parallelism=4)
- **Chain format**: Append-only binary log with uint32 BE length prefixes and CBOR (RFC 8949) records
- **Attestation log**: Verisoo binary log format
- **Attestation log**: Attest binary log format
---
@@ -163,7 +163,7 @@ are part of an unbroken chain without seeing the contents of other records.
```bash
# Disclose records at chain indices 5, 12, and 47
$ soosef chain disclose --indices 5,12,47 --output disclosure.json
$ fieldwitness chain disclose --indices 5,12,47 --output disclosure.json
```
### Disclosure output format
@@ -180,7 +180,7 @@ $ soosef chain disclose --indices 5,12,47 --output disclosure.json
{
"chain_index": 5,
"content_hash": "...",
"content_type": "verisoo/attestation-v1",
"content_type": "attest/attestation-v1",
"prev_hash": "...",
"record_hash": "...",
"signer_pubkey": "...",
@@ -230,11 +230,11 @@ preceded it, because the chain is append-only with hash linkage.
If the device has internet access (even temporarily):
```bash
$ soosef chain anchor --tsa https://freetsa.org/tsr
$ fieldwitness chain anchor --tsa https://freetsa.org/tsr
```
This sends the chain head digest to a Timestamping Authority, receives a signed timestamp
token, and saves both as a JSON file under `~/.soosef/chain/anchors/`. The TSA token is a
token, and saves both as a JSON file under `~/.fwmetadata/chain/anchors/`. The TSA token is a
cryptographically signed proof from a third party that the hash existed at the stated time.
This is legally stronger than a self-asserted timestamp.
@@ -243,7 +243,7 @@ This is legally stronger than a self-asserted timestamp.
Without `--tsa`:
```bash
$ soosef chain anchor
$ fieldwitness chain anchor
```
This prints a compact text block. Publish it to any external witness:
@@ -268,16 +268,16 @@ This prints a compact text block. Publish it to any external witness:
For responding to a court order, subpoena, or legal discovery request:
1. **Selective disclosure** (`soosef chain disclose`) when the request specifies particular
1. **Selective disclosure** (`fieldwitness chain disclose`) when the request specifies particular
records and you must not reveal the full chain
2. **Evidence package** when the request requires original images with verification
capability
3. **Cold archive** when full preservation is required (e.g., an entire investigation)
All three formats include standalone verification scripts so the receiving party does not
need SooSeF installed. The verification scripts require only Python 3.11+ and the
need FieldWitness installed. The verification scripts require only Python 3.11+ and the
`cryptography` pip package.
> **Note:** Consult with legal counsel before producing evidence from SooSeF. The selective
> **Note:** Consult with legal counsel before producing evidence from FieldWitness. The selective
> disclosure mechanism is designed to support legal privilege and proportionality, but its
> application depends on your jurisdiction and the specific legal context.

View File

@@ -1,16 +1,16 @@
# Federation Guide
**Audience**: System administrators and technical leads setting up cross-organization
attestation sync between SooSeF instances.
attestation sync between FieldWitness instances.
**Prerequisites**: A running SooSeF instance (Tier 2 org server or Tier 3 relay), familiarity
**Prerequisites**: A running FieldWitness instance (Tier 2 org server or Tier 3 relay), familiarity
with the CLI, and trusted public keys from partner organizations.
---
## Overview
SooSeF federation synchronizes attestation records between organizations using a gossip
FieldWitness federation synchronizes attestation records between organizations using a gossip
protocol. Nodes periodically exchange Merkle roots, detect divergence, and fetch missing
records. The system is eventually consistent with no central coordinator, no leader
election, and no consensus protocol -- just append-only logs that converge.
@@ -20,7 +20,7 @@ Federation operates at two levels:
1. **Offline bundles** -- JSON export/import via sneakernet (USB drive). Works on all tiers
including fully airgapped Tier 1 field devices.
2. **Live gossip** -- HTTP-based periodic sync between Tier 2 org servers and Tier 3
federation relays. Requires the `federation` extra (`pip install soosef[federation]`).
federation relays. Requires the `federation` extra (`pip install fieldwitness[federation]`).
> **Warning:** Federation shares attestation records (image hashes, Ed25519 signatures,
> timestamps, and signer public keys). It never shares encryption keys, plaintext messages,
@@ -76,16 +76,16 @@ Each peer tracks:
Unhealthy peers are skipped during gossip rounds but remain registered. They are retried
on the next full gossip round. Peer state persists in SQLite at
`~/.soosef/attestations/federation/peers.db`.
`~/.fwmetadata/attestations/federation/peers.db`.
### Gossip interval
The default gossip interval is 60 seconds, configurable via the `VERISOO_GOSSIP_INTERVAL`
The default gossip interval is 60 seconds, configurable via the `FIELDWITNESS_GOSSIP_INTERVAL`
environment variable. In Docker Compose, set it in the environment section:
```yaml
environment:
- VERISOO_GOSSIP_INTERVAL=60
- FIELDWITNESS_GOSSIP_INTERVAL=60
```
Lower intervals mean faster convergence but more network traffic.
@@ -102,13 +102,13 @@ Always verify fingerprints out-of-band (in person or over a known-secure voice c
On Organization A:
```bash
$ cp ~/.soosef/identity/public.pem /media/usb/org-a-pubkey.pem
$ cp ~/.fwmetadata/identity/public.pem /media/usb/org-a-pubkey.pem
```
On Organization B:
```bash
$ soosef keys trust --import /media/usb/org-a-pubkey.pem
$ fieldwitness keys trust --import /media/usb/org-a-pubkey.pem
```
Repeat in both directions so each organization trusts the other.
@@ -123,7 +123,7 @@ Through the web UI at `/federation`, or via the peer store directly:
```bash
# On Org A's server, register Org B's federation endpoint
$ soosef federation peer add \
$ fieldwitness federation peer add \
--url https://orgb.example.org:8000 \
--fingerprint a1b2c3d4e5f6...
```
@@ -143,7 +143,7 @@ security groups, etc.).
For manual one-time sync:
```bash
$ soosef federation sync --peer https://orgb.example.org:8000
$ fieldwitness federation sync --peer https://orgb.example.org:8000
```
### Step 4: Monitor sync status
@@ -163,19 +163,19 @@ For Tier 1 field devices and airgapped environments, use offline bundles.
### Exporting a bundle
```bash
$ soosef chain export --output /media/usb/bundle.zip
$ fieldwitness chain export --output /media/usb/bundle.zip
```
To export only records from a specific investigation:
```bash
$ soosef chain export --investigation "case-2026-001" --output /media/usb/bundle.zip
$ fieldwitness chain export --investigation "case-2026-001" --output /media/usb/bundle.zip
```
To export a specific index range:
```bash
$ soosef chain export --start 100 --end 200 --output /media/usb/partial.zip
$ fieldwitness chain export --start 100 --end 200 --output /media/usb/partial.zip
```
### Importing a bundle
@@ -183,7 +183,7 @@ $ soosef chain export --start 100 --end 200 --output /media/usb/partial.zip
On the receiving instance:
```bash
$ soosef chain import /media/usb/bundle.zip
$ fieldwitness chain import /media/usb/bundle.zip
```
During import:
@@ -191,12 +191,12 @@ During import:
- Records signed by untrusted fingerprints are rejected
- Duplicate records (matching SHA-256) are skipped
- Imported records are tagged with `federated_from` metadata
- A delivery acknowledgment record (`soosef/delivery-ack-v1`) is automatically appended
- A delivery acknowledgment record (`fieldwitness/delivery-ack-v1`) is automatically appended
to the local chain
### Delivery acknowledgments
When a bundle is imported, SooSeF signs a `soosef/delivery-ack-v1` chain record that
When a bundle is imported, FieldWitness signs a `fieldwitness/delivery-ack-v1` chain record that
contains:
- The SHA-256 of the imported bundle file
@@ -208,7 +208,7 @@ bundle was delivered and ingested. It creates a two-way federation handshake.
```bash
# On receiving org: export the acknowledgment back
$ soosef chain export --start <ack_index> --end <ack_index> \
$ fieldwitness chain export --start <ack_index> --end <ack_index> \
--output /media/usb/delivery-ack.zip
```
@@ -293,7 +293,7 @@ Records are rejected if the signer's fingerprint is not in the local trust store
the sender's public key first:
```bash
$ soosef keys trust --import /path/to/sender-pubkey.pem
$ fieldwitness keys trust --import /path/to/sender-pubkey.pem
```
**Consistency proof failure**
@@ -302,7 +302,7 @@ A consistency proof failure means the peer's log is not a valid extension of the
This indicates a potential fork -- the peer may have a different chain history. Investigate
before proceeding:
1. Compare chain heads: `soosef chain status` on both instances
1. Compare chain heads: `fieldwitness chain status` on both instances
2. If a fork is confirmed, one instance's records must be exported and re-imported into a
fresh chain
@@ -311,7 +311,7 @@ before proceeding:
The gossip loop requires the `federation` extra:
```bash
$ pip install "soosef[federation]"
$ pip install "fieldwitness[federation]"
```
This installs `aiohttp` for async HTTP communication.

View File

@@ -1,4 +1,4 @@
# SooSeF Documentation
# FieldWitness Documentation
## For Reporters and Field Users
@@ -30,5 +30,5 @@
|---|---|
| [Federation Architecture](architecture/federation.md) | System design: threat model, layers (chain, bundles, federation), key domains, permission tiers. |
| [Chain Format Spec](architecture/chain-format.md) | CBOR record format, entropy witnesses, serialization, storage format, content types. |
| [Export Bundle Spec](architecture/export-bundle.md) | SOOSEFX1 binary format, envelope encryption (X25519 + AES-256-GCM), Merkle trees. |
| [Export Bundle Spec](architecture/export-bundle.md) | FIELDWITNESSX1 binary format, envelope encryption (X25519 + AES-256-GCM), Merkle trees. |
| [Federation Protocol Spec](architecture/federation-protocol.md) | CT-inspired server protocol: API endpoints, gossip, storage tiers, receipts, security model. |

View File

@@ -0,0 +1,240 @@
# C2PA Integration Plan
**Audience:** FieldWitness developers and maintainers
**Status:** Planning (pre-implementation)
**Last updated:** 2026-04-01
## Overview
FieldWitness needs C2PA (Coalition for Content Provenance and Authenticity) export/import
capability. C2PA is the emerging industry standard for content provenance, backed by
Adobe, Microsoft, Google, and the BBC. ProofMode, Guardian Project, and Starling Lab
have all adopted C2PA. FieldWitness must speak C2PA to remain relevant in the provenance
space.
---
## C2PA Spec Essentials
- JUMBF-based provenance standard embedded in media files
- Core structures: **Manifest Store > Manifest > Claim + Assertions + Ingredients + Signature**
- Claims are CBOR maps with assertion references, signing algorithm, `claim_generator`,
and timestamps
- Standard assertions:
- `c2pa.actions` -- edit history
- `c2pa.hash.data` -- hard binding (byte-range)
- `c2pa.location.broad` -- city/region location
- `c2pa.exif` -- EXIF metadata
- `c2pa.creative.work` -- title, description, authorship
- `c2pa.training-mining` -- AI training/mining consent
- Vendor-specific assertions under reverse-DNS (e.g., `org.fieldwitness.*`)
- Signing uses **COSE_Sign1** (RFC 9052)
- Supported algorithms: Ed25519 (OKP), ES256/ES384/ES512 (ECDSA), PS256/PS384/PS512 (RSA-PSS)
- **X.509 certificate chain required** -- embedded in COSE unprotected header; raw public
keys are not sufficient
- Offline validation works with pre-installed trust anchors; self-signed certs work in
"local trust anchor" mode
## Python Library: c2pa-python
- Canonical binding from C2PA org (PyPI: `c2pa-python`, GitHub: `contentauth/c2pa-python`)
- Rust extension (`c2pa-rs` via PyO3), not pure Python
- Version ~0.6.x, API not fully stable
- Platform wheels: manylinux2014 x86_64/aarch64, macOS, Windows
- **No armv6/armv7 wheels** -- affects Tier 1 Raspberry Pi deployments
- Core API: `c2pa.Reader`, `c2pa.Builder`, `builder.sign()`, `c2pa.create_signer()`
- `create_signer` takes a callback, algorithm, certs PEM, optional timestamp URL
- `timestamp_url=None` skips RFC 3161 timestamping (acceptable for offline use)
---
## Concept Mapping: FieldWitness to C2PA
### Clean mappings
| FieldWitness | C2PA |
|--------|------|
| `AttestationRecord` | C2PA Manifest |
| `attestor_fingerprint` | Signer cert subject (wrapped in X.509) |
| `AttestationRecord.timestamp` | Claim `created` (ISO 8601) |
| `CaptureMetadata.captured_at` | `c2pa.exif` DateTimeOriginal |
| `CaptureMetadata.location` | `c2pa.location.broad` |
| `CaptureMetadata.device` | `c2pa.exif` Make/Model |
| `CaptureMetadata.caption` | `c2pa.creative.work` description |
| `ImageHashes.sha256` | `c2pa.hash.data` (hard binding) |
| Ed25519 private key | COSE_Sign1 signing key (needs X.509 wrapper) |
### FieldWitness has, C2PA does not
- Perceptual hashes (phash, dhash) -- map to vendor assertion `org.fieldwitness.perceptual-hashes`
- Merkle log inclusion proofs -- map to vendor assertion `org.fieldwitness.merkle-proof`
- Chain records with entropy witnesses -- map to vendor assertion `org.fieldwitness.chain-record`
- Delivery acknowledgment records (entirely FieldWitness-specific)
- Cross-org gossip federation
- Perceptual matching for verification (survives recompression)
- Selective disclosure / redaction
### C2PA has, FieldWitness does not
- Hard file binding (byte-range exclusion zones)
- X.509 certificate trust chains
- Actions history (`c2pa.actions`: crop, rotate, AI-generate, etc.)
- AI training/mining consent
- Ingredient DAG (content derivation graph)
---
## Privacy Design
Three tiers of identity disclosure:
1. **Org-level cert (preferred):** One self-signed X.509 cert per organization, not per
person. Subject is org name. Individual reporters do not appear in the manifest.
2. **Pseudonym cert:** Subject is pseudonym or random UUID. Valid C2PA but unrecognized
by external trust anchors.
3. **No C2PA export:** For critical-threat presets, evidence stays in FieldWitness format until
reaching Tier 2.
### GPS handling
C2PA's `c2pa.location.broad` is city/region level. FieldWitness captures precise GPS. On
export, downsample to city-level unless the operator explicitly opts in. Precise GPS
stays in FieldWitness record only.
### Metadata handling
Strip all EXIF from the output file except what is intentionally placed in the
`c2pa.exif` assertion.
---
## Offline-First Constraints
- **Tier 1 (field, no internet):** C2PA manifests without RFC 3161 timestamp. FieldWitness
chain record provides timestamp anchoring via vendor assertion.
- **Tier 2 (org server, may have internet):** Optionally contact TSA at export time.
Connects to existing `anchors.py` infrastructure.
- Entropy witnesses embedded as vendor assertions provide soft timestamp evidence.
- Evidence packages include org cert PEM alongside C2PA manifest for offline verification.
- `c2pa-python` availability gated behind `has_c2pa()` -- not all hardware can run it.
---
## Architecture
### New module: `src/fieldwitness/c2pa_bridge/`
```
src/fieldwitness/c2pa_bridge/
__init__.py # Public API: export, import, has_c2pa()
cert.py # Self-signed X.509 cert generation from Ed25519 key
export.py # AttestationRecord -> C2PA manifest
importer.py # C2PA manifest -> AttestationRecord (best-effort)
vendor_assertions.py # org.fieldwitness.* assertion schemas
cli.py # CLI subcommands: fieldwitness c2pa export / verify / import
```
### Module relationships
- `export.py` reads from `attest/models.py`, `federation/chain.py`,
`keystore/manager.py`; calls `cert.py` and `vendor_assertions.py`
- `importer.py` reads image bytes, writes `AttestationRecord` via
`attest/attestation.py`, parses vendor assertions
### Web UI
New routes in the `attest.py` blueprint:
- `GET /attest/<record_id>/c2pa` -- download C2PA-embedded image
- `POST /attest/import-c2pa` -- upload and import C2PA manifest
### Evidence packages
`evidence.py` gains `include_c2pa=True` option. Adds C2PA-embedded file variants and
org cert to the ZIP.
### pyproject.toml extra
```toml
c2pa = ["c2pa-python>=0.6.0", "fieldwitness[attest]"]
```
---
## Implementation Phases
### Phase 0 -- Prerequisites (~1h)
- `has_c2pa()` in `_availability.py`
- `c2pa` extra in `pyproject.toml`
### Phase 1 -- Certificate management (~3h)
- `c2pa_bridge/cert.py`
- Self-signed X.509 from Ed25519 identity key
- Configurable subject (org name default, pseudonym for high-threat)
- Store at `~/.fwmetadata/identity/c2pa_cert.pem`
- Regenerate on key rotation
### Phase 2 -- Export path (~6h)
- `c2pa_bridge/export.py` + `vendor_assertions.py`
- Core function `export_c2pa()` takes image data, `AttestationRecord`, key, cert, options
- Builds assertions: `c2pa.actions`, `c2pa.hash.data`, `c2pa.exif`, `c2pa.creative.work`,
`org.fieldwitness.perceptual-hashes`, `org.fieldwitness.chain-record`, `org.fieldwitness.attestation-id`
- Vendor assertion schemas versioned (v1)
### Phase 3 -- Import path (~5h)
- `c2pa_bridge/importer.py`
- `import_c2pa()` reads C2PA manifest, produces `AttestationRecord`
- Maps C2PA fields to FieldWitness model
- Returns `C2PAImportResult` with `trust_status`
- Creates new FieldWitness attestation record over imported data
### Phase 4 -- CLI integration (~4h)
- `fieldwitness c2pa export/verify/import/show` subcommands
- Gated on `has_c2pa()`
### Phase 5 -- Web UI + evidence packages (~5h)
- Blueprint routes for export/import
- Evidence package C2PA option
### Phase 6 -- Threat-level presets (~2h)
- Add `c2pa` config block to each preset (`export_enabled`, `privacy_level`,
`include_precise_gps`, `timestamp_url`)
- `C2PAConfig` sub-dataclass in `FieldWitnessConfig`
### MVP scope
**Phases 0-2 (~10h):** Produces C2PA-compatible images viewable in Adobe Content
Credentials and any C2PA verifier.
---
## Key Decisions (Before Coding)
1. **Use existing Ed25519 identity key for cert** (not a separate key) -- preserves
single-key-domain design.
2. **Cert stored at `~/.fwmetadata/identity/c2pa_cert.pem`**, regenerated on key rotation.
3. **Tier 1 ARM fallback:** Tier 1 produces FieldWitness records; Tier 2 generates C2PA export
on their behalf.
4. **Pin `c2pa-python>=0.6.0`**, add shim layer for API stability.
5. **Hard binding computed by `c2pa-python` Builder** automatically.
---
## FieldWitness's Unique C2PA Value
- **Cross-org chain of custody** via gossip federation (delivery ack records as ingredients)
- **Perceptual hash matching** embedded in C2PA (survives JPEG recompression via
WhatsApp/Telegram)
- **Merkle log inclusion proofs** in manifest (proves attestation committed to append-only log)
- **Entropy witnesses** as soft timestamp attestation (makes backdating harder without
RFC 3161)
- **Privacy-preserving by design** (org certs, GPS downsampling, zero-identity mode)
- **Fully offline end-to-end verification** (bundled cert + `c2pa-python`, no network needed)

View File

@@ -0,0 +1,214 @@
# Go-to-Market Feasibility Plan
**Audience:** Internal planning (solo developer)
**Status:** Active planning document
**Last updated:** 2026-04-01
## Overview
Phased plan for building credibility and visibility for FieldWitness in the press freedom and
digital security space. Constraints: solo developer, ~10-15 hrs/week, portfolio/learning
project that should also produce real-world value.
---
## Current Strengths
- Federation layer is genuinely novel: gossip-based attestation sync across orgs with
offline-first design and append-only hash chains
- Three-tier deployment model maps to how press freedom orgs actually work
- C2PA export is well-timed as CAI gains momentum
- Working codebase with tests, deployment configs, documentation
## Core Challenges
- **Trust deficit:** "Some guy built a tool" is a warning sign in this space, not a
selling point
- **Chicken-and-egg:** Need audit for credibility, need credibility/money for audit,
need adoption for money
- **Limited bandwidth:** 10-15 hrs/week makes sequencing critical
- **Stego perception risk:** Steganography angle can be a credibility liability if
positioned as headline feature (perceived as "hacker toy")
---
## Phase 1: Foundation (Months 1-6)
**Goal:** Make the project legible to the ecosystem.
### Technical credibility (60% of time)
- Ship C2PA export as v0.3.0 headline feature (target: 8 weeks)
- Write formal threat model document at `docs/security/threat-model.md`
- Model after Signal protocol docs or Tor design doc
- De-emphasize steganography in public surfaces -- lead with "offline-first provenance
attestation with gossip federation"
- Set up reproducible builds with pinned dependencies
- Get CI/CD visibly working with test/lint/type-check/coverage badges
### Positioning and documentation (20% of time)
- Write "Why FieldWitness Exists" document (~1500 words): the problem, why existing tools
don't solve it, what FieldWitness does differently, who it's for, what it needs
- Create 2-minute demo video: field attestation -> sneakernet sync -> federation ->
verification
### Community engagement (20% of time)
- Lurk on `liberationtech@lists.stanford.edu` -- do NOT announce tool cold; wait for
relevant threads
- GitHub engagement with adjacent projects (real contributions, not performative):
- `guardian/proofmode-android`
- `contentauth/c2pa-python`
- `freedomofpress/securedrop`
- Post Show HN when C2PA export ships
---
## Phase 2: Credibility Escalation (Months 7-12)
**Goal:** Get external validation from at least one recognized entity.
### OTF (Open Technology Fund) -- https://www.opentech.fund/
**Internet Freedom Fund:** $50K-$900K over 12-36 months. Solo developers eligible.
Rolling applications.
**Red Team Lab:** FREE security audits commissioned through partner firms (Cure53, Trail
of Bits, Radically Open Security). This is the single highest-leverage action.
**Usability Lab:** Free UX review.
**Application timeline:** 2-4 months from submission to decision.
**Strategy:** Apply to Red Team Lab for audit FIRST (lower commitment for OTF, validates
you as "OTF-vetted").
### Compelling application elements
1. Lead with problem: "Provenance attestation tools assume persistent internet. For
journalists in [specific scenario], this fails."
2. Lead with differentiator: "Gossip federation for cross-org attestation sync,
offline-first, bridges to C2PA."
3. Be honest about status: "Working prototype at v0.3.0, needs audit and field testing."
4. Budget: stipend, audit (if Red Team Lab unavailable), 1-2 conferences, federation
relay hosting.
### Backup audit and funding paths
| Organization | URL | Notes |
|---|---|---|
| OSTIF | https://ostif.org/ | Funds audits for open-source projects; may be too early-stage |
| Radically Open Security | https://www.radicallyopensecurity.com/ | Nonprofit, reduced rates for internet freedom projects; focused audit ~$15-30K |
| NLnet Foundation | https://nlnet.nl/ | EUR 5-50K grants, lightweight process, solo devs welcome, includes audit funding |
| Filecoin Foundation for Decentralized Web | https://fil.org/grants | Relevant to federation/provenance angle |
### Community building
- Submit talk to **IFF 2027** (Internet Freedom Festival, Valencia, ~March)
- Open sessions and tool showcases have low barriers
- Talk title: "Federated Evidence Chains: Offline Provenance for Journalists in
Hostile Environments"
- Cold outreach to 3-5 specific people:
- Access Now Digital Security Helpline trainers
- Harlo Holmes (FPF Director of Digital Security)
- Guardian Project developers (ProofMode team)
- Position as complementary, not competitive
- Lead with "I want honest feedback"
- Conferences:
- **RightsCon** -- https://www.rightscon.org/
- **IFF** -- https://internetfreedomfestival.org/
- **USENIX Security / PETS** -- academic venues, for federation protocol paper
---
## Phase 3: Traction or Pivot (Months 13-24)
### Green lights (keep going)
- OTF Red Team Lab acceptance or any grant funding
- A digital security trainer says "I could see using this"
- A journalist or NGO runs it in any scenario
- Another developer contributes a meaningful PR
- Conference talk accepted
### Red lights (pivot positioning)
- Zero response from outreach after 6+ months
- Funders say problem is already solved
- Security reviewers find fundamental design flaws
### If green (months 13-24)
- Execute audit, publish results publicly (radical transparency)
- Build pilot deployment guide
- Apply for Internet Freedom Fund
- Present at RightsCon 2027/2028
### If red (months 13-24)
- Reposition as reference implementation / research project
- Write federation protocol as academic paper
- Lean into portfolio angle
---
## Professional Portfolio Positioning
### Framing
"I designed and implemented a gossip-based federation protocol for offline-first
provenance attestation, targeting field deployment in resource-constrained environments.
The system uses Ed25519 signing, Merkle trees with consistency proofs, append-only hash
chains with CBOR serialization, and bridges to the C2PA industry standard."
### Skills demonstrated
- Cryptographic protocol design
- Distributed systems (gossip, consistency proofs)
- Security engineering (threat modeling, audit prep, key management)
- Systems architecture (three-tier, offline-first)
- Domain expertise (press freedom, evidence integrity)
- Grant writing (if pursued)
### Target roles
- Security engineer (FPF, EFF, Access Now, Signal, Cloudflare)
- Protocol engineer (decentralized systems)
- Developer advocate (security companies)
- Infrastructure engineer
### Key portfolio artifacts
- Threat model document (shows security thinking)
- Audit report, even with findings (shows maturity)
- C2PA bridge (shows standards interop, not just NIH)
---
## Timeline (10-15 hrs/week)
| Month | Focus | Deliverable | Time split |
|-------|-------|-------------|------------|
| 1-2 | C2PA export + threat model | v0.3.0, `threat-model.md` | 12 code, 3 docs |
| 3-4 | Demo video + "Why FieldWitness" + CI | Video, doc, badges | 8 code, 4 docs, 3 outreach |
| 5-6 | OTF Red Team Lab app + community | Application submitted, Show HN | 5 code, 5 grants, 5 outreach |
| 7-9 | Community + backup grants | Outreach emails, NLnet/FFDW apps | 8 code, 3 grants, 4 outreach |
| 10-12 | IFF submission + traction check | Talk submitted, go/no-go decision | 8 code, 2 grants, 5 outreach |
| 13-18 | (If green) Audit + pilot guide | Published audit, pilot doc | 10 code, 5 docs |
| 19-24 | (If green) Conference + IFF app | Talk, major grant application | 5 code, 5 grant, 5 outreach |
---
## What NOT to Bother With
- Paid marketing, ads, PR
- Product Hunt, startup directories, "launch" campaigns
- Project website beyond clean README
- Corporate partnerships
- Whitepapers before audit
- Mobile apps
- Discord/Slack community (dead community is worse than none)
- Press coverage (too early)
- Competing with SecureDrop on source protection
- General tech conference talks (domain-specific venues only)

View File

@@ -0,0 +1,480 @@
# FieldWitness Threat Model
**Status:** Living document -- updated as the design evolves and as external review
identifies gaps. Version numbers track significant revisions.
**Document version:** 0.1 (2026-04-01)
**Corresponds to:** FieldWitness v0.2.0
This document follows the style of the Signal Protocol specification and the Tor design
document: it makes precise claims, distinguishes what is guaranteed from what is not, and
does not use marketing language. Unresolved questions and known gaps are stated plainly.
**This document has not been externally audited.** Claims here reflect the designer's
intent and analysis. An independent security review is planned as part of Phase 2 (see
`docs/planning/gtm-feasibility.md`). Until that review is complete, treat this document
as a design statement, not a security certification.
---
## Table of Contents
1. [Intended Users](#1-intended-users)
2. [Adversary Model](#2-adversary-model)
3. [Assets Protected](#3-assets-protected)
4. [Trust Boundaries](#4-trust-boundaries)
5. [Security Guarantees](#5-security-guarantees)
6. [Non-Guarantees](#6-non-guarantees)
7. [Cryptographic Primitives](#7-cryptographic-primitives)
8. [Key Management Model](#8-key-management-model)
9. [Federation Trust Model](#9-federation-trust-model)
10. [Known Limitations](#10-known-limitations)
---
## 1. Intended Users
FieldWitness is designed for three overlapping user populations:
**Field reporters and documenters.** Journalists, human rights monitors, and election
observers working in environments where physical device seizure is a plausible risk.
Operating assumption: the user may be detained, the device may be confiscated, and the
operator at Tier 2 (the org server) may not be reachable in real time. The user needs to
attest evidence locally on a Tier 1 (field device) and sync later -- or never, if the USB
is destroyed.
**Organizational administrators.** IT staff and security-aware operators at newsrooms or
NGOs running Tier 2 deployments. They manage keys, configure threat levels, operate the
source drop box, and maintain federation peering with partner organizations. They are
expected to understand basic operational security concepts but are not expected to be
cryptographers.
**Partner organizations.** Organizations that receive attested evidence bundles from the
primary organization and need to verify chain-of-custody without installing FieldWitness. They
interact with standalone `verify.py` scripts included in evidence packages.
FieldWitness is **not** designed as a general-purpose secure communications tool, a replacement
for SecureDrop's source protection model, or a consumer privacy application.
---
## 2. Adversary Model
### 2.1 Passive Network Observer
**Capability:** Can observe all network traffic between nodes, including Tier 2 to Tier 3
communication and gossip federation traffic. Cannot break TLS or Ed25519.
**Goal:** Determine which organizations are communicating, the timing and volume of
attestation syncs, and potentially correlate sync events with news events.
**FieldWitness's position:** Transport-level metadata (IP addresses, timing, volume) is not
hidden. TLS (self-signed, port 8000) protects payload content. A passive observer can
determine that two Tier 2 servers are federating; they cannot read the attestation records
being exchanged without the relevant Ed25519 public keys.
**Gap:** No traffic padding, no onion routing, no anonymization of federation topology.
Organizations with strong network-level adversaries should route federation traffic through
Tor or a VPN. This is not built in.
### 2.2 Active Network Adversary
**Capability:** Can intercept, modify, replay, and drop traffic. Can present forged
TLS certificates if the operator hasn't pinned the peer's certificate.
**Goal:** Inject forged attestation records into the federation, suppress legitimate
records, or cause evidence to appear tampered.
**FieldWitness's position:** All attestation records are Ed25519-signed. A network adversary
cannot forge a valid signature without the private key. The append-only hash chain makes
retroactive injection detectable: inserting a record at position N requires recomputing all
subsequent hashes. Consistency proofs during gossip sync detect log divergence.
**Gap:** Certificate pinning for federation peers is not implemented as of v0.2.0. The
Tier 3 relay uses a self-signed certificate; operators should verify its fingerprint
out-of-band. Gossip peers authenticate by Ed25519 fingerprint, not certificate, which
provides a secondary check.
### 2.3 Physical Access
**Capability:** Has physical access to the field device (Tier 1 USB) or the org server
(Tier 2). May have forensic tools.
**Goal:** Extract private keys, recover attested evidence, identify the operator, or
determine what evidence was collected.
**FieldWitness's position:**
- Tier 1 is designed for amnesia: Debian Live USB with LUKS-encrypted persistent
partition. Pulling the USB from the host leaves no trace on the host machine. If the USB
itself is seized, LUKS protects the persistent partition.
- The killswitch (`fieldwitness fieldkit purge`) destroys all key material and data under
`~/.fwmetadata/` in sensitivity order. The deep forensic scrub removes Python bytecache, pip
metadata, download cache, and shell history entries. The final step is `pip uninstall
-y fieldwitness`.
- The dead man's switch fires the killswitch automatically if check-in is missed.
- Private keys are stored as PEM files with `0600` permissions. Key material is not
additionally encrypted at rest beyond the filesystem (LUKS on Tier 1; operator-managed
on Tier 2).
**Gap:** If the device is seized before the killswitch fires and LUKS has been unlocked
(i.e., the device is running), private keys are accessible. Cold boot attacks against
unlocked LUKS volumes are not mitigated. Key material is not stored in a hardware security
module or OS keychain.
### 2.4 Legal Compulsion
**Capability:** Can compel the operator (or their legal jurisdiction) to produce data,
keys, or records. May use court orders, search warrants, or jurisdiction-specific
administrative processes.
**Goal:** Obtain attestation records or private keys under legal authority.
**FieldWitness's position:** FieldWitness provides tools (selective disclosure, evidence packages)
for producing specific records under court order without revealing the full chain.
The Federation Relay (Tier 3) stores only hashes and signatures -- never private keys or
plaintext. Placing Tier 3 in a jurisdiction with strong press protections limits one
compulsion surface.
**Gap:** If private keys are seized, all past and future attestations signed by those keys
are attributable to the key holder. Key rotation limits forward exposure after a
compromise, but prior records signed by the old key remain attributable. FieldWitness does not
implement deniable authentication.
### 2.5 Insider Threat
**Capability:** Has legitimate access to the FieldWitness instance (e.g., a trusted
administrator or a compromised org server). Can read key material, attestation records,
and logs.
**Goal:** Selectively alter or delete records, export keys, or suppress evidence.
**FieldWitness's position:** The append-only hash chain makes deletion or modification of prior
records detectable: the chain head hash changes and any external anchor (RFC 3161 TSA,
blockchain transaction) will no longer match. Key rotation is logged in the chain as a
`fieldwitness/key-rotation-v1` record signed by the old key, creating an auditable trail.
**Gap:** An insider with direct filesystem access can overwrite `chain.bin` entirely,
including the chain head, before an external anchor is taken. The chain provides integrity
guarantees only to the extent that external anchors are taken regularly and independently
(by another party or a public TSA). Frequency of anchoring is an operational decision, not
enforced by the software.
---
## 3. Assets Protected
The following assets are in scope for FieldWitness's security model:
| Asset | Description | Primary Protection |
|---|---|---|
| Attestation records | Ed25519-signed records linking a file hash to a time, identity, and optional metadata | Append-only chain, Ed25519 signatures |
| Identity private key | Ed25519 private key used to sign attestations | `0600` filesystem permissions, LUKS on Tier 1, killswitch |
| Channel key | AES-256-GCM key used for steganographic encoding | `0600` filesystem permissions, separate from identity key |
| Source submissions | Anonymous uploads through the drop box | EXIF stripping, no-branding upload page, HMAC receipt codes |
| Evidentiary metadata | GPS, timestamp, device model extracted from EXIF | Stored in attestation record, dangerous fields stripped |
| Federation topology | Which organizations are peering | Not protected at network level (see 2.1) |
The following are **out of scope** (not protected by FieldWitness):
- Source identity beyond what is stripped from EXIF
- Operator identity (FieldWitness does not provide anonymity)
- Content of files beyond what is hashed and signed (files are not encrypted at rest
unless encrypted before attestation)
- The Tier 3 relay's knowledge of federation topology
---
## 4. Trust Boundaries
```
[Field reporter / Tier 1] --- LUKS + killswitch --- [Seized device adversary]
|
[USB sneakernet or LAN]
|
[Org Server / Tier 2] ------- TLS (self-signed) ---- [Network adversary]
| [Active attacker: no forged sigs]
[Gossip federation]
|
[Federation Relay / Tier 3] - Stores hashes+sigs only, no keys
|
[Partner Org / Tier 2] ------ Ed25519 trust store -- [Untrusted peers rejected]
|
[Verifying party] ----------- standalone verify.py, cryptography package only
```
**Tier 1 trusts:** Its own key material (generated locally), the Tier 2 server it was
configured to sync with.
**Tier 2 trusts:** Its own key material, the Ed25519 public keys in its trust store
(imported explicitly by the administrator), the Tier 3 relay for transport only (not
content validation).
**Tier 3 trusts:** Nothing. It is a content-unaware relay. It cannot validate the
semantic content of what it stores because it has no access to private keys.
**Verifying party trusts:** The signer's Ed25519 public key (received out-of-band, e.g.,
in the evidence package), the `cryptography` Python package, and the chain linkage logic
in `verify.py`.
---
## 5. Security Guarantees
The following are properties FieldWitness is designed to provide. Each is conditional on the
named preconditions.
**G1: Attestation integrity.** Given an attestation record and the signer's Ed25519 public
key, a verifier can determine whether the record has been modified since signing.
_Precondition:_ The verifier has the correct public key and the `cryptography` package.
_Mechanism:_ Ed25519 signature over deterministic JSON serialization of the record.
**G2: Chain append-only property.** If a record is in the chain at position N, it cannot
be removed or modified without invalidating every subsequent record's hash linkage.
_Precondition:_ The verifier has observed the chain head at some prior point or has an
external anchor.
_Mechanism:_ Each record includes `prev_hash = SHA-256(canonical_bytes(record[N-1]))`.
**G3: Timestamp lower bound.** If the chain head has been submitted to an RFC 3161 TSA
and the token is preserved, all records prior to the anchored head provably existed before
the TSA's signing time.
_Precondition:_ The TSA's clock and signing key are trusted.
_Mechanism:_ RFC 3161 timestamp tokens stored in `chain/anchors/`.
**G4: Selective disclosure soundness.** A selective disclosure bundle proves that the
disclosed records are part of an unbroken chain without revealing the contents of
non-disclosed records.
_Precondition:_ The verifier has the chain head hash from an external source.
_Mechanism:_ Non-selected records appear as hashes only; chain linkage is preserved.
**G5: Federation record authenticity.** Records received via federation are accepted only
if signed by a key in the local trust store.
_Precondition:_ The trust store contains only keys the operator has explicitly imported.
_Mechanism:_ Ed25519 verification against trust store before appending federated records.
**G6: Source drop box anonymity (limited).** A source submitting via the drop box does
not need an account, FieldWitness is not mentioned on the upload page, and dangerous EXIF fields
are stripped before the file is stored.
_Precondition:_ The source accesses the drop box URL over HTTPS without revealing their
identity through other means (IP, browser fingerprint, etc.).
_Limitation:_ FieldWitness does not route drop box traffic through Tor or any anonymization
layer. Network-level anonymity is the source's responsibility.
---
## 6. Non-Guarantees
The following properties are explicitly **not** provided by FieldWitness. Including them here
prevents users from assuming protection that does not exist.
**NG1: Operator anonymity.** FieldWitness does not hide the identity of the organization
running the instance. The Tier 2 server has an IP address. The federation relay knows
which Tier 2 servers are peering.
**NG2: Deniable authentication.** Attestation records are non-repudiably signed by
Ed25519 keys. There is no plausible deniability about which key produced a signature.
**NG3: Forward secrecy for attestation keys.** Ed25519 identity keys are long-lived.
If a private key is compromised, all attestations signed by that key are attributable to
the key holder. Key rotation limits future exposure but does not retroactively remove
attributability.
**NG4: Protection against a compromised Tier 2 before anchoring.** An insider with full
Tier 2 access can rewrite the chain before any external anchor is taken. External anchors
are the primary protection against insider tampering; their value is proportional to how
frequently and independently they are taken.
**NG5: Content confidentiality.** FieldWitness does not encrypt attested files at rest. Files
are hashed and signed, not encrypted. Encryption before attestation is the operator's
responsibility.
**NG6: Source protection beyond EXIF stripping.** The drop box strips dangerous EXIF
fields and does not log source IP addresses in attestation records. It does not provide
the same source protection model as SecureDrop. Organizations with strong source
protection requirements should use SecureDrop for intake and FieldWitness for evidence chain
management.
**NG7: Auditability of the Tier 3 relay.** The relay stores only hashes and signatures,
but FieldWitness does not currently provide a mechanism for operators to audit what the relay
has and has not forwarded. The relay is trusted for availability, not integrity.
---
## 7. Cryptographic Primitives
All cryptographic choices are documented here to support independent review and long-term
archival verifiability.
### Signing
| Primitive | Algorithm | Parameters | Use |
|---|---|---|---|
| Identity signing | Ed25519 | RFC 8032 | Sign attestation records, key rotation records, delivery acks |
| Key storage | PEM | PKCS8 (private), SubjectPublicKeyInfo (public) | Disk format for identity keypair |
Ed25519 was chosen for: short key and signature sizes (32-byte public key, 64-byte
signature), deterministic signing (no random oracle required per operation), strong
security margins, and wide library support.
### Encryption (Stego channel key domain)
| Primitive | Algorithm | Parameters | Use |
|---|---|---|---|
| Symmetric encryption | AES-256-GCM | 256-bit key, 96-bit IV, 128-bit tag | Payload encryption in stego encode |
| Key derivation | Argon2id | time=3, memory=65536, parallelism=4, saltlen=16 | Derive AES key from passphrase + PIN + reference photo fingerprint |
**Note:** The AES-256-GCM channel key domain (Stego) and the Ed25519 identity key
domain (Attest) are kept strictly separate. They serve different security purposes and
share no key material.
### Hashing
| Primitive | Algorithm | Use |
|---|---|---|
| Cryptographic hash | SHA-256 | Chain record linkage (`prev_hash`), content fingerprinting |
| Content fingerprinting | SHA-256 | `ImageHashes.sha256` for all file types |
| Perceptual hash | pHash (DCT-based) | Image tamper detection, survives compression |
| Perceptual hash | dHash (difference hash) | Image tamper detection |
| Perceptual hash | aHash (average hash) | Fuzzy matching, high tolerance |
| Chain serialization | CBOR (RFC 7049) | Canonical encoding for chain records |
| HMAC | HMAC-SHA256 | Drop box receipt code derivation |
### External Timestamping
| Mechanism | Standard | Use |
|---|---|---|
| RFC 3161 TSA | RFC 3161 | Automated, signed timestamp tokens |
| Manual anchor | Any external witness | Chain head hash submitted to blockchain, email, etc. |
---
## 8. Key Management Model
### Key types and locations
| Key | Type | Location | Purpose |
|---|---|---|---|
| Identity private key | Ed25519 | `~/.fwmetadata/identity/private.pem` | Sign all attestation records |
| Identity public key | Ed25519 | `~/.fwmetadata/identity/public.pem` | Shared with verifiers; included in evidence packages |
| Channel key | AES-256-GCM | `~/.fwmetadata/stego/channel.key` | Stego encoding/decoding shared secret |
| Trust store keys | Ed25519 (public only) | `~/.fwmetadata/trusted_keys/<fingerprint>/` | Verify federated records from partners |
### Key rotation
Identity rotation creates a `fieldwitness/key-rotation-v1` chain record signed by the **old**
key, containing the new public key. This establishes a cryptographic chain of trust from
the original key through all rotations. Verifiers following the rotation chain can confirm
that new attestations come from the same organizational identity as old ones.
Channel (AES) key rotation creates a new key and archives the old one. Old channel keys
are required to decode stego payloads encoded with them; archived keys are preserved under
`~/.fwmetadata/stego/archived/`.
### Identity recovery
After device loss, a `fieldwitness/key-recovery-v1` chain record is signed by the **new** key,
carrying the old key's fingerprint and optional cosigner fingerprints. This is an
auditable assertion, not a cryptographic proof that the old key authorized the recovery
(the old key is lost). The recovery record's legitimacy depends on out-of-band
confirmation (e.g., cosigner verification, organizational attestation).
### Backup
The keystore manager (`fieldwitness/keystore/manager.py`) tracks backup state. Encrypted key
bundles can be exported to the SOOBNDL format for cold storage. The backup reminder
interval is configurable; the default is 7 days.
---
## 9. Federation Trust Model
### Peer authentication
Federation peers are identified by their Ed25519 public key fingerprint (first 16 bytes of
SHA-256 of the public key, hex-encoded). Peering is established by explicit administrator
action: the peer's public key fingerprint is configured locally. There is no automatic
peer discovery or trust-on-first-use.
### Record acceptance
A record received via federation is accepted only if:
1. It is signed by an Ed25519 key in the local trust store.
2. The signature is valid over the record's canonical serialization.
3. The record does not duplicate a record already in the local log (by record ID).
Records signed by unknown keys are silently dropped. There is no mechanism to accept
records from temporarily trusted but unregistered peers.
### The Tier 3 relay
The Tier 3 relay is a content-unaware intermediary. It forwards attestation bundles between
Tier 2 nodes but has no access to private keys and cannot validate the semantic content of
records. It is trusted for availability (it should forward what it receives) but not for
integrity (it cannot be used as an authority for whether records are authentic).
### Consistency proofs
During gossip sync, nodes exchange their current Merkle log root and size. If roots
differ, the node with fewer records requests a consistency proof from the node with more
records. The consistency proof proves that the smaller log is a prefix of the larger log,
preventing log divergence. Records are fetched incrementally after the proof verifies.
The consistency proof implementation is in `src/fieldwitness/attest/merkle.py`.
---
## 10. Known Limitations
This section is a candid accounting of current gaps. Items here are candidates for future
work, not dismissals.
**L1: No hardware key storage.** Private keys are stored as PEM files protected only by
filesystem permissions and LUKS (on Tier 1). A hardware security module (HSM), TPM, or OS
keychain would provide stronger protection against physical extraction from a running
system. This is a significant gap for high-threat deployments.
**L2: No certificate pinning for federation.** Tier 2 to Tier 3 connections use TLS with
self-signed certificates. The peer's certificate fingerprint is not currently pinned in
configuration. An active network adversary with the ability to present a forged certificate
could intercept federation traffic. The Ed25519 peer fingerprint provides a secondary check
but is not a substitute.
**L3: Killswitch reliability.** The killswitch's effectiveness depends on the operating
system's file deletion semantics. On HDDs without secure erase, file overwriting may not
prevent forensic recovery. On SSDs with wear leveling, even overwriting does not guarantee
physical deletion. The deep forensic scrub does multiple passes, but this is not
equivalent to verified physical destruction. For critical-threat deployments, physical
destruction of storage media is more reliable than software scrub.
**L4: Anchor frequency is an operational decision.** The chain's tamper-evidence
properties against insider threats depend on how frequently external anchors are taken.
FieldWitness does not enforce or automate anchor frequency. An organization that anchors
infrequently has a larger window during which insider tampering is undetectable.
**L5: Gossip topology is not hidden.** The list of peers a node gossips with is visible
to a network observer. For organizations where federation topology is itself sensitive
information, all federation should be routed through Tor or equivalent.
**L6: No audit of Tier 3 relay behavior.** FieldWitness does not currently provide a way for
operators to verify that the Tier 3 relay has faithfully forwarded all bundles it received.
A malicious or compromised relay could suppress specific records. The design mitigation is
to use the relay only for transport, never as an authoritative source -- but no
verification mechanism is implemented.
**L7: Drop box source anonymity is limited.** The drop box does not log source IP addresses
in attestation records or require accounts, but it does not anonymize the source's network
connection. A source's IP is visible to the Tier 2 server operator in web server access
logs. Organizations providing source protection should use Tor for source access and may
wish to configure the web server to not log IP addresses.
**L8: Steganalysis resistance is not guaranteed.** The steganography backend includes a
steganalysis module (`stego/steganalysis.py`) for estimating detection resistance, but
stego channels are not guaranteed to be undetectable by modern ML-based steganalysis tools
under all conditions. Stego should be treated as a covert channel with meaningful
detection risk, not a guaranteed-invisible channel.
**L9: No formal security proof.** The security of FieldWitness's federation protocol, chain
construction, and selective disclosure has not been formally analyzed. The design draws on
established primitives (Ed25519, SHA-256, RFC 3161, Merkle trees) and patterns (gossip,
append-only logs, Certificate Transparency-inspired consistency proofs), but informal
design analysis is not a substitute for a formal proof or an independent security audit.

View File

@@ -1,15 +1,15 @@
# Source Drop Box Setup Guide
**Audience**: Administrators setting up SooSeF's anonymous source intake feature.
**Audience**: Administrators setting up FieldWitness's anonymous source intake feature.
**Prerequisites**: A running SooSeF instance with web UI enabled (`soosef[web]` extra),
**Prerequisites**: A running FieldWitness instance with web UI enabled (`fieldwitness[web]` extra),
an admin account, and HTTPS configured (self-signed is acceptable).
---
## Overview
The source drop box is a SecureDrop-style anonymous file intake built into the SooSeF web
The source drop box is a SecureDrop-style anonymous file intake built into the FieldWitness web
UI. Admins create time-limited upload tokens, sources open the token URL in a browser and
submit files without creating an account. Files are processed through the extract-then-strip
EXIF pipeline and automatically attested on receipt. Sources receive HMAC-derived receipt
@@ -24,7 +24,7 @@ codes that prove delivery.
## How It Works
```
Admin Source SooSeF Server
Admin Source FieldWitness Server
| | |
|-- Create token ------------->| |
| (label, expiry, max_files) | |
@@ -58,11 +58,11 @@ The drop box should always be served over HTTPS. Sources must be able to trust t
connection is not being intercepted.
```bash
$ soosef serve --host 0.0.0.0
$ fieldwitness serve --host 0.0.0.0
```
SooSeF auto-generates a self-signed certificate on first HTTPS start. For production use,
place a reverse proxy with a proper TLS certificate in front of SooSeF.
FieldWitness auto-generates a self-signed certificate on first HTTPS start. For production use,
place a reverse proxy with a proper TLS certificate in front of FieldWitness.
### Step 2: Create an upload token
@@ -95,7 +95,7 @@ Share the upload URL over an already-secure channel:
### Step 4: Source uploads files
The source opens the URL in their browser. The upload page is minimal -- no SooSeF branding,
The source opens the URL in their browser. The upload page is minimal -- no FieldWitness branding,
no identifying marks, generic styling. The page works over Tor Browser with JavaScript
enabled (no external resources, no CDN, no fonts, no analytics).
@@ -118,7 +118,7 @@ The admin panel at `/dropbox/admin` shows:
## The Extract-Then-Strip Pipeline
Every file uploaded through the drop box is processed through SooSeF's EXIF pipeline:
Every file uploaded through the drop box is processed through FieldWitness's EXIF pipeline:
1. **Extract**: all EXIF metadata is read from the original image bytes
2. **Classify**: fields are split into evidentiary (GPS coordinates, capture timestamp --
@@ -173,10 +173,10 @@ comparing their locally computed hash with the server's receipt.
| What | Where |
|---|---|
| Uploaded files (stripped) | `~/.soosef/temp/dropbox/` (mode 0700) |
| Token metadata | `~/.soosef/auth/dropbox.db` (SQLite) |
| Receipt codes | `~/.soosef/auth/dropbox.db` (SQLite) |
| Attestation records | `~/.soosef/attestations/` (standard attestation log) |
| Uploaded files (stripped) | `~/.fwmetadata/temp/dropbox/` (mode 0700) |
| Token metadata | `~/.fwmetadata/auth/dropbox.db` (SQLite) |
| Receipt codes | `~/.fwmetadata/auth/dropbox.db` (SQLite) |
| Attestation records | `~/.fwmetadata/attestations/` (standard attestation log) |
Expired tokens are cleaned up automatically on every admin page load.
@@ -186,9 +186,9 @@ Expired tokens are cleaned up automatically on every admin page load.
### Source safety
- **No SooSeF branding** on the upload page. Generic "Secure File Upload" title.
- **No FieldWitness branding** on the upload page. Generic "Secure File Upload" title.
- **No authentication required** -- sources never create accounts or reveal identity.
- **No IP logging** -- SooSeF does not log source IP addresses. Ensure your reverse proxy
- **No IP logging** -- FieldWitness does not log source IP addresses. Ensure your reverse proxy
(if any) also does not log access requests to `/dropbox/upload/` paths.
- **Self-contained page** -- inline CSS and JavaScript only. No external resources, CDN
calls, web fonts, or analytics. Works with Tor Browser.
@@ -203,12 +203,12 @@ Expired tokens are cleaned up automatically on every admin page load.
Once reached, the link stops accepting uploads.
- **Revoke immediately** -- if a token is compromised or no longer needed, revoke it from
the admin panel. This deletes the token and all associated receipt records from SQLite.
- **Audit trail** -- token creation events are logged to `~/.soosef/audit.jsonl` with the
- **Audit trail** -- token creation events are logged to `~/.fwmetadata/audit.jsonl` with the
action `dropbox.token_created`.
### Running as a Tor hidden service
For maximum source protection, run SooSeF as a Tor hidden service:
For maximum source protection, run FieldWitness as a Tor hidden service:
1. Install Tor on the server
2. Configure a hidden service in `torrc` pointing to `127.0.0.1:5000`

View File

@@ -1,7 +1,7 @@
# SooSeF Admin Operations Guide
# FieldWitness Admin Operations Guide
**Audience**: IT administrators, system operators, and technically competent journalists
responsible for deploying, configuring, and maintaining SooSeF instances for their
responsible for deploying, configuring, and maintaining FieldWitness instances for their
organization.
**Prerequisites**: Familiarity with Linux command line, Docker basics, and SSH. For Tier 1
@@ -15,9 +15,9 @@ This guide covers the operational tasks an admin performs after initial deployme
installation and deployment, see [deployment.md](../deployment.md). For architecture
details, see [docs/architecture/](../architecture/).
Your responsibilities as a SooSeF admin:
Your responsibilities as a FieldWitness admin:
1. Deploy and maintain SooSeF instances (Tier 1 USB, Tier 2 server, Tier 3 relay)
1. Deploy and maintain FieldWitness instances (Tier 1 USB, Tier 2 server, Tier 3 relay)
2. Manage user accounts and access
3. Configure threat level presets for your environment
4. Manage the source drop box
@@ -42,7 +42,7 @@ Each user has:
From the admin panel, issue a temporary password for a locked-out user. The user should
change it on next login. All password resets are recorded in the audit log
(`~/.soosef/audit.jsonl`).
(`~/.fwmetadata/audit.jsonl`).
### Account Lockout
@@ -54,7 +54,7 @@ For persistent lockout (e.g., a compromised account), delete the user from the a
### Audit Trail
All admin actions are logged to `~/.soosef/audit.jsonl` in JSON-lines format:
All admin actions are logged to `~/.fwmetadata/audit.jsonl` in JSON-lines format:
```json
{"timestamp": "2026-04-01T12:00:00+00:00", "actor": "admin", "action": "user.create", "target": "user:reporter1", "outcome": "success", "source": "web"}
@@ -70,13 +70,13 @@ Actions logged: `user.create`, `user.delete`, `user.password_reset`,
## 2. Threat Level Configuration
SooSeF ships four presets at `deploy/config-presets/`. Select based on your operational
FieldWitness ships four presets at `deploy/config-presets/`. Select based on your operational
environment.
### Applying a Preset
```bash
$ cp deploy/config-presets/high-threat.json ~/.soosef/config.json
$ cp deploy/config-presets/high-threat.json ~/.fwmetadata/config.json
```
Restart the server to apply.
@@ -92,7 +92,7 @@ Restart the server to apply.
### Custom Configuration
Edit `~/.soosef/config.json` directly. All fields have defaults. Key fields for security:
Edit `~/.fwmetadata/config.json` directly. All fields have defaults. Key fields for security:
| Field | What It Controls |
|---|---|
@@ -104,7 +104,7 @@ Edit `~/.soosef/config.json` directly. All fields have defaults. Key fields for
| `deadman_grace_hours` | Grace period after missed check-in before auto-purge |
| `deadman_warning_webhook` | URL to POST a JSON warning during grace period |
| `cover_name` | CN for the self-signed TLS certificate (cover/duress mode) |
| `backup_reminder_days` | Days before `soosef status` warns about overdue backups |
| `backup_reminder_days` | Days before `fieldwitness status` warns about overdue backups |
> **Warning**: Setting `auth_enabled: false` disables all login requirements. Never
> do this on a network-accessible instance.
@@ -136,14 +136,14 @@ Share the URL over an already-secure channel only:
### What Happens When a Source Uploads
1. The source opens the URL in any browser (no account needed, no SooSeF branding)
1. The source opens the URL in any browser (no account needed, no FieldWitness branding)
2. Their browser computes SHA-256 hashes client-side before upload (SubtleCrypto)
3. Files are uploaded and processed:
- EXIF metadata is extracted (evidentiary fields: GPS, timestamp)
- All metadata is stripped from the stored copy (protects source device info)
- The original bytes are attested (signed) before stripping
4. The source receives a receipt code (HMAC of file hash + token)
5. Files are stored in `~/.soosef/temp/dropbox/` with mode 0700
5. Files are stored in `~/.fwmetadata/temp/dropbox/` with mode 0700
### Revoking Tokens
@@ -157,16 +157,16 @@ their receipt code. This returns the filename, SHA-256, and reception timestamp.
### Operational Security
- The upload page has no SooSeF branding -- it is a minimal HTML form
- The upload page has no FieldWitness branding -- it is a minimal HTML form
- No external resources are loaded (no CDN, fonts, analytics) -- Tor Browser compatible
- SooSeF does not log source IP addresses
- FieldWitness does not log source IP addresses
- If using a reverse proxy (nginx, Caddy), disable access logging for `/dropbox/upload/`
- Tokens auto-expire and are cleaned up on every admin page load
- For maximum source protection, run SooSeF as a Tor hidden service
- For maximum source protection, run FieldWitness as a Tor hidden service
### Storage Management
Uploaded files accumulate in `~/.soosef/temp/dropbox/`. Periodically review and process
Uploaded files accumulate in `~/.fwmetadata/temp/dropbox/`. Periodically review and process
submissions, then remove them from the temp directory. The files are not automatically
cleaned up (they persist until you act on them or the killswitch fires).
@@ -176,34 +176,34 @@ cleaned up (they persist until you act on them or the killswitch fires).
### Two Key Domains
SooSeF manages two independent key types:
FieldWitness manages two independent key types:
| Key | Algorithm | Location | Purpose |
|---|---|---|---|
| **Identity key** | Ed25519 | `~/.soosef/identity/` | Sign attestations, chain records |
| **Channel key** | AES-256-GCM (Argon2id-derived) | `~/.soosef/stegasoo/channel.key` | Steganographic encoding |
| **Identity key** | Ed25519 | `~/.fwmetadata/identity/` | Sign attestations, chain records |
| **Channel key** | AES-256-GCM (Argon2id-derived) | `~/.fwmetadata/stego/channel.key` | Steganographic encoding |
These are never merged. Rotating one does not affect the other.
### Key Rotation
**Identity rotation** archives the old keypair and generates a new one. If the chain is
enabled, a `soosef/key-rotation-v1` record is signed by the OLD key, creating a
enabled, a `fieldwitness/key-rotation-v1` record is signed by the OLD key, creating a
verifiable trust chain.
```bash
$ soosef keys rotate-identity
$ fieldwitness keys rotate-identity
```
After rotating, immediately:
1. Take a fresh backup (`soosef keys export`)
1. Take a fresh backup (`fieldwitness keys export`)
2. Notify all collaborators of the new fingerprint
3. Update trusted-key lists at partner organizations
**Channel rotation** archives the old key and generates a new one:
```bash
$ soosef keys rotate-channel
$ fieldwitness keys rotate-channel
```
After rotating, share the new channel key with all stego correspondents.
@@ -214,7 +214,7 @@ Import collaborator public keys so you can verify their attestations and accept
federation bundles:
```bash
$ soosef keys trust --import /media/usb/partner-pubkey.pem
$ fieldwitness keys trust --import /media/usb/partner-pubkey.pem
```
Always verify fingerprints out-of-band (in person or over a known-secure voice channel).
@@ -222,25 +222,25 @@ Always verify fingerprints out-of-band (in person or over a known-secure voice c
List trusted keys:
```bash
$ soosef keys show
$ fieldwitness keys show
```
Remove a trusted key:
```bash
$ soosef keys untrust <fingerprint>
$ fieldwitness keys untrust <fingerprint>
```
### Backup Schedule
SooSeF warns when backups are overdue (configurable via `backup_reminder_days`).
FieldWitness warns when backups are overdue (configurable via `backup_reminder_days`).
```bash
# Create encrypted backup
$ soosef keys export -o /media/usb/backup.enc
$ fieldwitness keys export -o /media/usb/backup.enc
# Check backup status
$ soosef status
$ fieldwitness status
```
Store backups on separate physical media, in a different location from the device.
@@ -249,7 +249,7 @@ Store backups on separate physical media, in a different location from the devic
## 5. Federation Setup
Federation allows multiple SooSeF instances to exchange attestation records.
Federation allows multiple FieldWitness instances to exchange attestation records.
### Adding Federation Peers
@@ -266,19 +266,19 @@ and Ed25519 fingerprint.
Before two organizations can federate, exchange public keys:
1. Export your public key: `cp ~/.soosef/identity/public.pem /media/usb/our-pubkey.pem`
1. Export your public key: `cp ~/.fwmetadata/identity/public.pem /media/usb/our-pubkey.pem`
2. Give it to the partner organization (physical handoff or secure channel)
3. Import their key: `soosef keys trust --import /media/usb/their-pubkey.pem`
3. Import their key: `fieldwitness keys trust --import /media/usb/their-pubkey.pem`
4. Verify fingerprints out-of-band
### Exporting Attestation Bundles
```bash
# Export all records
$ soosef chain export --output /media/usb/bundle.zip
$ fieldwitness chain export --output /media/usb/bundle.zip
# Export a specific range
$ soosef chain export --start 100 --end 200 --output /media/usb/bundle.zip
$ fieldwitness chain export --start 100 --end 200 --output /media/usb/bundle.zip
# Export filtered by investigation
# (investigation tag is set during attestation)
@@ -296,7 +296,7 @@ On the receiving instance, imported records are:
If the Tier 2 server and Tier 3 relay have network connectivity, gossip sync runs
automatically at the configured interval (default: 60 seconds, set via
`VERISOO_GOSSIP_INTERVAL` environment variable).
`FIELDWITNESS_GOSSIP_INTERVAL` environment variable).
Gossip flow:
1. Nodes exchange Merkle roots
@@ -326,7 +326,7 @@ No network connectivity is required at any point.
Verify the full chain periodically:
```bash
$ soosef chain verify
$ fieldwitness chain verify
```
This checks all hash linkage and Ed25519 signatures. It also verifies key rotation
@@ -338,10 +338,10 @@ Anchor the chain head to prove it existed before a given time:
```bash
# Automated (requires network)
$ soosef chain anchor --tsa https://freetsa.org/tsr
$ fieldwitness chain anchor --tsa https://freetsa.org/tsr
# Manual (prints hash for external submission)
$ soosef chain anchor
$ fieldwitness chain anchor
```
A single anchor implicitly timestamps every prior record (the chain is append-only).
@@ -358,7 +358,7 @@ For legal discovery or court orders, produce a proof showing specific records wh
keeping others redacted:
```bash
$ soosef chain disclose -i 42,43,44 -o disclosure.json
$ fieldwitness chain disclose -i 42,43,44 -o disclosure.json
```
The output includes full records for selected indices and hash-only entries for everything
@@ -370,7 +370,7 @@ else. A third party can verify the selected records are part of an unbroken chai
### Evidence Packages
For handing evidence to lawyers, courts, or organizations without SooSeF:
For handing evidence to lawyers, courts, or organizations without FieldWitness:
Self-contained ZIP containing original images, attestation records, chain data, your
public key, a standalone `verify.py`, and a README. The recipient verifies with:
@@ -413,7 +413,7 @@ Returns capabilities (stego-lsb, stego-dct, attest, fieldkit, chain).
### System Status
```bash
$ soosef status --json
$ fieldwitness status --json
```
Checks: identity key, channel key, chain integrity, dead man's switch state, backup
@@ -440,7 +440,7 @@ The Docker images include `HEALTHCHECK` directives that poll `/health` every 30
### Device Seizure (Imminent)
1. Trigger killswitch: `soosef fieldkit purge --confirm CONFIRM-PURGE`
1. Trigger killswitch: `fieldwitness fieldkit purge --confirm CONFIRM-PURGE`
2. For Tier 1 USB: pull the USB stick and destroy it physically if possible
3. Verify with a separate device that federation copies are intact
@@ -465,8 +465,8 @@ The Docker images include `HEALTHCHECK` directives that poll `/health` every 30
Data is gone. Restore from the most recent backup:
```bash
$ soosef init
$ soosef keys import -b /media/usb/backup.enc
$ fieldwitness init
$ fieldwitness keys import -b /media/usb/backup.enc
```
Federation copies of attestation data are unaffected. Local attestations created since
@@ -480,22 +480,22 @@ the last federation sync or backup are lost.
| Task | Frequency | Command |
|---|---|---|
| Check system status | Daily | `soosef status` |
| Check in (if deadman armed) | Per interval | `soosef fieldkit checkin` |
| Backup keys | Per `backup_reminder_days` | `soosef keys export` |
| Verify chain integrity | Weekly | `soosef chain verify` |
| Anchor chain | Weekly | `soosef chain anchor` |
| Check system status | Daily | `fieldwitness status` |
| Check in (if deadman armed) | Per interval | `fieldwitness fieldkit checkin` |
| Backup keys | Per `backup_reminder_days` | `fieldwitness keys export` |
| Verify chain integrity | Weekly | `fieldwitness chain verify` |
| Anchor chain | Weekly | `fieldwitness chain anchor` |
| Review drop box submissions | As needed | `/dropbox/admin` |
| Clean temp files | Monthly | Remove processed files from `~/.soosef/temp/` |
| Clean temp files | Monthly | Remove processed files from `~/.fwmetadata/temp/` |
| Create cold archive | Monthly | Export via CLI or web |
| Update SooSeF | As releases are available | `pip install --upgrade soosef` |
| Update FieldWitness | As releases are available | `pip install --upgrade fieldwitness` |
### Docker Volume Backup
```bash
$ docker compose stop server
$ docker run --rm -v server-data:/data -v /backup:/backup \
busybox tar czf /backup/soosef-$(date +%Y%m%d).tar.gz -C /data .
busybox tar czf /backup/fieldwitness-$(date +%Y%m%d).tar.gz -C /data .
$ docker compose start server
```
@@ -505,8 +505,8 @@ $ docker compose start server
periodically. The audit log is append-only; truncate by copying the tail:
```bash
$ tail -n 10000 ~/.soosef/audit.jsonl > ~/.soosef/audit.jsonl.tmp
$ mv ~/.soosef/audit.jsonl.tmp ~/.soosef/audit.jsonl
$ tail -n 10000 ~/.fwmetadata/audit.jsonl > ~/.fwmetadata/audit.jsonl.tmp
$ mv ~/.fwmetadata/audit.jsonl.tmp ~/.fwmetadata/audit.jsonl
```
> **Warning**: Truncating the audit log removes historical records. Archive the full

View File

@@ -1,7 +1,7 @@
# Administrator Quick Reference
**Audience**: IT staff and technical leads responsible for deploying and maintaining
SooSeF instances.
FieldWitness instances.
---
@@ -47,23 +47,23 @@ Exposes port 8001 (federation API only).
### Kubernetes
```bash
$ docker build -t soosef-server --target server -f deploy/docker/Dockerfile .
$ docker build -t soosef-relay --target relay -f deploy/docker/Dockerfile .
$ docker build -t fieldwitness-server --target server -f deploy/docker/Dockerfile .
$ docker build -t fieldwitness-relay --target relay -f deploy/docker/Dockerfile .
$ kubectl apply -f deploy/kubernetes/namespace.yaml
$ kubectl apply -f deploy/kubernetes/server-deployment.yaml
$ kubectl apply -f deploy/kubernetes/relay-deployment.yaml
```
Single-replica only. SooSeF uses SQLite -- do not scale horizontally.
Single-replica only. FieldWitness uses SQLite -- do not scale horizontally.
---
## Threat Level Presets
Copy the appropriate preset to configure SooSeF for the operational environment:
Copy the appropriate preset to configure FieldWitness for the operational environment:
```bash
$ cp deploy/config-presets/<level>-threat.json ~/.soosef/config.json
$ cp deploy/config-presets/<level>-threat.json ~/.fwmetadata/config.json
```
| Level | Session | Killswitch | Dead Man | Cover Name |
@@ -81,45 +81,45 @@ $ cp deploy/config-presets/<level>-threat.json ~/.soosef/config.json
| Command | Description |
|---|---|
| `soosef init` | Create directory structure, generate keys, write default config |
| `soosef serve --host 0.0.0.0` | Start web UI (LAN-accessible) |
| `soosef status` | Pre-flight check: keys, chain, deadman, backup, geofence |
| `soosef status --json` | Machine-readable status output |
| `fieldwitness init` | Create directory structure, generate keys, write default config |
| `fieldwitness serve --host 0.0.0.0` | Start web UI (LAN-accessible) |
| `fieldwitness status` | Pre-flight check: keys, chain, deadman, backup, geofence |
| `fieldwitness status --json` | Machine-readable status output |
### Keys
| Command | Description |
|---|---|
| `soosef keys show` | Display current key info and fingerprints |
| `soosef keys export -o backup.enc` | Export encrypted key bundle |
| `soosef keys import -b backup.enc` | Import key bundle from backup |
| `soosef keys rotate-identity` | Rotate Ed25519 identity (records in chain) |
| `soosef keys rotate-channel` | Rotate AES-256-GCM channel key |
| `soosef keys trust --import pubkey.pem` | Trust a collaborator's public key |
| `fieldwitness keys show` | Display current key info and fingerprints |
| `fieldwitness keys export -o backup.enc` | Export encrypted key bundle |
| `fieldwitness keys import -b backup.enc` | Import key bundle from backup |
| `fieldwitness keys rotate-identity` | Rotate Ed25519 identity (records in chain) |
| `fieldwitness keys rotate-channel` | Rotate AES-256-GCM channel key |
| `fieldwitness keys trust --import pubkey.pem` | Trust a collaborator's public key |
### Fieldkit
| Command | Description |
|---|---|
| `soosef fieldkit status` | Show fieldkit state (deadman, geofence, USB, tamper) |
| `soosef fieldkit checkin` | Reset dead man's switch timer |
| `soosef fieldkit check-deadman` | Check if deadman timer expired (for cron) |
| `soosef fieldkit purge --confirm CONFIRM-PURGE` | Activate killswitch |
| `soosef fieldkit geofence set --lat X --lon Y --radius M` | Set GPS boundary |
| `soosef fieldkit usb snapshot` | Record USB whitelist baseline |
| `soosef fieldkit tamper baseline` | Record file integrity baseline |
| `fieldwitness fieldkit status` | Show fieldkit state (deadman, geofence, USB, tamper) |
| `fieldwitness fieldkit checkin` | Reset dead man's switch timer |
| `fieldwitness fieldkit check-deadman` | Check if deadman timer expired (for cron) |
| `fieldwitness fieldkit purge --confirm CONFIRM-PURGE` | Activate killswitch |
| `fieldwitness fieldkit geofence set --lat X --lon Y --radius M` | Set GPS boundary |
| `fieldwitness fieldkit usb snapshot` | Record USB whitelist baseline |
| `fieldwitness fieldkit tamper baseline` | Record file integrity baseline |
### Chain and Evidence
| Command | Description |
|---|---|
| `soosef chain status` | Show chain head, length, integrity |
| `soosef chain verify` | Verify full chain (hashes + signatures) |
| `soosef chain log --count 20` | Show recent chain entries |
| `soosef chain export -o bundle.zip` | Export attestation bundle |
| `soosef chain disclose -i 5,12,47 -o disclosure.json` | Selective disclosure |
| `soosef chain anchor` | Manual anchor (prints hash for external witness) |
| `soosef chain anchor --tsa https://freetsa.org/tsr` | RFC 3161 automated anchor |
| `fieldwitness chain status` | Show chain head, length, integrity |
| `fieldwitness chain verify` | Verify full chain (hashes + signatures) |
| `fieldwitness chain log --count 20` | Show recent chain entries |
| `fieldwitness chain export -o bundle.zip` | Export attestation bundle |
| `fieldwitness chain disclose -i 5,12,47 -o disclosure.json` | Selective disclosure |
| `fieldwitness chain anchor` | Manual anchor (prints hash for external witness) |
| `fieldwitness chain anchor --tsa https://freetsa.org/tsr` | RFC 3161 automated anchor |
---
@@ -132,7 +132,7 @@ The web UI admin panel at `/admin` provides:
- Reset passwords (temporary password issued)
- View active sessions
User credentials are stored in SQLite at `~/.soosef/auth/soosef.db`.
User credentials are stored in SQLite at `~/.fwmetadata/auth/fieldwitness.db`.
---
@@ -140,9 +140,9 @@ User credentials are stored in SQLite at `~/.soosef/auth/soosef.db`.
| What | How often | Command |
|---|---|---|
| Key bundle | After every rotation, weekly minimum | `soosef keys export -o backup.enc` |
| Cold archive | Weekly or before travel | `soosef archive export --include-keys -o archive.zip` |
| Docker volume | Before updates | `docker compose stop server && docker run --rm -v server-data:/data -v /backup:/backup busybox tar czf /backup/soosef-$(date +%Y%m%d).tar.gz -C /data .` |
| Key bundle | After every rotation, weekly minimum | `fieldwitness keys export -o backup.enc` |
| Cold archive | Weekly or before travel | `fieldwitness archive export --include-keys -o archive.zip` |
| Docker volume | Before updates | `docker compose stop server && docker run --rm -v server-data:/data -v /backup:/backup busybox tar czf /backup/fieldwitness-$(date +%Y%m%d).tar.gz -C /data .` |
Store backups on separate physical media. Keep one copy offsite.
@@ -151,11 +151,11 @@ Store backups on separate physical media. Keep one copy offsite.
## Federation Setup
1. Exchange public keys between organizations (verify fingerprints out-of-band)
2. Import collaborator keys: `soosef keys trust --import /path/to/pubkey.pem`
2. Import collaborator keys: `fieldwitness keys trust --import /path/to/pubkey.pem`
3. Register peers via web UI at `/federation` or via CLI
4. Gossip starts automatically; monitor at `/federation`
For airgapped federation: `soosef chain export` to USB, carry to partner, import there.
For airgapped federation: `fieldwitness chain export` to USB, carry to partner, import there.
---
@@ -177,7 +177,7 @@ For airgapped federation: `soosef chain export` to USB, carry to partner, import
- [ ] Disable unnecessary services (bluetooth, avahi-daemon)
- [ ] Apply a threat level preset appropriate for the environment
- [ ] Set `cover_name` in config if operating under cover
- [ ] Set `SOOSEF_DATA_DIR` to an inconspicuous path if needed
- [ ] Set `FIELDWITNESS_DATA_DIR` to an inconspicuous path if needed
- [ ] Enable HTTPS (default) or place behind a reverse proxy with TLS
- [ ] Create systemd service for bare metal (see `docs/deployment.md` Section 7)
- [ ] Set up regular backups (key bundle + cold archive)
@@ -194,11 +194,11 @@ For airgapped federation: `soosef chain export` to USB, carry to partner, import
| Web UI unreachable from LAN | `host` must be `0.0.0.0`, not `127.0.0.1`. Check firewall. |
| Docker container exits | `docker compose logs server` -- check for port conflict or volume permissions |
| Dead man fires unexpectedly | Service crashed and exceeded interval+grace. Ensure `Restart=on-failure`. |
| Permission errors on `~/.soosef/` | Run SooSeF as the same user who ran `soosef init` |
| Permission errors on `~/.fwmetadata/` | Run FieldWitness as the same user who ran `fieldwitness init` |
| Drop box tokens expire immediately | System clock wrong. Run `date -u` and fix if needed. |
| Chain anchor TSA fails | Requires network. Use manual anchor on airgapped devices. |
| Account locked out | Wait for lockout to expire, or restart the server. |
| SSL cert shows wrong name | Delete `~/.soosef/certs/cert.pem`, set `cover_name`, restart. |
| SSL cert shows wrong name | Delete `~/.fwmetadata/certs/cert.pem`, set `cover_name`, restart. |
---
@@ -215,5 +215,5 @@ $ curl http://localhost:8000/health
$ curl http://localhost:8001/health
# Full system status
$ soosef status --json
$ fieldwitness status --json
```

View File

@@ -1,6 +1,6 @@
# Emergency Reference Card
**Audience**: All SooSeF users. Print, laminate, and carry in your wallet.
**Audience**: All FieldWitness users. Print, laminate, and carry in your wallet.
---
@@ -17,7 +17,7 @@ In the browser: **Fieldkit** > **Emergency Purge** > type `CONFIRM-PURGE` > clic
From a terminal:
```
soosef fieldkit purge --confirm CONFIRM-PURGE
fieldwitness fieldkit purge --confirm CONFIRM-PURGE
```
### Option 3: Hardware button (Raspberry Pi only)
@@ -51,7 +51,7 @@ If enabled, you must check in before the deadline or all data will be destroyed.
**Check in**: Browser > **Fieldkit** > **Check In**
Or: `soosef fieldkit checkin`
Or: `fieldwitness fieldkit checkin`
If you cannot check in, contact your editor. They may be able to disarm it remotely.

View File

@@ -1,22 +1,22 @@
# SooSeF Reporter Field Guide
# FieldWitness Reporter Field Guide
**Audience**: Reporters, field researchers, and documentarians using SooSeF to protect
**Audience**: Reporters, field researchers, and documentarians using FieldWitness to protect
and verify their work. No technical background required.
**Prerequisites**: A working SooSeF instance (Tier 1 USB or web UI access to a Tier 2
**Prerequisites**: A working FieldWitness instance (Tier 1 USB or web UI access to a Tier 2
server). Your IT admin should have set this up for you.
---
## What SooSeF Does For You
## What FieldWitness Does For You
SooSeF helps you do three things:
FieldWitness helps you do three things:
1. **Prove your photos and files are authentic** -- every photo you attest gets a
cryptographic signature that proves you took it, when, and that it has not been
tampered with since.
2. **Hide messages in images** -- send encrypted messages that look like ordinary photos.
3. **Destroy everything if compromised** -- if your device is about to be seized, SooSeF
3. **Destroy everything if compromised** -- if your device is about to be seized, FieldWitness
can erase all evidence of itself and your data in seconds.
---
@@ -37,7 +37,7 @@ permanent, tamper-evident record.
5. Add a location if relevant (optional)
6. Click **Attest**
SooSeF will:
FieldWitness will:
- Extract GPS coordinates and timestamp from the photo's EXIF data (for the provenance record)
- Strip device-identifying information (serial numbers, firmware version) from the stored copy
- Sign the photo with your Ed25519 identity key
@@ -46,7 +46,7 @@ SooSeF will:
**Through the CLI (if available):**
```bash
$ soosef attest IMAGE photo.jpg --caption "Market protest, central square"
$ fieldwitness attest IMAGE photo.jpg --caption "Market protest, central square"
```
> **Warning**: Attest the original, unedited photo. If you crop, filter, or resize
@@ -57,12 +57,12 @@ $ soosef attest IMAGE photo.jpg --caption "Market protest, central square"
If you have a folder of photos from a field visit:
```bash
$ soosef attest batch ./field-photos/ --caption "Site visit 2026-04-01"
$ fieldwitness attest batch ./field-photos/ --caption "Site visit 2026-04-01"
```
### Checking Your Status
Run `soosef status` or visit the web UI home page to see:
Run `fieldwitness status` or visit the web UI home page to see:
- Whether your identity key is set up
- How many attestations you have
- Whether your dead man's switch needs a check-in
@@ -92,7 +92,7 @@ message, passphrase, and PIN.
**CLI:**
```bash
$ soosef stego encode vacation.jpg -r shared_photo.jpg -m "Meeting moved to Thursday"
$ fieldwitness stego encode vacation.jpg -r shared_photo.jpg -m "Meeting moved to Thursday"
# Passphrase: (enter your passphrase, hidden)
# PIN: (enter your PIN, hidden)
```
@@ -101,22 +101,22 @@ The output is a normal-looking image file that contains your hidden message.
### Transport-Aware Encoding
If you are sending the image through a messaging app, tell SooSeF which platform. The
app will recompress images, so SooSeF needs to use a survival-resistant encoding:
If you are sending the image through a messaging app, tell FieldWitness which platform. The
app will recompress images, so FieldWitness needs to use a survival-resistant encoding:
```bash
$ soosef stego encode photo.jpg -r shared.jpg -m "Safe house confirmed" --transport whatsapp
$ soosef stego encode photo.jpg -r shared.jpg -m "Safe house confirmed" --transport signal
$ soosef stego encode photo.jpg -r shared.jpg -m "Safe house confirmed" --transport telegram
$ fieldwitness stego encode photo.jpg -r shared.jpg -m "Safe house confirmed" --transport whatsapp
$ fieldwitness stego encode photo.jpg -r shared.jpg -m "Safe house confirmed" --transport signal
$ fieldwitness stego encode photo.jpg -r shared.jpg -m "Safe house confirmed" --transport telegram
```
> **Warning**: Never reuse the same carrier image twice. SooSeF will warn you if you
> **Warning**: Never reuse the same carrier image twice. FieldWitness will warn you if you
> do. Comparing two versions of the same image trivially reveals steganographic changes.
### Decoding a Message
```bash
$ soosef stego decode received_image.jpg -r shared_photo.jpg
$ fieldwitness stego decode received_image.jpg -r shared_photo.jpg
# Passphrase: (same passphrase)
# PIN: (same PIN)
```
@@ -126,7 +126,7 @@ $ soosef stego decode received_image.jpg -r shared_photo.jpg
## Check-In (Dead Man's Switch)
If your admin has enabled the dead man's switch, you must check in regularly. If you miss
your check-in window, SooSeF assumes something has gone wrong and will eventually destroy
your check-in window, FieldWitness assumes something has gone wrong and will eventually destroy
all data to protect you.
**Check in through the web UI:** Visit the **Fieldkit** page and click **Check In**.
@@ -134,7 +134,7 @@ all data to protect you.
**Check in through the CLI:**
```bash
$ soosef fieldkit checkin
$ fieldwitness fieldkit checkin
```
> **Warning**: If you will be unable to check in (traveling without the device, planned
@@ -150,7 +150,7 @@ If your device is about to be seized or compromised:
**CLI:**
```bash
$ soosef fieldkit purge --confirm CONFIRM-PURGE
$ fieldwitness fieldkit purge --confirm CONFIRM-PURGE
```
**Web UI:** Visit the **Fieldkit** page and use the emergency purge button.
@@ -169,9 +169,9 @@ access.
4. All attestation records and chain data
5. Temporary files and audit logs
6. Configuration
7. System log entries mentioning SooSeF
8. Python bytecache and pip metadata (to hide that SooSeF was installed)
9. The SooSeF package itself
7. System log entries mentioning FieldWitness
8. Python bytecache and pip metadata (to hide that FieldWitness was installed)
9. The FieldWitness package itself
> **Warning**: This is irreversible. Make sure you have recent backups stored
> separately before relying on the killswitch. See "Backups" below.
@@ -180,25 +180,25 @@ access.
## Backups
Back up your keys regularly. SooSeF will remind you if your backup is overdue.
Back up your keys regularly. FieldWitness will remind you if your backup is overdue.
### Creating a Backup
```bash
$ soosef keys export -o /media/usb/soosef-backup.enc
$ fieldwitness keys export -o /media/usb/fieldwitness-backup.enc
```
You will be prompted for a passphrase. This creates an encrypted bundle containing your
identity key and channel key. Store the USB drive **in a different physical location**
from your SooSeF device.
from your FieldWitness device.
### Restoring From Backup
On a fresh SooSeF instance:
On a fresh FieldWitness instance:
```bash
$ soosef init
$ soosef keys import -b /media/usb/soosef-backup.enc
$ fieldwitness init
$ fieldwitness keys import -b /media/usb/fieldwitness-backup.enc
```
---
@@ -206,11 +206,11 @@ $ soosef keys import -b /media/usb/soosef-backup.enc
## Evidence Packages
When you need to hand evidence to a lawyer, a court, or a partner organization that does
not use SooSeF:
not use FieldWitness:
1. Go to the web UI or use the CLI to create an evidence package
2. Select the photos to include
3. SooSeF creates a ZIP file containing:
3. FieldWitness creates a ZIP file containing:
- Your original photos
- Attestation records with signatures
- The chain segment proving order and integrity
@@ -218,7 +218,7 @@ not use SooSeF:
- A standalone verification script
- A README with instructions
The recipient can verify the evidence using only Python -- they do not need SooSeF.
The recipient can verify the evidence using only Python -- they do not need FieldWitness.
---
@@ -238,7 +238,7 @@ encrypted with keys derived from the passphrase, PIN, and reference photo. If yo
any of the three, the message cannot be recovered.
**You need to share evidence with a court**: Use selective disclosure
(`soosef chain disclose`) to produce a proof that includes only the specific records
(`fieldwitness chain disclose`) to produce a proof that includes only the specific records
requested. The court can verify these records are part of an authentic, unbroken chain
without seeing your other work.
@@ -259,5 +259,5 @@ the killswitch fires automatically.
- **Back up regularly** and store backups in a separate physical location.
- **Lock the browser** or close it when you walk away. Session timeouts help, but do not
rely on them.
- **Do not discuss SooSeF by name** in environments where your communications may be
- **Do not discuss FieldWitness by name** in environments where your communications may be
monitored. If `cover_name` is configured, the tool presents itself under that name.

View File

@@ -1,6 +1,6 @@
# Reporter Quick-Start Card
**Audience**: Field reporters using a SooSeF Tier 1 bootable USB device.
**Audience**: Field reporters using a FieldWitness Tier 1 bootable USB device.
No technical background assumed.
**Print this page on a single sheet, laminate it, and keep it with the USB stick.**
@@ -14,7 +14,7 @@ No technical background assumed.
3. **Enter your passphrase** when the blue screen appears (this unlocks your data)
4. **Wait for the browser** to open automatically
You are now running SooSeF. The laptop's own hard drive is never touched.
You are now running FieldWitness. The laptop's own hard drive is never touched.
---
@@ -54,7 +54,7 @@ If your admin has enabled the dead man's switch, you must check in regularly.
Or from a terminal:
```
soosef fieldkit checkin
fieldwitness fieldkit checkin
```
If you miss your check-in window, the system will destroy all data after the grace period.
@@ -80,7 +80,7 @@ Everything is gone. Keys, photos, attestations, messages -- all destroyed.
1. **Close the browser**
2. **Pull the USB stick**
The laptop returns to its normal state. No trace of SooSeF remains.
The laptop returns to its normal state. No trace of FieldWitness remains.
---