Skip to main content

Sidecar Format

The sidecar file contains both layers of cryptographic proof in a single JSON document that travels alongside the media file.

Overview

A sidecar file (e.g., photo.sidecar.json) accompanies each media file (e.g., photo.jpg). This separation keeps the original media untouched while providing all verification data.

Why JSON?

The proof format is JSON for three reasons:

  • Human-readable — Developers can inspect proofs without special tools
  • Flexible storage — Store as a file, in a database, on a blockchain, or transmit via API
  • Easy integration — Every platform has JSON libraries

Structure

{
"version": "1.0",
"capture_trust": {
"jwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ii4uLiJ9..."
},
"media_integrity": {
"content_hash": "a1b2c3d4e5f6...",
"signature": "MEUCIQC...",
"public_key": "BHx5y...",
"capture_id": "550e8400-e29b-41d4-a716-446655440000",
"captured_at": "2025-01-15T10:30:00Z"
}
}

Fields

Root

FieldTypeDescription
versionstringSchema version (currently "1.0")
capture_trustobjectServer-issued trust token
media_integrityobjectDevice-generated integrity proof

capture_trust

FieldTypeDescription
jwtstringES256-signed JWT from the SignedShot API

media_integrity

FieldTypeDescription
content_hashstringSHA-256 hash of the media file (hex, 64 characters)
signaturestringECDSA signature over the signed message (base64)
public_keystringDevice's public key (base64, uncompressed EC point, 65 bytes)
capture_idstringUUID of the capture session (must match JWT)
captured_atstringISO8601 UTC timestamp of capture

JWT Payload

The capture_trust.jwt is a standard JWT. When decoded, it contains:

{
"iss": "https://api.signedshot.io",
"aud": "signedshot",
"sub": "capture-service",
"iat": 1705312200,
"capture_id": "550e8400-e29b-41d4-a716-446655440000",
"publisher_id": "9a5b1062-a8fe-4871-bdc1-fe54e96cbf1c",
"device_id": "ea5c9bfe-6bbc-4ee2-b82d-0bcfcc185ef1",
"attestation": {
"method": "app_check",
"app_id": "io.signedshot.capture"
}
}

JWT Fields

FieldTypeDescription
issstringIssuer (API URL)
audstringAudience
substringSubject (always "capture-service")
iatnumberIssued at (Unix timestamp)
capture_idstringUnique capture session ID
publisher_idstringPublisher UUID
device_idstringDevice UUID
attestationobjectAttestation details

attestation Object

FieldTypeDescription
methodstring"sandbox", "app_check", or "app_attest"
app_idstringApp bundle ID (only present when attested)

Signature Format

Media Integrity Signature

The media_integrity.signature signs a message constructed from:

{content_hash}:{capture_id}:{captured_at}

For example:

a1b2c3d4e5f6...:550e8400-e29b-41d4-a716-446655440000:2025-01-15T10:30:00Z

The signature is:

  • Algorithm: ECDSA with P-256 curve
  • Generated by the device's Secure Enclave
  • Encoded as base64

JWT Signature

The JWT uses:

  • Algorithm: ES256 (ECDSA with P-256)
  • Key ID (kid) in header for JWKS lookup
  • Verification via /.well-known/jwks.json

File Naming Convention

Media FileSidecar File
photo.jpgphoto.sidecar.json
video.mp4video.sidecar.json
IMG_1234.HEICIMG_1234.sidecar.json

Example: Complete Sidecar

{
"version": "1.0",
"capture_trust": {
"jwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleS0xIn0.eyJpc3MiOiJodHRwczovL2FwaS5zaWduZWRzaG90LmlvIiwiYXVkIjoic2lnbmVkc2hvdCIsInN1YiI6ImNhcHR1cmUtc2VydmljZSIsImlhdCI6MTcwNTMxMjIwMCwiY2FwdHVyZV9pZCI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMCIsInB1Ymxpc2hlcl9pZCI6IjlhNWIxMDYyLWE4ZmUtNDg3MS1iZGMxLWZlNTRlOTZjYmYxYyIsImRldmljZV9pZCI6ImVhNWM5YmZlLTZiYmMtNGVlMi1iODJkLTBiY2ZjYzE4NWVmMSIsImF0dGVzdGF0aW9uIjp7Im1ldGhvZCI6ImFwcF9jaGVjayIsImFwcF9pZCI6ImlvLnNpZ25lZHNob3QuY2FwdHVyZSJ9fQ.signature"
},
"media_integrity": {
"content_hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"signature": "MEUCIQDKZokqnCjrRtw+3S0P2mjJH+E8zRqgaG6R4bG6V7oONwIgF3lQsGV1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3M=",
"public_key": "BHx5yK3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3K1K3M=",
"capture_id": "550e8400-e29b-41d4-a716-446655440000",
"captured_at": "2025-01-15T10:30:00Z"
}
}

Verification Steps

To verify a sidecar:

  1. Parse the sidecar JSON
  2. Verify Capture Trust (JWT)
    • Fetch JWKS from issuer's /.well-known/jwks.json
    • Find key by kid in JWT header
    • Verify ES256 signature
  3. Verify Media Integrity
    • Compute SHA-256 of media file
    • Compare with content_hash
    • Reconstruct signed message: {hash}:{capture_id}:{captured_at}
    • Verify ECDSA signature using public_key
  4. Cross-validate
    • Confirm capture_id matches in both JWT and media_integrity

Next Steps