Skip to content

Rust SDK

The certivu crate is an async, thin REST client for signing and verifying AI-generated content. It targets performance-sensitive backends hitting /v1/verify at volume and aligns with the C2PA ecosystem (c2pa-rs).

Like every Certivu SDK, it does no client-side cryptography. Signing happens server-side (org private keys and signing quota live behind the API) and verification goes through the API. The crate never bundles ML-DSA.

The crate ships from cdn.certivu.ai as a static Cargo registry — not from crates.io or a public GitHub repo (the monorepo is private). Cargo’s sparse-registry protocol is plain static files, so the CDN serves the whole registry.

Add the registry once to your .cargo/config.toml (project-local, or ~/.cargo/config.toml for all projects):

[registries.certivu]
index = "sparse+https://cdn.certivu.ai/sdk/rust/"

Then depend on it, naming the registry:

Cargo.toml
[dependencies]
certivu = { version = "1.0.0", registry = "certivu" }
tokio = { version = "1", features = ["full"] }

Requires Rust 1.74+. Uses rustls for TLS (no OpenSSL). The crate versions on its own line (starting 1.0.0), independent of the platform version, and is content-addressed — a given version’s bytes never change.

let client = certivu::Client::builder("ctv_key_abc123") // omit (pass "") for verify-only
.generator_id("your-generator-uuid") // required for signing
.build();

Builder options: base_url (point at the sandbox), generator_id, timeout (default 60s), and http_client (supply your own reqwest::Client). Client is cheap to clone and safe to share across tasks.


let img = std::fs::read("output.png").unwrap(); // or .mp3, .mp4, .pdf, etc.
let res = client.sign(img, "stable-diffusion-xl").await?;
println!("{}", res.token); // ctv_7f3kx9mq2...
println!("{}", res.record_id); // rec-uuid
std::fs::write("output.signed.png", &res.signed_content).unwrap(); // store THIS

res.signed_content is the signed content with the ctv_ token injected into a format-native container and the watermark embedded — deliver it instead of the original. Use sign_with(content, model, SignOptions { format: Some("image".into()), generator_id: None }) to override the format or generator. When the API recognises identical input bytes it re-uses the existing record without charging a new signature; res.deduplicated is then true.

use certivu::SignItem;
let results = client.sign_batch(vec![
SignItem { content: a, model: "sdxl".into(), format: None },
SignItem { content: b, model: "sdxl".into(), format: Some("image".into()) },
]).await?;
for r in results {
match r {
Ok(s) => println!("{}", s.token),
Err(e) => eprintln!("item failed: {e}"),
}
}

Verification is free, unlimited, and needs no API key — construct a verify-only client with an empty key:

let client = certivu::Client::new("");
let res = client.verify(content_bytes).await?;
println!("{} {}", res.authentic, res.confidence); // e.g. true high
if let Some(p) = res.provenance {
println!("signed by {} using {}", p.org, p.model);
}

Use verify_with_token(content, "ctv_...") to take the fast path when you already hold the token. Verification works on transformed copies (resized, recompressed, reposted) via watermark, pHash, and fingerprint recovery.

let status = client.token_status("ctv_...").await?;
println!("{} {} {:?}", status.model, status.generator_status, status.org_name);

Any non-2xx response is Error::Api(ApiError). Match and use the predicate helpers:

use certivu::Error;
match client.sign(img, "sdxl").await {
Ok(res) => { /* … */ }
Err(Error::Api(e)) if e.is_quota() => eprintln!("upgrade at {:?}", e.upgrade_url),
Err(Error::Api(e)) if e.is_auth() => eprintln!("invalid API key"),
Err(Error::Api(e)) if e.is_validation() => eprintln!("{}", e.message),
Err(e) => eprintln!("certivu error: {e}"),
}

let overview = client.analytics_overview(Some(30)).await?; // lookback days (None = default)
let page = client.audit_log(Some(1), Some(20)).await?; // page, limit
let rec = client.record_analytics("rec-uuid").await?; // Growth+
let wh = client.create_webhook("https://hooks.example/certivu", vec!["tamper.detected".into()]).await?;
println!("{}", wh.secret); // shown ONCE — store it to verify delivery signatures

Enterprise plans can pull an EU AI Act compliance attestation:

let att = client.attestation(None, None).await?; // default last-90-days window
let html = client.export_attestation("html", None, None).await?; // rendered document string

  • No client crypto. Signing and verification are both API calls; offline verification is not supported.
  • TLS via rustls — no system OpenSSL dependency.
  • Registry index is sparse+https://cdn.certivu.ai/sdk/rust/; the crate tarball and index are served statically from the CDN (no crates.io, no GitHub).