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>
8.8 KiB
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 historyc2pa.hash.data-- hard binding (byte-range)c2pa.location.broad-- city/region locationc2pa.exif-- EXIF metadatac2pa.creative.work-- title, description, authorshipc2pa.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-rsvia 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_signertakes a callback, algorithm, certs PEM, optional timestamp URLtimestamp_url=Noneskips 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:
-
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.
-
Pseudonym cert: Subject is pseudonym or random UUID. Valid C2PA but unrecognized by external trust anchors.
-
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.pyinfrastructure. - Entropy witnesses embedded as vendor assertions provide soft timestamp evidence.
- Evidence packages include org cert PEM alongside C2PA manifest for offline verification.
c2pa-pythonavailability gated behindhas_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.pyreads fromattest/models.py,federation/chain.py,keystore/manager.py; callscert.pyandvendor_assertions.pyimporter.pyreads image bytes, writesAttestationRecordviaattest/attestation.py, parses vendor assertions
Web UI
New routes in the attest.py blueprint:
GET /attest/<record_id>/c2pa-- download C2PA-embedded imagePOST /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
c2pa = ["c2pa-python>=0.6.0", "fieldwitness[attest]"]
Implementation Phases
Phase 0 -- Prerequisites (~1h)
has_c2pa()in_availability.pyc2paextra inpyproject.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.pyimport_c2pa()reads C2PA manifest, producesAttestationRecord- Maps C2PA fields to FieldWitness model
- Returns
C2PAImportResultwithtrust_status - Creates new FieldWitness attestation record over imported data
Phase 4 -- CLI integration (~4h)
fieldwitness c2pa export/verify/import/showsubcommands- 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
c2paconfig block to each preset (export_enabled,privacy_level,include_precise_gps,timestamp_url) C2PAConfigsub-dataclass inFieldWitnessConfig
MVP scope
Phases 0-2 (~10h): Produces C2PA-compatible images viewable in Adobe Content Credentials and any C2PA verifier.
Key Decisions (Before Coding)
- Use existing Ed25519 identity key for cert (not a separate key) -- preserves single-key-domain design.
- Cert stored at
~/.fwmetadata/identity/c2pa_cert.pem, regenerated on key rotation. - Tier 1 ARM fallback: Tier 1 produces FieldWitness records; Tier 2 generates C2PA export on their behalf.
- Pin
c2pa-python>=0.6.0, add shim layer for API stability. - Hard binding computed by
c2pa-pythonBuilder 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)