Overview
The badge signer is a Python service that generates verifieddit badges with embedded C2PA Content Credentials. It uses c2patool for signing, Pillow for image generation, and qrcode for QR code embedding.
Repository: Sanmarcsoft/verifieddit-www (path: services/badge-signer/)
Components
- server.py – HTTP server handling badge signing requests
- badge_png_generator.py – Generates badge PNG images with QR codes
- badge-manifest-template.json – C2PA manifest template for badge signing
- c2patool – CAI c2patool binary (v0.9.12, fetched in Nix build)
Certificates
The badge signer requires PKI certificates for C2PA signing:
| File | Container Path | Description |
|---|---|---|
| Signing key | /certs/signer-key.pem | Private key for C2PA signing |
| Certificate chain | /certs/chain.pem | Full certificate chain (signer + intermediate + root) |
Certificate Source
Certificates are managed by the Trusteddit PKI system:
- Repository:
Sanmarcsoft/trusteddit-pki - PKI Services:
Sanmarcsoft/trusteddit-pki-services - Certificate chain follows C2PA 2.2 trust model
- Root CA is the SanMarcSoft/Trusteddit root
Certificate Rotation
- Generate new certificates via the Trusteddit PKI
- Update the certificate files in the deployment
- Re-deploy the badge signer
- Verify signing works with the new certificates
| |
Nix Build
The Nix flake:
- Targets
x86_64-linux - Uses
python312.withPackagesfor Python dependencies (pillow, qrcode) - Fetches
c2patoolbinary from GitHub releases (withautoPatchelfHookfor dynamic linking) - Includes DejaVu fonts for badge text rendering
| |
c2patool Binary
The c2patool binary is fetched as a Nix derivation:
| |
autoPatchelfHook automatically patches the ELF binary to use Nix store paths for shared libraries (libssl, libstdc++).
Environment Variables
| Variable | Container Value | Description |
|---|---|---|
C2PATOOL | ${c2patool}/bin/c2patool | Path to c2patool binary (Nix store path) |
MANIFEST | ${appFiles}/app/badge-manifest-template.json | Path to manifest template |
PRIVATE_KEY | /certs/signer-key.pem | Path to signing private key |
SIGN_CERT | /certs/chain.pem | Path to certificate chain |
PYTHONUNBUFFERED | 1 | Disable Python output buffering |
Deployment
| |
Three Pillars of Provenance
The badge signer implements Pillar 1: Hard Binding (C2PA JUMBF) of the Three Pillars of Provenance architecture:
- Pillar 1 (implemented): Hard Binding – C2PA JUMBF embedded in image
- Pillar 2 (planned): Soft Binding – TrustMark invisible watermark
- Pillar 3 (planned): Perceptual Fingerprinting – pHash + dHash
The Manifest Store service (trusteddit-pki-services/manifest-store/) provides the API for Pillars 2 and 3.
Troubleshooting
- c2patool “not found”: The binary path is set via the
C2PATOOLenvironment variable to the Nix store path. If missing, the entrypoint script or container config is wrong. - Certificate errors: Check that
/certs/signer-key.pemand/certs/chain.pemare mounted/available. Verify certificate validity withopenssl x509 -in /certs/chain.pem -text -noout. - Font rendering issues: DejaVu fonts must be included in the image. The Nix flake includes
pkgs.dejavu_fonts. - Image too large for signing: Very large images may exhaust memory. Check container memory limits.