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:
@@ -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.
|
||||
|
||||
@@ -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")` |
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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. |
|
||||
|
||||
240
docs/planning/c2pa-integration.md
Normal file
240
docs/planning/c2pa-integration.md
Normal 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)
|
||||
214
docs/planning/gtm-feasibility.md
Normal file
214
docs/planning/gtm-feasibility.md
Normal 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)
|
||||
480
docs/security/threat-model.md
Normal file
480
docs/security/threat-model.md
Normal 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.
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user