c2pa-ml 0.1.0

C2PA manifest embedding for AI/ML model container formats: GGUF, SafeTensors, and ONNX
Documentation
  • Coverage
  • 98.15%
    53 out of 54 items documented1 out of 18 items with examples
  • Size
  • Source code size: 106.33 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 931.29 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 3s Average build duration of successful builds.
  • all releases: 3s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • writerslogic/c2pa-ml
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • dcondrey

Overview

Associates a C2PA Manifest Store with an AI/ML model by writing it into the model container's own metadata slot, so the model stays loadable by its usual runtime. Three formats are supported:

Format Metadata slot Manifest encoding
GGUF (llama.cpp) typed key/value metadata c2pa.manifest as a UINT8 array (raw bytes)
SafeTensors JSON header __metadata__ c2pa.manifest as Base64
ONNX protobuf metadata_props c2pa.manifest as Base64

A remote (or side-car) manifest can instead be referenced by URI under c2pa.manifest.uri, or both an embedded store and a URI can be written together.

The C2PA Technical Specification defines no dedicated embedding method for model containers; a manifest embedded in a model declares what the asset is with the asset type assertion. This crate provides the canonical c2pa.types.model.* strings for that assertion.

Zero dependencies on native targets; the WebAssembly/npm build uses only wasm-bindgen.

Quick Start

[dependencies]
c2pa-ml = "0.1"

Embed a manifest

use c2pa_ml::{embed_manifest, ManifestSource};

let model: &[u8] = /* .gguf / .safetensors / .onnx bytes */;
let store: Vec<u8> = /* C2PA Manifest Store bytes */;

// Embed a Manifest Store directly (format is auto-detected)...
let signed = embed_manifest(model, &ManifestSource::embedded(store)).unwrap();

// ...or reference a remote manifest by URI...
let signed = embed_manifest(model, &ManifestSource::remote("https://example.com/m.c2pa")).unwrap();

// ...or both.
let signed = embed_manifest(model, &ManifestSource::both("https://example.com/m.c2pa", vec![/* ... */])).unwrap();

Read a manifest

use c2pa_ml::{read_manifest, read_manifest_uri};

let store = read_manifest(&signed).unwrap();          // embedded Manifest Store bytes
let uri = read_manifest_uri(&signed).unwrap();        // Option<String>: active manifest URI

Verify presence

use c2pa_ml::verify;

let report = verify(&signed).unwrap();
assert!(report.is_compliant());
// report.format, report.has_embedded_manifest, report.has_remote_uri

Declare the asset type

use c2pa_ml::Format;

let model_type = Format::detect(&signed).unwrap().model_type();
assert_eq!(model_type.as_str(), "c2pa.types.model.onnx"); // for an ONNX model

Explicit format

ONNX has no magic number, so auto-detection matches it last as a best-effort protobuf shape check. When the format is known in advance, use embed_manifest_as, or call the per-format module (gguf, safetensors, onnx) directly.

use c2pa_ml::{embed_manifest_as, Format, ManifestSource};

let signed = embed_manifest_as(model, Format::Onnx, &ManifestSource::embedded(store)).unwrap();

Other languages

The same API is published for JavaScript and Python from this crate, so it also serves Node, pnpm, and browser/bundler users.

npm / pnpm (WebAssembly)

npm install c2pa-ml   # or: pnpm add c2pa-ml
import { embedManifest, readManifest, detectFormat } from "c2pa-ml";

const signed = embedManifest(model, store); // Uint8Array in, Uint8Array out
const manifest = readManifest(signed);

PyPI

pip install c2pa-ml
import c2pa_ml

signed = c2pa_ml.embed_manifest(model, store)  # bytes in, bytes out
manifest = c2pa_ml.read_manifest(signed)
fmt = c2pa_ml.detect_format(model)             # "GGUF" | "SafeTensors" | "ONNX" | None

Design

  • The Manifest Store and/or manifest URI are stored under the reserved keys c2pa.manifest / c2pa.manifest.uri in the format's native metadata slot
  • GGUF: metadata is re-serialized and the tensor-data region is re-padded to general.alignment; tensor-info offsets are relative to that region, so tensor data is never rewritten
  • SafeTensors: only the JSON header is rewritten; each tensor's data_offsets are relative to the data block and stay valid
  • ONNX: only the top-level protobuf field stream is rewritten; every other field (ir_version, graph, opset_import, …) is copied through verbatim
  • Embedding replaces any existing C2PA entries; remove_manifest restores the model to its unembedded bytes

Scope

This crate implements embedding and extraction only. Manifest construction, signing, and content (hard/soft) binding are out of scope; use the official C2PA SDK to build and sign manifests. The c2pa.hash.data assertion should exclude the metadata region carrying the Manifest Store.

Related Crates

Crate Description
c2pa-fonts OpenType/TrueType (SFNT) font embedding
c2pa-warc WARC web archive embedding (ISO 28500)
c2pa-zip ZIP-based (OCF) document embedding: EPUB, DOCX, ODT
c2pa-structured-text Structured text embedding via ASCII armour delimiters
c2pa-rs Official C2PA SDK

License

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

Built by WritersLogic