
C2PA AWS Integration: Lambda and S3 Signing Pipeline
Definition: The Original Pictures AWS integration uses an S3 event to trigger a Lambda that signs each uploaded object through the API and writes the signed asset to a destination bucket.
TL;DR: Drop a file into an S3 bucket, an event triggers a Lambda, the Lambda calls the signing API, and the signed asset lands in a signed bucket. It is the standard event-driven pattern, applied to provenance.
Event-driven signing
An S3 ObjectCreated event invokes a Lambda for each new object. The Lambda fetches the object, calls the signing API, and writes the signed result to a destination bucket. No polling, no servers to manage, and it scales with upload volume.
Why enterprises use it
Large platforms already run on S3 and Lambda. Slotting signing into that pipeline means provenance is added with infrastructure the team already operates, and IAM roles keep the API key out of application code via Secrets Manager.
Throughput and cost
Lambda concurrency scales to spikes, and signing runs as asynchronous jobs, so high-volume buckets queue work instead of holding connections open. A webhook tells you when each signed asset is ready to copy to the destination bucket.
Implementation
import hashlib, json, os, uuid import boto3, urllib3 API = "https://dev-api.originalpictures.com" http = urllib3.PoolManager() s3 = boto3.client("s3") AUTH = {"Authorization": f"Bearer {os.environ['OP_API_KEY']}"} # resolved from Secrets Manager def api(method, path, body=None, idempotent=False): headers = {**AUTH, "Content-Type": "application/json"} if idempotent: headers["Idempotency-Key"] = str(uuid.uuid4()) r = http.request(method, API + path, headers=headers, body=None if body is None else json.dumps(body)) if r.status >= 300: raise RuntimeError(f"{path}: {r.status}") return json.loads(r.data) def handler(event, _ctx): """S3 ObjectCreated: upload the exact bytes, complete, and accept a signing job.""" for rec in event["Records"]: b, k = rec["s3"]["bucket"]["name"], rec["s3"]["object"]["key"] data = s3.get_object(Bucket=b, Key=k)["Body"].read() up = api("POST", "/v1/uploads", { "byte_length": len(data), "sha256": hashlib.sha256(data).hexdigest(), "declared_media_type": "image/jpeg", "mode": "single", "filename": k}) # Storage URL: send exactly the issued headers, never the API key. http.request("PUT", up["upload"]["url"], body=data, headers=up["upload"]["headers"]) api("POST", f"/v1/uploads/{up['upload_id']}/complete", {}, idempotent=True) api("POST", "/v1/sign", {"asset_id": up["asset_id"], "recipe_id": "rcp_system_watermark_then_sign"}, idempotent=True) def on_published(asset_id, key, bucket): """Called from your webhook receiver once the job completes: store the signed bytes.""" dl = api("POST", f"/v1/assets/{asset_id}/download") signed = http.request(dl["method"], dl["url"], headers=dl["headers"]).data s3.put_object(Bucket=f"{bucket}-signed", Key=key, Body=signed)
FAQ
Where does the API key live?
In AWS Secrets Manager, read by the Lambda execution role. It never sits in code or environment files in the repo.
Where Original Pictures stands today
Original Pictures ships three things today: a Sign API, a Verify API, and the SDKs that wrap them (not yet published to npm or PyPI). One POST /v1/sign with the watermark-then-sign recipe attaches a C2PA-format manifest and an invisible TrustMark watermark, with RFC 3161 timestamp evidence recorded alongside the signature. Any C2PA-aware validator can read the manifest without calling us.
Two things are on the near roadmap, and we name them as roadmap, not as shipped: C2PA Conformance Program recognition (target Q3 2026, until then our manifests use the published C2PA v2.2 format and any C2PA-aware validator can read them, but third-party validators will show our signer as not-yet-listed), and a consumer capture app (Q3 2026). We do not sell a capture SDK, and we do not claim Trust-List membership we do not yet hold.
Bottom line: Use an S3 event to trigger a Lambda that signs each object, adding provenance with the event-driven infrastructure your team already runs.
Related
Original Pictures is progressing through the C2PA Conformance Program; our signing certificate is not yet on the official C2PA Trust List. Target: Q3 2026. We will not describe ourselves as "C2PA-certified" until it is true.
Original Pictures provides content-provenance infrastructure. It does not by itself constitute legal compliance with the EU AI Act or any other regime; compliance depends on how you deploy it, your disclosures, and your governance. Figures are drawn from public reporting, verify against primary sources before citing in regulated materials. Nothing here is legal advice.
Last verified 2026-05-25. Author: Mahdi Kazempour, Founder, Original Pictures.