Configuring SDK settings with Context
Regardless of which language you're working in, you use the Context and Settings classes to control SDK behavior including verification, trust anchors, thumbnails, signing, and more.
Overview of Context
Context encapsulates SDK configuration that controls how Reader, Builder, and other components operate, including:
- Settings: Trust configuration, builder behavior, thumbnails, and more.
- Signer configuration: Optional signing credentials that can be stored for reuse.
Using Context provides explicit, isolated configuration without thread-local state. It enables you to run different configurations simultaneously (for example for development with test certificates or production with strict validation), simplifies testing, and improves code clarity.
Context:
- Can be moved but not copied. After moving,
is_valid()returnsfalseon the source. - Is used at construction:
ReaderandBuildercopy configuration from theContextat construction time. TheContextdoesn't need to outlive them. - Is reusable: Use the same
Contextto create multipleReaderandBuilderinstances.
Overview of Settings
You can specify a declarative configuration for the SDK using Settings, that you can load from a JSON file or set programmatically.
Settings object structure
For the complete reference to the Settings object, see SDK object reference - Settings.
Settings JSON has this top-level structure:
{
"version": 1,
"trust": { ... },
"cawg_trust": { ... },
"core": { ... },
"verify": { ... },
"builder": { ... },
"signer": { ... },
"cawg_x509_signer": { ... }
}
| Property | Description |
|---|---|
version | Settings format version (must be 1) |
trust | Certificate trust configuration for C2PA validation |
cawg_trust | Certificate trust configuration for CAWG identity assertions |
core | Core SDK behavior and performance tuning |
verify | Validation and verification behavior |
builder | Manifest creation and embedding behavior |
signer | C2PA signer configuration |
cawg_x509_signer | CAWG identity assertion signer configuration |
The version property must be 1. All other properties are optional.
If you don't specify a property, the SDK uses the default value. If you specify null, the property is explicitly set to null (not the default). This distinction matters when overriding default behavior.
For Boolean values, use JSON true and false, not the strings "true" and "false".
Creating and using Context
- Rust
- C++
- Python
For more details on using Context and Settings in Rust, see Rust library - Configuring SDK settings
Creating a Context
The simplest way to create a Context is with default settings:
use c2pa::Context;
let context = Context::new();
Loading settings from a file
Load settings from a file using the with_settings() method, which automatically detects the format (JSON or TOML):
use c2pa::{Context, Builder, Result};
fn main() -> Result<()> {
// From a file
let context = Context::new()
.with_settings(include_str!("settings.json"))?;
// Create builder using context settings
let builder = Builder::from_context(context);
Ok(())
}
Loading settings inline
You can also provide settings inline in either JSON or TOML format. For example, using JSON:
use c2pa::{Context, Result};
fn main() -> Result<()> {
// Inline JSON format
let context = Context::new()
.with_settings(r#"
{"verify":
{"verify_after_sign": true}}
"#)?;
Ok(())
}
Loading settings programmatically
use c2pa::{Context, Settings, Result};
fn main() -> Result<()> {
let mut settings = Settings::default();
settings.verify.verify_after_sign = true;
let context = Context::new().with_settings(settings)?;
Ok(())
}
Using Context with Reader
Reader uses Context to control manifest validation and remote resource fetching:
use c2pa::{Context, Reader, Result};
use std::fs::File;
fn main() -> Result<()> {
// Configure context
let context = Context::new()
.with_settings(r#"{"verify": {"remote_manifest_fetch": false}}"#)?;
// Create reader with context
let stream = File::open("path/to/image.jpg")?;
let reader = Reader::from_context(context)
.with_stream("image/jpeg", stream)?;
println!("{}", reader.json());
Ok(())
}
Using Context with Builder
Builder uses Context to configure signing operations. The Context automatically creates a signer from settings when needed:
use c2pa::{Context, Builder, Result};
use std::io::Cursor;
use serde_json::json;
fn main() -> Result<()> {
// Configure context with signer and builder settings
let context = Context::new()
.with_settings(json!({
"signer": {
"local": {
"alg": "ps256",
"sign_cert": "path/to/cert.pem",
"private_key": "path/to/key.pem",
"tsa_url": "http://timestamp.digicert.com"
}
},
"builder": {
"claim_generator_info": {"name": "My App"},
"intent": {"Create": "digitalCapture"}
}
}))?;
// Create builder with context and inline JSON definition
let mut builder = Builder::from_context(context)
.with_definition(json!({"title": "My Image"}))?;
// Save with automatic signer from context
let mut source = std::fs::File::open("source.jpg")?;
let mut dest = Cursor::new(Vec::new());
builder.save_to_stream("image/jpeg", &mut source, &mut dest)?;
Ok(())
}
For more details on using Context and Settings in Rust, see C++ library - Configuring SDK settings
Creating a Context
The simplest way to create a Context is with SDK default settings:
#include "c2pa.hpp"
c2pa::Context context; // Uses SDK defaults
// Use with Reader or Builder
c2pa::Reader reader(context, "image.jpg");
c2pa::Builder builder(context, manifest_json);
Creating from inline JSON
To specify a simple configuration that doesn't need to be shared across the codebase, you can use inline JSON like this:
c2pa::Context context(R"({
"version": 1,
"verify": {"verify_after_sign": true},
"builder": {"claim_generator_info": {"name": "My App"}}
})");
Creating from a Settings object
To specify a configuration that needs runtime logic or incremental construction, use a Settings object like this:
c2pa::Settings settings;
settings.set("builder.thumbnail.enabled", "false");
settings.set("verify.verify_after_sign", "true");
settings.update(R"({"builder": {"claim_generator_info": {"name": "My App"}}})");
c2pa::Context context(settings);
Creating using ContextBuilder
To load a configuration from files or combine multiple configuration sources, use ContextBuilder. Don't use if you have a single configuration source, since direct construction is simpler.
c2pa::Settings base_settings;
base_settings.set("builder.thumbnail.enabled", "true");
auto context = c2pa::Context::ContextBuilder()
.with_settings(base_settings)
.with_json(R"({"verify": {"verify_after_sign": true}})")
.with_json_settings_file("config/overrides.json")
.create_context();
Using Context with Reader
Reader uses Context to control validation, trust configuration, network access, and performance.
Since Context is used only at construction, Context doesn't need to outlive the Reader.
Reading an asset from a file
c2pa::Context context(R"({
"version": 1,
"verify": {
"remote_manifest_fetch": false,
"ocsp_fetch": false
}
})");
c2pa::Reader reader(context, "image.jpg");
std::cout << reader.json() << std::endl;
Reading an asset from a stream
std::ifstream stream("image.jpg", std::ios::binary);
c2pa::Reader reader(context, "image/jpeg", stream);
std::cout << reader.json() << std::endl;
Using Context with Builder
Builder uses Context to control manifest creation, signing, thumbnails, and more.
Context doesn't need to outlive the Builder, since its used only at construction.
Once you've created a Context, you use it with Builder like this:
...
c2pa::Builder builder(context, manifest_json);
// Pass signer explicitly at signing time
c2pa::Signer signer("es256", certs, private_key);
builder.sign(source_path, output_path, signer);
For more details on using Context and Settings in Rust, see Python library - Configuring SDK settings
Creating a Context
The simplest way to create a Context is with default settings:
from c2pa import Context
ctx = Context() # Uses SDK defaults
This is appropriate for quick prototyping or when the SDK defaults are acceptable.
Creating from inline JSON
from c2pa import Context
try:
ctx = Context.from_dict({
"verify": {
"verify_cert_anchors": True
},
"trust": {
"trust_anchors": "some url"
}
})
except Exception as e:
print(f"Exception loading settings: {e}")
Creating from a JSON file
import json
from c2pa import Context, Settings
with open("config/settings.json", "r") as f:
settings = Settings.from_json(f.read())
ctx = Context(settings)
Using Context with Reader
Reader uses Context to control how it validates manifests and handles remote resources:
- Verification behavior: Whether to verify after reading, check trust, and so on.
- Trust configuration: Which certificates to trust when validating signatures.
- Network access: Whether to fetch remote manifests or OCSP responses.
Context is used only at construction time. Reader copies the configuration it needs internally, so the Context does not need to outlive the Reader. A single Context can be reused for multiple Reader instances.
ctx = Context.from_dict({"verify": {"remote_manifest_fetch": False}})
reader = Reader("image.jpg", context=ctx)
print(reader.json())
Reading an asset from a stream:
with open("image.jpg", "rb") as stream:
reader = Reader("image/jpeg", stream, context=ctx)
print(reader.json())
Using Context with Builder
Builder uses Context to control how it creates and signs C2PA manifests. The Context affects:
- Claim generator information: Application name, version, and metadata embedded in the manifest.
- Thumbnail generation: Whether to create thumbnails, and their size, quality, and format.
- Action tracking: Auto-generation of actions like
c2pa.created,c2pa.opened,c2pa.placed. - Intent: The purpose of the claim (create, edit, or update).
- Verification after signing: Whether to validate the manifest immediately after signing.
- Signer configuration (optional): Credentials stored in the context for reuse.
Context is used only when constructing the Builder. The Builder copies the configuration it needs internally, so the Context does not need to outlive the Builder. A single Context can be reused for multiple Builder instances.
ctx = Context.from_dict({
"builder": {
"claim_generator_info": {"name": "An app", "version": "0.1.0"},
"intent": {"Create": "digitalCapture"}
}
})
builder = Builder(manifest_json, context=ctx)
with open("source.jpg", "rb") as src, open("output.jpg", "w+b") as dst:
builder.sign(signer, "image/jpeg", src, dst)