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.
Install
Section titled “Install”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:
[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.
Initialize
Section titled “Initialize”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.
Sign content
Section titled “Sign content”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-uuidstd::fs::write("output.signed.png", &res.signed_content).unwrap(); // store THISres.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}"), }}Verify content
Section titled “Verify content”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 highif 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.
Token status (no upload)
Section titled “Token status (no upload)”let status = client.token_status("ctv_...").await?;println!("{} {} {:?}", status.model, status.generator_status, status.org_name);Error handling
Section titled “Error handling”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}"),}Analytics, audit, webhooks
Section titled “Analytics, audit, webhooks”let overview = client.analytics_overview(Some(30)).await?; // lookback days (None = default)let page = client.audit_log(Some(1), Some(20)).await?; // page, limitlet 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 signaturesEnterprise plans can pull an EU AI Act compliance attestation:
let att = client.attestation(None, None).await?; // default last-90-days windowlet 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).