Using builder intents
Intents tell the Builder what kind of manifest you are creating. They enable validation, add required default actions, and help prevent invalid operations.
Intent types
There are three types of intents, shown here:
| Intent | Use when... | Parent ingredient | Auto-generated action |
|---|---|---|---|
Create | Creating brand-new content | Must NOT have one | c2pa.created |
Edit | Modifying existing content | Auto-created from source if not provided | c2pa.opened (linked to parent) |
Update | Making metadata-only changes | Auto-created from source if not provided | c2pa.opened (linked to parent) |
- Rust
- C++
- Python
- JavaScript
Setting the intent
Set the intent through Context settings or by calling set_intent on the Builder. Using Context keeps intent configuration alongside other builder settings:
use c2pa::{Context, Builder, Result};
fn main() -> Result<()> {
let context = Context::new()
.with_settings(r#"{
"builder": {
"intent": {"Create": "digitalCapture"},
"claim_generator_info": {"name": "My App", "version": "1.0"}
}
}"#)?;
let mut builder = Builder::from_context(context)
.with_definition(r#"{"title": "New Image"}"#)?;
let mut source = std::fs::File::open("source.jpg")?;
let mut dest = std::fs::File::create("signed.jpg")?;
builder.save_to_stream("image/jpeg", &mut source, &mut dest)?;
Ok(())
}
Alternatively, call set_intent directly on the Builder:
use c2pa::{Builder, BuilderIntent, DigitalSourceType};
builder.set_intent(BuilderIntent::Create(DigitalSourceType::DigitalCapture));
Create intent
Use BuilderIntent::Create(DigitalSourceType) for new digital creations without a parent ingredient. A DigitalSourceType is required; common values include Empty, DigitalCapture, TrainedAlgorithmicMedia, and DigitalCreation.
builder.set_intent(BuilderIntent::Create(DigitalSourceType::TrainedAlgorithmicMedia));
Edit intent
Use BuilderIntent::Edit for editing an existing asset. If no parent ingredient has been added, the Builder automatically derives one from the source stream:
use serde_json::json;
builder.set_intent(BuilderIntent::Edit);
builder.add_ingredient_from_stream(
json!({"title": "Original", "relationship": "parentOf"}).to_string(),
"image/jpeg",
&mut source_stream,
)?;
Update intent
Use BuilderIntent::Update for non-editorial, metadata-only changes. It allows exactly one ingredient (the parent) and does not allow changes to the parent's hashed content:
builder.set_intent(BuilderIntent::Update);
Setting the intent
Set the intent through Context settings or by calling set_intent on the Builder. Using Context keeps intent configuration alongside other builder settings:
#include "c2pa.hpp"
c2pa::Context context(R"({
"version": 1,
"builder": {
"intent": {"Create": "digitalCapture"},
"claim_generator_info": {"name": "My App", "version": "1.0"}
}
})");
c2pa::Builder builder(context, R"({})");
builder.sign(source_path, output_path, signer);
Alternatively, call set_intent directly on the Builder:
c2pa::Context context;
c2pa::Builder builder(context, R"({})");
builder.set_intent(Create, DigitalCapture);
builder.sign(source_path, output_path, signer);
Create intent
Use the Create intent for new digital creations without a parent ingredient. A C2paDigitalSourceType is required; common values include Empty, DigitalCapture, TrainedAlgorithmicMedia, and DigitalCreation.
c2pa::Context context(R"({
"version": 1,
"builder": {"intent": {"Create": "trainedAlgorithmicMedia"}}
})");
c2pa::Builder builder(context, R"({})");
builder.sign(source_path, output_path, signer);
Edit intent
Use the Edit intent for editing an existing asset. If no parent ingredient has been added, the Builder automatically creates one from the source stream passed to sign():
c2pa::Context context(R"({
"version": 1,
"builder": {"intent": "edit"}
})");
c2pa::Builder builder(context, R"({})");
builder.sign("original.jpg", "edited.jpg", signer);
To manually provide the parent ingredient:
c2pa::Context context(R"({
"version": 1,
"builder": {"intent": "edit"}
})");
c2pa::Builder builder(context, R"({})");
std::ifstream original("original.jpg", std::ios::binary);
builder.add_ingredient(
R"({"title": "Original Photo", "relationship": "parentOf"})",
"image/jpeg",
original
);
builder.sign("canvas.jpg", "edited.jpg", signer);
Update intent
Use the Update intent for non-editorial, metadata-only changes. It allows exactly one ingredient (the parent) and does not allow changes to the parent's hashed content:
c2pa::Context context(R"({
"version": 1,
"builder": {"intent": "update"}
})");
c2pa::Builder builder(context, R"({})");
builder.sign("signed_asset.jpg", "updated_asset.jpg", signer);
Setting the intent
Set the intent through Context settings. Using Context keeps intent configuration alongside other builder settings:
from c2pa import Context, Builder
ctx = Context.from_dict({
"builder": {
"intent": {"Create": "digitalCapture"},
"claim_generator_info": {"name": "My App", "version": "1.0"}
}
})
builder = Builder(manifest_json, context=ctx)
with open("source.jpg", "rb") as src, open("signed.jpg", "w+b") as dst:
builder.sign(signer, "image/jpeg", src, dst)
Alternatively, you can call set_intent directly on a Builder instance for one-off operations or when the intent is determined at runtime. For example:
with Builder({}) as builder:
builder.set_intent(
C2paBuilderIntent.CREATE,
C2paDigitalSourceType.TRAINED_ALGORITHMIC_MEDIA,
)
with open("source.jpg", "rb") as source, open("output.jpg", "wb") as dest:
builder.sign(signer, "image/jpeg", source, dest)
Create intent
Use the Create intent for new digital creations without a parent ingredient. A digital source type is required; common values include "digitalCapture", "trainedAlgorithmicMedia", and "digitalCreation".
ctx = Context.from_dict({
"builder": {"intent": {"Create": "trainedAlgorithmicMedia"}}
})
builder = Builder(manifest_json, context=ctx)
with open("source.jpg", "rb") as src, open("signed.jpg", "w+b") as dst:
builder.sign(signer, "image/jpeg", src, dst)
Edit intent
Use the Edit intent for editing an existing asset. If no parent ingredient has been added, the Builder automatically creates one from the source stream passed to sign():
ctx = Context.from_dict({
"builder": {"intent": "edit"}
})
builder = Builder(manifest_json, context=ctx)
with open("original.jpg", "rb") as src, open("edited.jpg", "w+b") as dst:
builder.sign(signer, "image/jpeg", src, dst)
To manually provide the parent ingredient:
import json
ctx = Context.from_dict({
"builder": {"intent": "edit"}
})
builder = Builder(manifest_json, context=ctx)
ingredient_json = json.dumps({
"title": "Original Photo",
"relationship": "parentOf"
})
with open("original.jpg", "rb") as ingredient:
builder.add_ingredient(ingredient_json, "image/jpeg", ingredient)
with open("canvas.jpg", "rb") as src, open("edited.jpg", "w+b") as dst:
builder.sign(signer, "image/jpeg", src, dst)
Update intent
Use the Update intent for non-editorial, metadata-only changes. It allows exactly one ingredient (the parent) and does not allow changes to the parent's hashed content:
ctx = Context.from_dict({
"builder": {"intent": "update"}
})
builder = Builder(manifest_json, context=ctx)
with open("signed_asset.jpg", "rb") as src, open("updated.jpg", "w+b") as dst:
builder.sign(signer, "image/jpeg", src, dst)
Setting the intent
The snippets below use c2pa from const c2pa = await createC2pa({ wasmSrc }); (see Reading and verifying manifest data).
Call setIntent on a builder from c2pa.builder. Intents match the same Create / Edit / Update semantics as other CAI SDKs (see the table on this page).
const builder = await c2pa.builder.new();
await builder.setIntent({
create:
'http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia',
});
await builder.setIntent('edit');
await builder.setIntent('update');
Create intent
Use create with a digital source type URI. There must be no parent ingredient; the SDK may add c2pa.created when appropriate.
const builder = await c2pa.builder.new();
await builder.setIntent({
create:
'http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture',
});
// Add assertions, thumbnail, then sign (see [Build](../build.mdx)).
Edit intent
Use edit when changing pixel or editorial content. If you do not add a parent ingredient, one can be derived from the source blob you pass to sign.
const builder = await c2pa.builder.new();
await builder.setIntent('edit');
Add a parent explicitly with addIngredientFromBlob:
const parentBlob = await fetch('/original.jpg').then((r) => r.blob());
const builder = await c2pa.builder.new();
await builder.setIntent('edit');
await builder.addIngredientFromBlob(
{
title: 'Original Photo',
relationship: 'parentOf',
format: 'image/jpeg',
},
parentBlob.type,
parentBlob,
);
Update intent
Use update for restricted, metadata-oriented edits (single parent ingredient, no change to the parent’s hashed payload per C2PA rules).
const builder = await c2pa.builder.new();
await builder.setIntent('update');
More background: c2pa-rs Builder and the c2pa-web README.