This guide explains how to configure the C2PA SDK using Context and Settings. The configuration controls SDK behavior including verification, trust anchors, thumbnails, signing, and more.
See also:
- Usage: Reading and signing with Reader and Builder
- Rust SDK settings: Shared settings schema and additional JSON examples
- CAI settings schema reference: Complete schema reference
The simplest way to configure the SDK is to create a Context with inline JSON and pass it to Reader or Builder:
#include "c2pa.hpp"
// Create a Context with settings
c2pa::Context context(R"({
"version": 1,
"builder": {
"claim_generator_info": {"name": "My App", "version": "1.0"},
"thumbnail": {"enabled": false}
},
"verify": {
"remote_manifest_fetch": false
}
})");
// Use with Reader or Builder
c2pa::Reader reader(context, "image.jpg");
c2pa::Builder builder(context, manifest_json);For default SDK configuration, just create an empty Context:
c2pa::Context context; // Uses SDK defaults
c2pa::Reader reader(context, "image.jpg");Context encapsulates SDK configuration that controls how Reader, Builder, and other components operate:
- Settings: Trust configuration, builder behavior, thumbnails, and more
- Signer configuration: Optional signing credentials that can be stored for reuse
- State isolation: Each
Contextis independent, allowing multiple configurations to coexist
Context provides explicit, isolated configuration:
- Makes dependencies explicit: Configuration is passed directly to
ReaderandBuilder, not hidden in global state - Enables multiple configurations: Run different configurations simultaneously (e.g., development with test certificates, production with strict validation)
- Eliminates thread-local state: No subtle bugs from shared state
- Simplifies testing: Isolated configurations without cleanup or interference
- Improves code clarity:
Builder(context, manifest)shows configuration is being used
Note
The deprecated c2pa::load_settings(data, format) still works but you should migrate to Context. Never mix the two approaches.
See Migrating from deprecated APIS.
- Non-copyable, moveable:
Contextcan be moved but not copied. After moving,is_valid()returnsfalseon the source - Used at construction:
ReaderandBuildercopy configuration from the context at construction time. TheContextdoesn't need to outlive them - Reusable: Use the same
Contextto create multiple readers and builders
c2pa::Context context(settings);
// All three use the same configuration
c2pa::Builder builder1(context, manifest1);
c2pa::Builder builder2(context, manifest2);
c2pa::Reader reader(context, "image.jpg");
// Context can go out of scope, readers/builders still workSettings use JSON or TOML format. The schema is shared across all SDK languages (Rust, C/C++, Python). JSON is preferred for C++.
// JSON (preferred)
c2pa::Context context(R"({"verify": {"verify_after_sign": true}})");
// TOML
c2pa::Settings settings(R"([verify]
verify_after_sign = true)", "toml");
c2pa::Context context(settings);There are several ways to create a Context:
For quick prototyping and simple use cases, you can use the SDK defaults like this:
c2pa::Context context; // Uses SDK defaultsFor information on the defaults, see Configuring SDK settings - Default configuration.
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"}}
})");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);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();Important
Later configuration overrides earlier configuration. In the example above, overrides.json will override values from base_settings and the inline JSON.
ContextBuilder methods:
| Method | Description |
|---|---|
with_settings(settings) |
Apply a Settings object |
with_json(json_string) |
Apply settings from a JSON string |
with_json_settings_file(path) |
Load and apply settings from a JSON file |
with_signer(signer) |
Store a Signer in the context (consumed; used by Builder::sign with no explicit signer) |
with_progress_callback(callback) |
Register a progress/cancel callback (see Progress callbacks and cancellation) |
create_context() |
Build and return the Context (consumes the builder) |
You can register a callback on a Context to receive progress notifications during signing and reading operations, and to cancel an operation in flight.
Use ContextBuilder::with_progress_callback to attach a callback before building the context:
#include <atomic>
std::atomic<int> phase_count{0};
auto context = c2pa::Context::ContextBuilder()
.with_progress_callback([&](c2pa::ProgressPhase phase, uint32_t step, uint32_t total) {
++phase_count;
// Return true to continue, false to cancel.
return true;
})
.create_context();
// Use the context normally — the callback fires automatically.
c2pa::Builder builder(context, manifest_json);
builder.sign("source.jpg", "output.jpg", signer);The callback signature is:
bool callback(c2pa::ProgressPhase phase, uint32_t step, uint32_t total);phase— which stage the SDK is in (seeProgressPhasevalues below).step— monotonically increasing counter within the current phase, starting at1. Resets to1at the start of each new phase. Use as a liveness signal: a risingstepmeans the SDK is making forward progress.total—0= indeterminate (show a spinner);1= single-shot phase;> 1= determinate (step / totalgives a completion fraction).- Return value — return
trueto continue,falseto request cancellation (same effect as callingcontext.cancel()).
Do not throw from the progress callback. Exceptions cannot cross the C/Rust boundary safely; if your callback throws, the wrapper catches it and the operation is aborted as a cancellation (you do not get your exception back at the call site). Use return false, context.cancel(), or application-side state instead.
You may call Context::cancel() from another thread while the same Context remains valid and is not being destroyed or moved concurrently with that call. The SDK returns a C2paException with an OperationCancelled error at the next progress checkpoint:
#include <thread>
auto context = c2pa::Context::ContextBuilder()
.with_progress_callback([](c2pa::ProgressPhase, uint32_t, uint32_t) {
return true; // Don't cancel from the callback — use cancel() instead.
})
.create_context();
// Kick off a cancel after 500 ms from a background thread.
std::thread cancel_thread([&context]() {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
context.cancel();
});
try {
c2pa::Builder builder(context, manifest_json);
builder.sign("large_file.jpg", "output.jpg", signer);
} catch (const c2pa::C2paException& e) {
// "OperationCancelled" if cancel() fired before signing completed.
}
cancel_thread.join();cancel() is safe to call when no operation is in progress — it is a no-op in that case (and a no-op if the Context is moved-from).
| Phase | When emitted |
|---|---|
Reading |
Start of a read/verification pass |
VerifyingManifest |
Manifest structure is being validated |
VerifyingSignature |
COSE signature is being verified |
VerifyingIngredient |
An ingredient manifest is being verified |
VerifyingAssetHash |
Asset hash is being computed and checked |
AddingIngredient |
An ingredient is being embedded |
Thumbnail |
A thumbnail is being generated |
Hashing |
Asset data is being hashed for signing |
Signing |
Claim is being signed |
Embedding |
Signed manifest is being embedded into the asset |
FetchingRemoteManifest |
A remote manifest URL is being fetched |
Writing |
Output is being written |
FetchingOCSP |
OCSP certificate status is being fetched |
FetchingTimestamp |
A timestamp is being fetched from a TSA |
Typical phase sequence during signing:
AddingIngredient → Thumbnail → Hashing → Signing → Embedding
If verify_after_sign is enabled, verification phases follow:
→ VerifyingManifest → VerifyingSignature → VerifyingAssetHash → VerifyingIngredient
Typical phase sequence during reading:
Reading → VerifyingManifest → VerifyingSignature → VerifyingAssetHash → VerifyingIngredient
with_progress_callback chains with other ContextBuilder methods:
auto context = c2pa::Context::ContextBuilder()
.with_settings(settings)
.with_signer(std::move(signer))
.with_progress_callback([](c2pa::ProgressPhase phase, uint32_t step, uint32_t total) {
// Update a UI progress bar, log phases, etc.
return true;
})
.create_context();Common configurations include:
- Development with test certificates
- Layered configuration
- Configuration from environment variables
- Multiple contexts
- Temporary contexts
Trust self-signed or custom CA certificates during development:
std::string test_ca = read_file("test-ca.pem");
c2pa::Context dev_context(R"({
"version": 1,
"trust": {
"user_anchors": ")" + test_ca + R"("
},
"verify": {
"remote_manifest_fetch": false,
"ocsp_fetch": false
},
"builder": {
"claim_generator_info": {"name": "Dev Build", "version": "dev"},
"thumbnail": {"enabled": false}
}
})");Load base configuration and apply environment-specific overrides:
auto context = c2pa::Context::ContextBuilder()
.with_json_settings_file("config/base.json")
.with_json_settings_file("config/" + environment + ".json")
.with_json(R"({
"builder": {
"claim_generator_info": {"version": ")" + app_version + R"("}
}
})")
.create_context();Adapt configuration based on runtime environment:
std::string env = std::getenv("ENVIRONMENT") ? std::getenv("ENVIRONMENT") : "dev";
c2pa::Settings settings;
if (env == "production") {
settings.update(read_file("config/production.json"), "json");
settings.set("verify.strict_v1_validation", "true");
} else {
settings.update(read_file("config/development.json"), "json");
settings.set("verify.remote_manifest_fetch", "false");
}
c2pa::Context context(settings);Use different Context objects for different purposes:
c2pa::Context dev_context(dev_settings);
c2pa::Context prod_context(prod_settings);
c2pa::Builder dev_builder(dev_context, manifest);
c2pa::Builder prod_builder(prod_context, manifest);Since configuration is copied at construction, you can use temporary contexts:
c2pa::Reader reader(
c2pa::Context(R"({"verify": {"remote_manifest_fetch": false}})"),
"image.jpg"
);Reader uses Context to control validation, trust configuration, network access, and performance.
Important
Context is used only at construction. Reader copies the configuration it needs internally, so the Context doesn't need to outlive the Reader.
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;std::ifstream stream("image.jpg", std::ios::binary);
c2pa::Reader reader(context, "image/jpeg", stream);
std::cout << reader.json() << std::endl;Builder uses Context to control manifest creation, signing, thumbnails, and more.
Important
The Context is used only when constructing the Builder. It copies the configuration internally, so the Context doesn't need to outlive the Builder.
c2pa::Context context(R"({
"version": 1,
"builder": {
"claim_generator_info": {"name": "My App", "version": "1.0"},
"intent": {"Create": "digitalCapture"}
}
})");
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);The Settings class provides methods for creating and manipulating configuration:
| Method | Description |
|---|---|
Settings() |
Create default settings |
Settings(data, format) |
Parse settings from a string. Format is "json" or "toml" |
set(path, json_value) |
Set a value by dot-separated path (e.g., "verify.verify_after_sign"). Value must be JSON-encoded. Returns *this for chaining |
update(data) |
Merge JSON configuration (same as update(data, "json")) |
update(data, format) |
Merge configuration from a string with specified format |
is_valid() |
Returns true if the object is valid (not moved-from) |
Note
- Settings are moveable, not copyable. After moving,
is_valid()returnsfalseon the source. set()andupdate()can be chained for sequential configuration.- Later calls override earlier ones (last wins).
Tip
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.
Important
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".
Tip
For the complete default settings configuration, see Rust library - Configuring SDK settings - Default configuration.
The trust properties control which certificates are trusted when validating C2PA manifests.
| Property | Description | Default |
|---|---|---|
trust.user_anchors |
Additional root certificates (PEM format). Adds custom CAs without replacing built-in trust anchors. Recommended for development. | — |
trust.allowed_list |
Explicitly allowed certificates (PEM format). Trusted regardless of chain validation. Use for development/testing to bypass validation. | — |
trust.trust_anchors |
Default trust anchor root certificates (PEM format). Replaces the SDK's built-in trust anchors entirely. | — |
trust.trust_config |
Allowed Extended Key Usage (EKU) OIDs for certificate purposes (e.g., document signing: 1.3.6.1.4.1.311.76.59.1.9). |
— |
For development, add your test root CA without replacing the SDK's default trust store:
std::string test_root_ca = R"(-----BEGIN CERTIFICATE-----
MIICEzCCAcWgAwIBAgIUW4fUnS38162x10PCnB8qFsrQuZgwBQYDK2VwMHcxCzAJ
...
-----END CERTIFICATE-----)";
c2pa::Context context(R"({
"version": 1,
"trust": {
"user_anchors": ")" + test_root_ca + R"("
}
})");
c2pa::Reader reader(context, "signed_asset.jpg");For quick testing, bypass chain validation by explicitly allowing a specific certificate:
std::string test_cert = read_file("test_cert.pem");
c2pa::Settings settings;
settings.update(R"({
"version": 1,
"trust": {
"allowed_list": ")" + test_cert + R"("
}
})");
c2pa::Context context(settings);
c2pa::Reader reader(context, "signed_asset.jpg");Suppose dev_trust_config.json looks like this:
{
"version": 1,
"trust": {
"user_anchors": "-----BEGIN CERTIFICATE-----\nMIICEzCCA...\n-----END CERTIFICATE-----",
"trust_config": "1.3.6.1.4.1.311.76.59.1.9\n1.3.6.1.4.1.62558.2.1"
}
}For the PEM string (for example in user_anchors in above example):
- Use literal
\n(as two-character strings) in JSON for line breaks - Include the full certificate chain if needed
- Concatenate multiple certificates into a single string
Load in your application:
auto context = c2pa::Context::ContextBuilder()
.with_json_settings_file("dev_trust_config.json")
.create_context();
c2pa::Reader reader(context, "signed_asset.jpg");The cawg_trust properties configure CAWG (Creator Assertions Working Group) validation of identity assertions in C2PA manifests. The structure is identical to trust.
Note
CAWG trust settings only apply when processing identity assertions with X.509 certificates. If your workflow doesn't use CAWG identity assertions, these settings have no effect.
The core properties control SDK behavior and performance tuning:
| Property | Description | Default |
|---|---|---|
merkle_tree_chunk_size_in_kb |
Merkle tree chunk size | null |
merkle_tree_max_proofs |
Maximum merkle tree proofs | 5 |
backing_store_memory_threshold_in_mb |
Memory threshold for backing store | 512 |
decode_identity_assertions |
Decode identity assertions | true |
allowed_network_hosts |
Allowed network hosts for SDK requests | null |
Use cases:
- Performance tuning for large files: Set
backing_store_memory_threshold_in_mbto2048or higher for large video files with sufficient RAM - Restricted network environments: Set
allowed_network_hoststo limit which domains the SDK can contact
The verify properties control how the SDK validates C2PA manifests.
The following table lists the key properties (all default to true):
| Property | Description | Default |
|---|---|---|
verify_after_reading |
Automatically verify manifests when reading assets | true |
verify_after_sign |
Automatically verify manifests after signing (recommended) | true |
verify_trust |
Verify signing certificates against trust anchors | true |
verify_timestamp_trust |
Verify timestamp authority (TSA) certificates | true |
remote_manifest_fetch |
Fetch remote manifests referenced in the asset | true |
ocsp_fetch |
Fetch OCSP responses for certificate validation | false |
skip_ingredient_conflict_resolution |
Skip ingredient conflict resolution | false |
strict_v1_validation |
Enable strict C2PA v1 validation | false |
Warning
Disabling verify_trust or verify_timestamp_trust makes verification non-compliant with the C2PA specification. Only modify in controlled environments or with specific requirements.
Disable network-dependent features:
c2pa::Context context(R"({
"version": 1,
"verify": {
"remote_manifest_fetch": false,
"ocsp_fetch": false
}
})");
c2pa::Reader reader(context, "signed_asset.jpg");Enable all validation features for certification or compliance testing:
c2pa::Context context(R"({
"version": 1,
"verify": {
"strict_v1_validation": true,
"ocsp_fetch": true,
"verify_trust": true,
"verify_timestamp_trust": true
}
})");
c2pa::Reader reader(context, "asset_to_validate.jpg");
auto validation_result = reader.json();The builder settings control how the SDK creates and embeds C2PA manifests:
- Builder intent to specify the purpose of the claim (for example create or edit).
- Claim generator information - Identifies your application in the manifest.
- Thumbnail settings
- Action tracking settings - See ActionsSettings in the SDK object reference.
- Other builder settings
Use the builder.intent setting to specify the purpose of the claim, one of:
{"Create": <TYPE>}: Specifies a new digital creation, where<TYPE>is one of the DigitalSourceType.{"Edit": null}: An edit of a pre-existing parent asset.{"Update": null}: An restricted version ofEdittype for non-editorial changes.
Tip
For more information on intents, see Intents and BuilderIntent in the SDK object reference..
Example: Original digital capture (photos from camera)
c2pa::Context camera_context(R"({
"version": 1,
"builder": {
"intent": {"Create": "digitalCapture"},
"claim_generator_info": {"name": "Camera App", "version": "1.0"}
}
})");Example: Editing existing content
c2pa::Context editor_context(R"({
"version": 1,
"builder": {
"intent": {"Edit": null},
"claim_generator_info": {"name": "Photo Editor", "version": "2.0"}
}
})");Identifies your application in the C2PA manifest:
| Property | Description |
|---|---|
claim_generator_info.name |
Application name (required, e.g., "My Photo Editor") |
claim_generator_info.version |
Application version (recommended, e.g., "2.1.0") |
claim_generator_info.icon |
Icon in C2PA format (optional) |
claim_generator_info.operating_system |
OS identifier or "auto" to auto-detect (optional) |
See ClaimGeneratorInfoSettings in the SDK object reference.
Example:
c2pa::Context context(R"({
"version": 1,
"builder": {
"claim_generator_info": {
"name": "My Photo Editor",
"version": "2.1.0",
"operating_system": "auto"
}
}
})");
c2pa::Builder builder(context, manifest_json);The builder.thumbnail properties control automatic thumbnail generation:
| Property | Description | Default |
|---|---|---|
enabled |
Enable/disable thumbnail generation | true |
long_edge |
Maximum size in pixels for the long edge | 1024 |
quality |
Quality level: "low", "medium", or "high" |
"medium" |
format |
Output format (null for auto-detect) | null |
prefer_smallest_format |
Prefer smallest format by file size | true |
ignore_errors |
Continue if thumbnail generation fails | true |
Examples:
// Disable thumbnails for batch processing
c2pa::Context no_thumbnails(R"({
"builder": {
"claim_generator_info": {"name": "Batch Processor"},
"thumbnail": {"enabled": false}
}
})");
// Customize for mobile (smaller size, lower quality)
c2pa::Context mobile_thumbnails(R"({
"builder": {
"thumbnail": {
"long_edge": 512,
"quality": "low",
"prefer_smallest_format": true
}
}
})");The signer properties configure the C2PA signer. Set to null if you provide the signer at runtime, or configure as local or remote in settings.
Note
In C++, you typically create a c2pa::Signer explicitly and pass it to Builder::sign(). Settings-based signing is useful when you need the same configuration across multiple operations or when loading from files.
Use when you have direct access to the private key and certificate. See signer.local for all properties.
Example: Local signer with ES256
std::string config = R"({
"version": 1,
"signer": {
"local": {
"alg": "es256",
"sign_cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----",
"tsa_url": "http://timestamp.digicert.com"
}
}
})";
c2pa::Context context(config);
c2pa::Builder builder(context, manifest_json);
builder.sign(source_path, dest_path); // Uses signer from contextUse when the private key is on a secure signing service (HSM, cloud KMS). See signer.remote for all properties.
The signing service receives a POST request with data to sign and must return the signature:
c2pa::Context context(R"({
"version": 1,
"signer": {
"remote": {
"url": "https://signing-service.example.com/sign",
"alg": "ps256",
"sign_cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
"tsa_url": "http://timestamp.digicert.com"
}
}
})");For full programmatic control, create a Signer and pass it to Builder::sign(). This is the typical C++ approach:
c2pa::Signer signer("es256", certs_pem, private_key_pem, "http://timestamp.digicert.com");
c2pa::Builder builder(context, manifest_json);
builder.sign(source_path, dest_path, signer);The Context controls verification and builder options. The signer is used only for the cryptographic signature.
The cawg_x509_signer property configures signing for identity assertions. It has the same structure as signer (local or remote).
When to use: To sign identity assertions separately from the main C2PA claim. When both signer and cawg_x509_signer are configured, the SDK uses a dual signer:
- Main claim signature from
signer - Identity assertions signed with
cawg_x509_signer
Example: Dual signer configuration
c2pa::Context context(R"({
"version": 1,
"signer": {
"local": {
"alg": "es256",
"sign_cert": "...",
"private_key": "..."
}
},
"cawg_x509_signer": {
"local": {
"alg": "ps256",
"sign_cert": "...",
"private_key": "..."
}
}
})");The SDK introduced Context-based APIs to replace constructors and functions that relied on thread-local state. The older APIs still compile but produce deprecation warnings. This section covers each deprecation that involves passing a Context, and explains the Builder::sign overloads.
| Deprecated API | Replacement |
|---|---|
load_settings(data, format) |
Context constructors or ContextBuilder |
Reader(format, stream) |
Reader(context, format, stream) |
Reader(source_path) |
Reader(context, source_path) |
Builder(manifest_json) |
Builder(context, manifest_json) |
Builder::sign(..., ostream, ...) |
Builder::sign(..., iostream, ...) |
c2pa::load_settings(data, format) sets thread-local settings that Reader and Builder pick up implicitly. Replace it with a Context that you pass explicitly.
| Aspect | load_settings (legacy) | Context |
|---|---|---|
| Scope | Global / thread-local | Per Reader/Builder, passed explicitly |
| Multiple configs | Awkward (per-thread) | One context per configuration |
| Testing | Shared global state | Isolated contexts per test |
Deprecated:
std::ifstream config_file("settings.json");
std::string config((std::istreambuf_iterator<char>(config_file)), std::istreambuf_iterator<char>());
c2pa::load_settings(config, "json");
c2pa::Reader reader("image/jpeg", stream); // uses thread-local settingsWith context API:
c2pa::Context context(settings_json_string); // or Context(Settings(...))
c2pa::Reader reader(context, "image/jpeg", stream);If you still use load_settings, construct Reader or Builder without a context parameter to continue using the thread-local settings. Prefer passing a context for new code.
The following constructors are deprecated because they rely on thread-local settings:
Reader(const std::string& format, std::istream& stream)Reader(const std::filesystem::path& source_path)Builder(const std::string& manifest_json)
The migration path for each is to create a Context and pass it as the first argument.
Deprecated:
c2pa::Reader reader("image/jpeg", stream);
c2pa::Reader reader("image.jpg");
c2pa::Builder builder(manifest_json);With context API:
c2pa::Context context; // or Context(settings) or Context(json_string)
c2pa::Reader reader(context, "image/jpeg", stream);
c2pa::Reader reader(context, "image.jpg");
c2pa::Builder builder(context, manifest_json);If you need default SDK behavior and have no custom settings, c2pa::Context context; with no arguments is sufficient.
The deprecation warnings reference IContextProvider in their suggested fix (e.g., "Use Reader(IContextProvider& context, ...)"). IContextProvider is the interface that Reader and Builder constructors accept. Context is the SDK's built-in implementation of this interface.
When the deprecation warning says "Use Reader(IContextProvider& context, ...)", passing a Context object satisfies that parameter.
External libraries can also implement IContextProvider to provide their own context objects (for example, wrapping a platform-specific configuration system). The interface is minimal: any class that can produce a valid C2paContext* pointer and report its validity can serve as a context provider. This becomes relevant when building integrations that need to manage context lifetime or initialization differently than the SDK's Context class does.
Builder::sign has two kinds of overloads: those that take an explicit Signer argument, and those that use the signer stored in the Builder's Context.
Pass a Signer directly when you create signers at runtime or use different signers for different signing operations:
c2pa::Signer signer("es256", certs, key, tsa_url);
// Stream-based (preferred)
std::fstream dest("output.jpg", std::ios::in | std::ios::out | std::ios::binary);
builder.sign("image/jpeg", source, dest, signer);
// File-based
builder.sign("source.jpg", "output.jpg", signer);If a signer is configured in the Context (through settings JSON or ContextBuilder::with_signer()), you can call sign without a Signer argument. The context's signer is used automatically. If both a programmatic signer (via with_signer()) and a settings-based signer exist, the programmatic signer takes priority.
c2pa::Signer signer("es256", certs, key, tsa_url);
auto context = c2pa::Context::ContextBuilder()
.with_json(settings_json)
.with_signer(std::move(signer)) // signer is consumed here
.create_context();
c2pa::Builder builder(context, manifest_json);
// Stream-based (preferred)
std::fstream dest("output.jpg", std::ios::in | std::ios::out | std::ios::binary);
builder.sign("image/jpeg", source, dest);
// File-based
builder.sign("source.jpg", "output.jpg");This is useful when you want to configure signing once and reuse the same context across multiple builders without passing the signer to each sign call.
Deprecated:
std::ofstream out("output.jpg", std::ios::binary);
builder.sign("image/jpeg", source, out, signer);With context API:
std::fstream dest("output.jpg", std::ios::in | std::ios::out | std::ios::binary);
builder.sign("image/jpeg", source, dest, signer);