Custom profiles

Create trust profiles tailored to your exact requirements. Select specific checks, write custom expressions, and integrate with your existing systems.

Creating custom profiles

Start with the "custom" template (no checks enabled) and add exactly the checks you need:

from jpegtrust import create_profile, save_profile

# Pick only the checks relevant to your workflow
profile = create_profile(
    check_ids=["signature_chain", "pixel_hash", "copyright"],
    name="Content Licensing Policy",
    issuer="My Stock Photo Platform",
)
save_profile(profile, "licensing.yml")

Writing custom expressions

Profile expressions use json-formula syntax to query the indicator metadata. You can write your own checks beyond the predefined ones by editing the YAML directly.

Expression syntax

Pattern Example
Dot access manifests[0].status.signature
Quoted keys (with dots/colons) manifests[0].assertions.'cawg.metadata'
Array indexing manifests[0]
Equality claim.alg == "sha256"
Not equal / null check field != null
Boolean logic expr1 && expr2 || expr3
contains() contains(keys(obj), "key")
keys() keys(manifests[0].assertions)
[?filter] manifests[?assertions.'jpt.rights' != null]
length() length(manifests[?assertions.'cawg.identity' != null]) > 0
< <= > >= manifests[0].assertions.'cawg.metadata'.'exif:GPSLatitude' > 50
[-1] manifests[-1]

Custom check example

Add a custom check to a generated profile by editing the YAML:

# Add after the generated checks in your profile YAML
# declaration: the Trust Declaration made when the asset was created
# manifests[0]: the active manifest (ISO/DIS 21617-1 A.4.1)

- id: specific_camera_app
  description: "Image must come from our approved camera app"
  expression: >
    declaration.'claim.v2'.claim_generator_info[0].name == "Approved Camera v2"
  report_text:
    'true':
      en: "Image captured with approved camera app"
    'false':
      en: "Image was NOT captured with the approved camera app"

- id: newcastle_location
  description: "Photo taken in Newcastle area"
  expression: >
    manifests[0].assertions.'cawg.metadata'.'photoshop:City' == "Newcastle upon Tyne"
  report_text:
    'true':
      en: "Location confirmed: Newcastle area"
    'false':
      en: "Location does not match Newcastle"

Extending with custom workflows

The jpegtrust package integrates into any Python workflow. Here are common patterns:

CI pipeline integration

# In your CI/CD pipeline script
import sys
from jpegtrust import batch_verify

result = batch_verify("./uploads", "policy.yml")
if result["summary"]["failed"] > 0:
    print("FAILED: Some images do not meet trust policy")
    sys.exit(1)
print("All images passed trust verification")

Web API wrapper

# FastAPI example
from fastapi import FastAPI, UploadFile
from jpegtrust import extract_from_file, verify
import tempfile

app = FastAPI()

@app.post("/verify")
async def verify_upload(image: UploadFile):
    with tempfile.NamedTemporaryFile(suffix=".jpg") as tmp:
        tmp.write(await image.read())
        tmp.flush()
        indicator = extract_from_file(tmp.name)

    if not indicator:
        return {"error": "No metadata found"}

    profile_yaml = open("policy.yml").read()
    report = verify(indicator, profile_yaml)
    return report.to_dict()

Automated ingest pipeline

import os
from jpegtrust import extract_from_file, verify, load_profile

profile_yaml = load_profile("newsroom.yml")
watch_dir = "/incoming/photos"

for fname in os.listdir(watch_dir):
    if not fname.endswith(".jpg"):
        continue
    path = os.path.join(watch_dir, fname)
    indicator = extract_from_file(path)
    if indicator is None:
        print(f"SKIP {fname}: no metadata")
        continue
    report = verify(indicator, profile_yaml)
    if report.status == "pass":
        os.rename(path, f"/approved/{fname}")
    else:
        os.rename(path, f"/review/{fname}")
        print(f"REVIEW {fname}: {report.verdict} ({report.score})")

Environment variables and configuration

The jpegtrust package is stateless -- all configuration is passed explicitly through function arguments and profile YAML files. This makes it easy to integrate into any environment without hidden state.

# Common pattern: load profile path from environment
import os
from jpegtrust import verify_image

profile_path = os.environ.get("TRUST_PROFILE", "default_policy.yml")
report = verify_image("photo.jpg", profile_path)