An API-based image viewer web application that allows users to verify C2PA content credentials as well as view EXIF and IPTC information embedded within the image.
- Displays the main image with a clean white background and subtle shadow effect
- Shows verification status badges:
- Content Credentials: Green "Authenticity Verified" badge for C2PA-verified images
- Digital Source Type: Shows image origin (e.g., "Digital Camera (RAW)", "Generative AI")
- Responsive design that works on all screen sizes
- Extracts EXIF and IPTC metadata from images
- Displays photography information (camera make, model, lens, exposure settings)
- Shows detailed metadata sections:
- Camera & Lens (make, model, serial numbers)
- Exposure Settings (aperture, shutter speed, ISO, focal length)
- Image Details (format, resolution, dimensions)
- Photographer Information (artist, capture dates)
- Extracts and displays C2PA thumbnails (claim and ingredient images)
- Shows provenance history with timestamps and detailed edit actions
- Displays specific adjustment values (e.g., "Blacks: -5", "Clarity: +20")
- Digital Source Type Detection:
- Detects camera capture (DNG, RAW files → "Digital Camera (RAW)")
- Detects AI-generated content (Adobe Firefly, DALL-E, Midjourney → "Generative AI")
- Shows orange badge for AI-generated, green badge for camera capture
- RESTful API for metadata extraction
- Accepts image URIs as input
- Handles both local files and remote URLs
- CORS-enabled for cross-origin requests
| Endpoint | Method | Description | Performance |
|---|---|---|---|
/api/exif_metadata |
GET | EXIF, IPTC, GPS metadata only | Fast (~10-50ms) |
/api/c2pa_metadata |
GET | C2PA provenance, thumbnails, digital source type | Slower (~100-500ms+) |
/api/c2pa_mini |
GET | Minimal C2PA for quick verification | Fast (~50-200ms) |
/api/upload |
POST | Upload image, returns all metadata | Variable |
Retrieve EXIF, IPTC, and GPS metadata for an image (no C2PA cryptographic verification).
Query Parameters:
uri(required): Image file path or URL
Example:
GET /api/exif_metadata?uri=https://example.com/image.jpg
Response:
{
"image.jpg": {
"filename": "image.jpg",
"format": "JPEG",
"width": 4000,
"height": 3000,
"file_size_bytes": 1234567,
"file_size_mb": 1.18,
"photography": {
"camera_make": "Canon",
"camera_model": "EOS R5",
"lens_model": "RF 24-70mm f/2.8L",
"aperture": "f/2.8",
"shutter_speed": "1/250s",
"iso": "400",
"focal_length": "50mm"
},
"exif": { "Make": "Canon", "Model": "EOS R5", ... },
"gps": { "latitude": "40.7128", "longitude": "-74.0060" },
"iptc": { "title": "Sunset", "description": "A beautiful sunset", ... }
}
}Retrieve C2PA provenance data, including signature information, edit actions, embedded thumbnails, and digital source type.
Query Parameters:
uri(required): Image file path or URL
Example:
GET /api/c2pa_metadata?uri=https://example.com/image.jpg
Response:
{
"provenance": [
{ "name": "Claim Generator", "generator": "Lightroom Classic 15.1" },
{ "name": "Issued By", "issuer": "Adobe Inc." },
{ "name": "Issued On", "date": "Jan 08, 2026 at 04:21 PM IST" },
],
"c2pa_data": { "basic_info": {...}, "signature_info": {...}, "assertions": [...] },
"thumbnails": {
"claim_thumbnail": "base64_encoded_data...",
"ingredient_thumbnail": "base64_encoded_data..."
},
"digital_source_type": {
"code": "digitalCapture",
"label": "Digital Camera (RAW)"
}
}Retrieve minimal C2PA credentials for quick trust verification (e.g., hover previews). Optimized for speed with 5-minute response caching.
Query Parameters:
uri(required): Image file path or URL
Example:
GET /api/c2pa_mini?uri=https://example.com/image.jpg
Response:
{
"creator": "John Doe",
"issued_by": "Adobe Inc.",
"issued_on": "Jan 08, 2026 at 04:21 PM IST",
"status": "Authenticity Verified",
"digital_source_type": "Digital Camera (RAW)",
"more": "https://apps.thecontrarian.in/c2pa/?uri=..."
}Upload an image file and receive complete metadata (EXIF + C2PA) plus the image as a base64 data URL.
Request: multipart/form-data with file field named file
Example:
POST /api/upload
Content-Type: multipart/form-data
file: [image file]
Response:
{
"filename.jpg": {
"filename": "filename.jpg",
"format": "JPEG",
"width": 4000,
"height": 3000,
"photography": {...},
"exif": {...},
"gps": {...},
"iptc": {...},
"provenance": [...],
"thumbnails": {...},
"digital_source_type": { "code": "digitalCapture", "label": "Digital Camera (RAW)" },
"image_data": "data:image/jpeg;base64,..."
}
}| Endpoint | Method | Description |
|---|---|---|
/ |
GET | Serve the main HTML page |
/styles.css |
GET | Serve CSS stylesheet |
/script.js |
GET | Serve JavaScript file |
/content_credentials_logo.svg |
GET | Serve Content Credentials logo |
/{filename} |
GET | Serve image files (jpg, jpeg, png, gif) |
- Python 3.12 or higher
- UV package manager (install)
-
Clone the repository
git clone https://github.com/thecont1/c2pa-viewer.git cd c2pa-viewer -
Install dependencies using UV:
uv sync
Start the server:
uv run uvicorn server:app --host 0.0.0.0 --port 8080The server will start on http://localhost:8080.
Run the test script (requires server to be running):
# Test with default remote image
uv run python test_server.py
# Test with a local file
uv run python test_server.py --uri /path/to/image.jpg --skip-upload
# Test with a URL
uv run python test_server.py --uri https://example.com/image.jpg
# Test upload endpoint
uv run python test_server.py --uri /path/to/image.jpgAccess the application:
- Frontend:
http://localhost:8080/ - With image:
http://localhost:8080/?uri=https://example.com/image.jpg
Example: http://localhost:8080/?uri=https://thecontrarian.in/library/originals/GHANA/DSCF9243.jpg
- Backend: Python 3.12, FastAPI, c2pa-python
- Frontend: HTML5, CSS3, JavaScript
- Metadata Extraction:
- c2pa-python (official C2PA SDK)
- pillow (EXIF and image processing)
- Server: Uvicorn (ASGI server)
| File | Description |
|---|---|
index.html |
Main HTML file for the application |
server.py |
FastAPI server with all API endpoints |
styles.css |
CSS styling |
script.js |
Frontend JavaScript for rendering metadata |
test_server.py |
API endpoint tests |
The API separates EXIF extraction from C2PA verification for performance:
- EXIF/IPTC extraction is fast (simple binary parsing, ~10-50ms)
- C2PA verification is slower (cryptographic signature verification, ~100-500ms+)
This separation allows clients to request only the data they need.
The system detects image origin from C2PA data:
- Checks
claim_generatorfor AI tools (Firefly, DALL-E, Midjourney, etc.) - Checks actions for AI-related operations (
text_to_image, generative fills) - Checks ingredient formats for camera types (DNG, RAW → "Digital Camera (RAW)")
- Falls back to "Digital Camera" for standard C2PA images
- Modern async/await support
- Built-in data validation with Pydantic
- Automatic API documentation (OpenAPI/Swagger)
- Better type safety and IDE support
- Higher performance
- Official C2PA SDK with proper API
- More reliable metadata extraction
- Better thumbnail extraction
- No external process dependencies
- Direct access to C2PA manifest structure
c2pa-viewer/
├── index.html # Main HTML file
├── styles.css # Stylesheet
├── script.js # Frontend JavaScript
├── server.py # FastAPI server with all API endpoints
├── test_server.py # API endpoint tests
├── pyproject.toml # Project dependencies (UV)
├── uv.lock # Dependency lock file
├── Dockerfile # Docker configuration
├── fly.toml # Fly.io deployment config
└── README.md # This file
This project is licensed under the MIT License.