Skip to main content

crates.io docs.rs CI OpenSSF Scorecard License

Overview

Implements C2PA manifest discovery over HTTP: the c2pa-manifest Link header, per RFC 8288, with a Tower middleware.

When an asset is served over HTTP and carries no embedded Manifest Store, a validator should look for a Link header carrying rel="c2pa-manifest". Its target is where the C2PA Manifest Store can be retrieved.

Link: <https://fabrikam.example/m.c2pa>; rel="c2pa-manifest"
[dependencies]
c2pa-http = "0.1"

This is the one discovery method that is not a file format, so it composes with all the others: a document embedding its manifest via c2pa-html can also advertise one over HTTP, and the specification gives the header precedence.

This crate owns two things:

  1. The header — parse and serialise it, with no dependencies, under any HTTP stack.
  2. The middleware — a Tower Layer that attaches it to every response.

Retrieving the manifest is left to the caller: the crate performs no network I/O, so it makes no decisions about timeouts, redirects, or trust.

Not certified or conformance-tested by the C2PA. It implements the discovery method as specified.

What it does

link parse and serialise the header; no dependencies, &str in and out
layer the Tower Layer that attaches it to every response

Retrieving the manifest is left to you: the crate performs no network I/O, so it makes no decisions about timeouts, redirects, or trust. Error::Inaccessible exists so a caller that does fetch can report manifest.inaccessible through the same type.

Serve it

use c2pa_http::ManifestLinkLayer;
use tower::ServiceBuilder;

let layer = ManifestLinkLayer::new("https://fabrikam.example/m.c2pa")?;
let service = ServiceBuilder::new().layer(layer);
# Ok::<(), c2pa_http::Error>(())

The header is appended, never set. A response may already carry Link fields for preload, canonical, or pagination hints; replacing them would break unrelated behaviour.

Read it

use c2pa_http::link;

let header = r#"</style.css>; rel=preload, <https://a.example/m.c2pa>; rel="c2pa-manifest""#;
let found = link::extract([header])?;
assert_eq!(found.uri, "https://a.example/m.c2pa");
# Ok::<(), c2pa_http::Error>(())

Embedded manifests, and the childlabel rule

A target may name a Manifest Store already embedded in the asset, via a JUMBF URI fragment. Referencing a specific manifest inside the store is not permitted, and a validator must ignore the childlabel portion — so it is discarded:

use c2pa_http::link;

let header = r#"<https://a.example/i.jpg#jumbf=c2pa/urn:uuid:1234>; rel="c2pa-manifest""#;
let found = link::extract([header])?;
assert!(found.is_embedded());
assert_eq!(found.uri, "https://a.example/i.jpg#jumbf=c2pa");
# Ok::<(), c2pa_http::Error>(())

Parsing that holds up

Commas separate link-values and semicolons separate parameters — but both are legal inside a <target> or a quoted string. A query string of ?ids=1,2,3 is ordinary, and splitting naively on those characters is the classic way to mis-parse this header. The scanner tracks both contexts.

Also handled: quoted and unquoted rel, case-insensitive matching, rel as a space-separated token list, several Link fields on one response, backslash escapes inside quoted parameters, and RFC 8288's rule that only the first rel parameter counts.

Competing targets are rejected rather than guessed at: the specification defines no precedence between two different c2pa-manifest links, so choosing one would be inventing a rule. Duplicate links naming the same target are fine.

Header injection is impossible, without rejecting anything

A raw CR, LF, space, or angle bracket cannot legally appear in a URI at all — RFC 3986 excludes them. So a string carrying one is not a URI to be rejected; it is a URI that has not been encoded yet. link::format percent-encodes it, which is both the spec-correct repair and what makes injection impossible:

use c2pa_http::link;

// A CR/LF payload lands inside the URI instead of starting a new header.
let header = link::format("https://a.example/\r\nX-Injected: yes")?;
assert!(header.contains("%0D%0A"));
assert!(!header.contains('\n'));
# Ok::<(), c2pa_http::Error>(())

> becomes %3E and can no longer close the target early; non-ASCII travels as percent-encoded UTF-8. Encoding is idempotent% is left untouched, so an already-encoded URI is not double-encoded into %2520 — and every delimiter a URI needs (? # / : @ & = + and the sub-delims) is preserved, so query strings and fragments survive intact.

When you would rather be told that your input needed repairing, use link::format_strict or ManifestLinkLayer::new_strict. For a target read from configuration that is usually the better choice: a stray space becomes a startup error instead of a silent %20 and a 404 at validation time.

Scope

This crate attaches and reads a header. It deliberately does not inspect request or response bodies to detect embedded provenance: that means buffering the whole body before forwarding it, which turns a streaming proxy into an unbounded memory sink and hands any client a denial of service. That belongs behind its own explicit opt-in with a mandatory size cap, not in the layer that writes a header.

Other languages

Python and JavaScript get the link parser — the Tower layer has no meaning off a Rust service stack, but emitting and reading the header is exactly what a web framework needs.

import c2pa_http
response["Link"] = c2pa_http.format("https://a.example/m.c2pa")
found = c2pa_http.extract([incoming.headers.get("link")])
import { format, extract } from "c2pa-http";
res.setHeader("Link", format("https://a.example/m.c2pa"));

Features

feature default adds
tower yes the Layer/Service (http, tower-layer, tower-service, pin-project-lite)
python no PyO3 bindings for the PyPI distribution

default-features = false leaves a dependency-free RFC 8288 parser usable under any HTTP stack — hyper, axum, a Cloudflare Worker, or a hand-rolled server.

Related Crates

Part of a family of single-purpose crates, one per C2PA embedding method. Each is standalone and independently versioned.

Crate Description
c2pa-structured-text Structured text: ASCII-armoured manifest in a comment or front matter
c2pa-unstructured-text Unstructured text: invisible Unicode variation-selector run
c2pa-html HTML: script and link elements in the document head
c2pa-text-binding Soft binding and content fingerprinting for text assets
c2pa-vtt WebVTT caption and subtitle embedding
c2pa-zip ZIP-based documents: EPUB, DOCX, ODT, OXPS
c2pa-warc WARC web archive embedding (ISO 28500)
c2pa-fonts OpenType/TrueType (SFNT) font embedding
c2pa-ml ML model containers: GGUF, SafeTensors, ONNX
c2pa Official C2PA SDK

Security

Found a vulnerability? Please report it privately — see SECURITY.md.

License

Licensed under either of Apache License, Version 2.0 or MIT License at your option.

Built by WritersLogic

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

c2pa_http-0.1.0.tar.gz (35.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

c2pa_http-0.1.0-cp39-abi3-win_amd64.whl (124.2 kB view details)

Uploaded CPython 3.9+Windows x86-64

c2pa_http-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (258.5 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

c2pa_http-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (253.3 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

c2pa_http-0.1.0-cp39-abi3-macosx_11_0_arm64.whl (228.2 kB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

c2pa_http-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl (230.5 kB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

File details

Details for the file c2pa_http-0.1.0.tar.gz.

File metadata

  • Download URL: c2pa_http-0.1.0.tar.gz
  • Upload date:
  • Size: 35.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for c2pa_http-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c108bf4ea5e6b172cc0c38f030a81bcf721d0939d0f07abd769694ec010c9670
MD5 4c6256287e45af9a074ecc302bd0019e
BLAKE2b-256 de079f711d3b0048c0c49b583d4dfdfe9b5273c5ec469faf324656aeb50d5693

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_http-0.1.0.tar.gz:

Publisher: release.yml on writerslogic/c2pa-http

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pa_http-0.1.0-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: c2pa_http-0.1.0-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 124.2 kB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for c2pa_http-0.1.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 a397078df0f10df56ec791112f3b6308ff09bbb01b80b161797ea65d1f35e2fa
MD5 172e702979be48ff60db03541fa012c7
BLAKE2b-256 6b41959cf5c9d3fe7e8eae4afc043590402ffe3cb8903f1c6540f6a85c058c53

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_http-0.1.0-cp39-abi3-win_amd64.whl:

Publisher: release.yml on writerslogic/c2pa-http

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pa_http-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for c2pa_http-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 239fdcdf015069e7cfe944fd259c13d8f508e793230af4253cbd1bab0d4a404e
MD5 c9b28cc79c935df3b09c72ac213e2bda
BLAKE2b-256 fa4ae213c7ef84633b0914ce5bb7cce85a5db2efc86ee5ed29d17e74aa582d34

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_http-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on writerslogic/c2pa-http

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pa_http-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for c2pa_http-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 b81c97b3201f2edb821dca6c4d3f39f8c1ba9e89b7ca53505db7c43611f7b34f
MD5 62450ea76fb8dac350e01f33dfe21555
BLAKE2b-256 74357702ec4ca45c06a553915817dbe75df7633cc5dbbce358d648e38a1dbf26

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_http-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on writerslogic/c2pa-http

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pa_http-0.1.0-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for c2pa_http-0.1.0-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 23d9fe88340f05e04563f92c009aa276d7dc6151a04621b87982d5f889366912
MD5 c506214db32178161805692741344351
BLAKE2b-256 21830f6f1b3b115c27924e1dc1be7149d2bd30cc9b893390fb530a9b27990e4b

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_http-0.1.0-cp39-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on writerslogic/c2pa-http

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pa_http-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for c2pa_http-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 550ee7d3de8c6526125b5e5663b1e7be0af0e08bc4ead64e1f9acc812bcbebaa
MD5 dba1d331aa70023ceb9d4860dacc7623
BLAKE2b-256 03ef3ad32e472512a59c33a6e5fc518a7a521efe97e32b00f83ede4ac4d754ac

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_http-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on writerslogic/c2pa-http

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

6 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page