diff --git a/.gitignore b/.gitignore
index 0500d8a1..6fabebf6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,9 +8,8 @@
.docusaurus
.cache-loader
/docs/js-sdk/api
-/docs/js-sdk/examples/quickstart/*.ts
-/docs/js-sdk/examples/view-manifest/*.ts
-/docs/js-sdk/examples/view-manifest/*.html
+/docs/js-sdk/examples/*
+/docs/js-sdk/examples/*
/docs/c2patool/*.md
/docs/c2patool/docs/*.md
/docs/c2pa-node/*.md
@@ -35,4 +34,4 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
-.dccache
+.dccache
\ No newline at end of file
diff --git a/docs/manifest/manifest-tasks.mdx b/docs/manifest/manifest-tasks.mdx
deleted file mode 100644
index d24fb6a9..00000000
--- a/docs/manifest/manifest-tasks.mdx
+++ /dev/null
@@ -1,97 +0,0 @@
----
-id: tasks
-title: Common tasks working with manifest data
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-## Reading a manifest
-
-
-
-
- This is how to read a manifest using JavaScript.
-
-
-
- This is how to read a manifest using Python.
-
-
-
- This is how to read a manifest using C++. Do we want also want C?
-
-
-
- This is how to read a manifest using Node.js.
-
-
-
-## Getting resources from a manifest
-
-
-
- This is how to get resources from a manifest using JavaScript.
-
-
-
- This is how to get resources from a manifest using Python.
-
-
-
- This is how to get resources from a manifest using C++.
-
-
-
- This is how to get resources from a manifest using Node.js.
-
-
-
-## Building a manifest
-
-
-
-
- This is how to build a manifest using Python.
-
-
-
- This is how to build a manifest using C++.
-
-
-
- This is how to build a manifest using Node.js.
-
-
-
-## Writing a manifest
-
-
-
- This is how to write a manifest using Python.
-
-
-
- This is how to write a manifest using C++.
-
-
-
- This is how to write a manifest using Node.js.
-
-
-
-## Signing a manifest
-
-
-
- This is how to sign a manifest using Python.
-
-
-
- This is how to sign a manifest using C++.
-
-
-
- This is how to sign a manifest using Node.js.
-
-
diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx
new file mode 100644
index 00000000..2d4d0e3c
--- /dev/null
+++ b/docs/tasks/build.mdx
@@ -0,0 +1,50 @@
+---
+id: build
+title: Attaching and signing a manifest
+hide_table_of_contents: true
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+import PythonBuild from './includes/_python-build.md';
+/*
+import NodeBuild from './includes/_node-build.md';
+*/
+import NodeWIP from './includes/_node-wip.md';
+import CppBuild from './includes/_cpp-build.md';
+import RustBuild from './includes/_rust-build.md';
+
+
+
+
+
+You can't currently attach a manifest to an asset and sign the claim using the JavaScript library. You need to use a language that runs on the "back-end," such as Python, Node.js, C++, or Rust.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx
new file mode 100644
index 00000000..cb55f1f8
--- /dev/null
+++ b/docs/tasks/get-resources.mdx
@@ -0,0 +1,52 @@
+---
+id: get-resources
+title: Getting resources from a manifest
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+import JSGetResources from './includes/_js-get-resources.md';
+import PythonGetResources from './includes/_python-get-resources.md';
+/*
+import NodeGetResources from './includes/_node-get-resources.md';
+*/
+import NodeWIP from './includes/_node-wip.md';
+import CppGetResources from './includes/_cpp-get-resources.md';
+import RustGetResources from './includes/_rust-get-resources.md';
+
+Manifest data can include binary resources such as thumbnail and icon images which are referenced by JUMBF URIs in manifest data.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/tasks/includes/_cpp-build.md b/docs/tasks/includes/_cpp-build.md
new file mode 100644
index 00000000..04f27f6e
--- /dev/null
+++ b/docs/tasks/includes/_cpp-build.md
@@ -0,0 +1,29 @@
+This is an example of how to assign a manifest to an asset and sign the claim using C++:
+
+```cpp
+const std::string manifest_json = R"{
+ "claim_generator": "c2pa_c_test/0.1",
+ "claim_generator_info": [
+ {
+ "name": "c2pa-c test",
+ "version": "0.1"
+ }
+ ],
+ "assertions": [
+ {
+ "label": "c2pa.training-mining",
+ "data": {
+ "entries": {
+ "c2pa.ai_generative_training": { "use": "notAllowed" },
+ "c2pa.ai_inference": { "use": "notAllowed" },
+ "c2pa.ai_training": { "use": "notAllowed" },
+ "c2pa.data_mining": { "use": "notAllowed" }
+ }
+ }
+ }
+ ]
+ };
+
+auto builder = Builder(manifest_json);
+
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_cpp-get-resources.md b/docs/tasks/includes/_cpp-get-resources.md
new file mode 100644
index 00000000..85fa94ed
--- /dev/null
+++ b/docs/tasks/includes/_cpp-get-resources.md
@@ -0,0 +1,72 @@
+This is how to get resources from a manifest using C++.
+
+```cpp
+string read_text_file(const fs::path &path)
+{
+ ifstream file(path);
+ if (!file.is_open())
+ {
+ throw runtime_error("Could not open file " + string(path));
+ }
+ string contents((istreambuf_iterator(file)), istreambuf_iterator());
+ file.close();
+ return contents.data();
+}
+
+int main()
+{
+ fs::path manifest_path = current_dir / "../tests/fixtures/training.json";
+ //fs::path certs_path = current_dir / "../tests/fixtures/es256_certs.pem";
+ //fs::path image_path = current_dir / "../tests/fixtures/A.jpg";
+ fs::path output_path = current_dir / "../target/example/training.jpg";
+ fs::path thumbnail_path = current_dir / "../target/example/thumbnail.jpg";
+
+ try
+ {
+ // load the manifest, certs, and private key
+ /* Commenting out, because not part of resource reading
+ string manifest_json = read_text_file(manifest_path).data();
+
+ string certs = read_text_file(certs_path).data();
+
+ // create a signer
+ Signer signer = Signer(&test_signer, Es256, certs, "http://timestamp.digicert.com");
+
+ auto builder = Builder(manifest_json);
+ auto manifest_data = builder.sign(image_path, output_path, signer);
+ */
+
+ // read the new manifest and display the JSON
+ auto reader = Reader(output_path);
+
+ auto manifest_store_json = reader.json();
+ cout << "The new manifest is " << manifest_store_json << endl;
+
+ // get the active manifest
+ json manifest_store = json::parse(manifest_store_json);
+ if (manifest_store.contains("active_manifest"))
+ {
+ string active_manifest = manifest_store["active_manifest"];
+ json &manifest = manifest_store["manifests"][active_manifest];
+
+ string identifer = manifest["thumbnail"]["identifier"];
+
+ reader.get_resource(identifer, thumbnail_path);
+
+ cout << "thumbnail written to" << thumbnail_path << endl;
+ }
+ }
+ catch (c2pa::Exception const &e)
+ {
+ cout << "C2PA Error: " << e.what() << endl;
+ }
+ catch (runtime_error const &e)
+ {
+ cout << "setup error" << e.what() << endl;
+ }
+ catch (json::parse_error const &e)
+ {
+ cout << "parse error " << e.what() << endl;
+ }
+}
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_cpp-read.md b/docs/tasks/includes/_cpp-read.md
new file mode 100644
index 00000000..95dac314
--- /dev/null
+++ b/docs/tasks/includes/_cpp-read.md
@@ -0,0 +1,17 @@
+
+Use the `read_file` function to read C2PA data from the specified file. This function examines the specified asset file for C2PA data and returns a JSON report if it finds any; it throws exceptions on errors. If there are validation errors, the report includes a `validation_status` field.
+
+```cpp
+auto json_store = C2pa::read_file("", "")
+```
+
+Where:
+
+- ``- The asset file to read.
+- `` - Optional path to data output directory; If provided, the function extracts any binary resources, such as thumbnails, icons, and C2PA data into that directory. These files are referenced by the identifier fields in the manifest store report.
+
+For example:
+
+```cpp
+auto json_store = C2pa::read_file("work/media_file.jpg", "output/data_dir")
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_cpp-setup.md b/docs/tasks/includes/_cpp-setup.md
new file mode 100644
index 00000000..c0c6853a
--- /dev/null
+++ b/docs/tasks/includes/_cpp-setup.md
@@ -0,0 +1,21 @@
+This is how to set up your code to use the C++ library:
+
+```cpp
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "c2pa.hpp"
+#include "test_signer.hpp"
+#include
+
+// this example uses nlohmann json for parsing the manifest
+using json = nlohmann::json;
+using namespace std;
+namespace fs = std::filesystem;
+using namespace c2pa;
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_js-get-resources.md b/docs/tasks/includes/_js-get-resources.md
new file mode 100644
index 00000000..4dd02de4
--- /dev/null
+++ b/docs/tasks/includes/_js-get-resources.md
@@ -0,0 +1,67 @@
+:::note
+That JavaScript library is being extensively revised so the APIs used here may change in the near future.
+:::
+
+The example below shows how to get resources from manifest data using the JavaScript library.
+
+```js
+import { createC2pa, selectProducer } from 'c2pa';
+import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url';
+import workerSrc from 'c2pa/dist/c2pa.worker.js?url';
+import { parseISO } from 'date-fns';
+
+const sampleImage =
+ 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg';
+
+(async () => {
+ let output: string[] = [];
+
+ const c2pa = await createC2pa({
+ wasmSrc,
+ workerSrc,
+ });
+
+ const { manifestStore, source } = await c2pa.read(sampleImage);
+ const activeManifest = manifestStore?.activeManifest;
+ if (activeManifest) {
+ // Get thumbnail
+ // Note: You would normally call `dispose()` when working with a
+ // component-based UI library (e.g. on component un-mount)
+ // @ts-expect-error noUnusedLocals
+ const { url, dispose } = source.thumbnail.getUrl();
+
+ // Get properties
+ const properties: Record = {
+ title: activeManifest.title,
+ format: activeManifest.format,
+ claimGenerator: activeManifest.claimGenerator.split('(')[0]?.trim(),
+ producer: selectProducer(activeManifest)?.name ?? 'Unknown',
+ thumbnail: `
`,
+ ingredients: (activeManifest.ingredients ?? [])
+ .map((i) => i.title)
+ .join(', '),
+ signatureIssuer: activeManifest.signatureInfo?.issuer,
+ signatureDate: activeManifest.signatureInfo?.time
+ ? parseISO(activeManifest.signatureInfo.time).toString()
+ : 'No date available',
+ };
+
+ output = Object.keys(properties).map((key) => {
+ return `
+
+ ${key} |
+ ${properties[key]} |
+
+ `;
+ });
+ } else {
+ output.push(`
+
+ No provenance data found |
+
+ `);
+ }
+
+ document.querySelector('#results tbody')!.innerHTML = output.join('');
+})();
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_js-read.md b/docs/tasks/includes/_js-read.md
new file mode 100644
index 00000000..7ddf4d98
--- /dev/null
+++ b/docs/tasks/includes/_js-read.md
@@ -0,0 +1,24 @@
+:::note
+That JavaScript library is being extensively revised so the APIs used here may change in the near future.
+:::
+
+Use [`c2pa.read`](../../docs/js-sdk/api/c2pa.c2pa#methods) to read manifest data from an asset; if the asset has a C2PA manifest and was processed without errors, the returned [`c2paReadResult`](../../docs/js-sdk/api/c2pa.c2pareadresult) contains a [`manifestStore`](../../docs/js-sdk/api/c2pa.c2pareadresult.manifeststore) object with several useful properties:
+
+- **manifests**: An object containing all the asset's manifests ([`Manifest`](../../docs/js-sdk/api/c2pa.manifest) objects), keyed by UUID.
+- **activeManifest**: A pointer to the latest [`manifest`](../../docs/js-sdk/api/c2pa.manifest) in the manifest store. Effectively the "parent" manifest, this is the likely starting point when inspecting an asset's C2PA data.
+- **validationStatus**: A list of any validation errors encountered. See [Validation](../../docs/js-sdk/guides/validation) for more information.
+
+```js
+ try {
+ // Read in image and get a manifest store
+ const { manifestStore } = await c2pa.read(sampleImage);
+ console.log('manifestStore', manifestStore);
+
+ // Get the active manifest
+ const activeManifest = manifestStore?.activeManifest;
+ console.log('activeManifest', activeManifest);
+ } catch (err) {
+ console.error('Error reading image:', err);
+ }
+})();
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_js-setup.md b/docs/tasks/includes/_js-setup.md
new file mode 100644
index 00000000..56885a08
--- /dev/null
+++ b/docs/tasks/includes/_js-setup.md
@@ -0,0 +1,52 @@
+:::note
+That JavaScript library is being extensively revised so the APIs used here may change in the near future.
+:::
+
+This is how to set up your code to use the JavaScript library:
+
+```js
+const version = '0.27.1';
+const sampleImage = '';
+
+import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm';
+
+(async () => {
+ // Initialize the c2pa-js SDK
+ const c2pa = await createC2pa({
+ wasmSrc:
+ 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/assets/wasm/toolkit_bg.wasm',
+ workerSrc:
+ 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js',
+ });
+
+ ...
+
+})();
+```
+
+If you installed the package locally, for example from npm, then it's simply:
+
+```js
+import { createC2pa } from 'c2pa';
+```
+
+Additionally, the `wasmSrc` and `workerSrc` objects need to available as static assets that can be fetched at runtime. The best way to do that depends on the build system. For example, using [Vite](https://vite.dev/guide/assets#explicit-url-imports), as shown in the [minimal-ts-vite example](https://github.com/contentauth/c2pa-js/blob/main/examples/minimal-ts-vite/examples/active-manifest/main.ts):
+
+
+```js
+import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url';
+import workerSrc from 'c2pa/dist/c2pa.worker.js?url';
+import { parseISO } from 'date-fns';
+
+(async () => {
+ let output: string[] = [];
+
+ const c2pa = await createC2pa({
+ wasmSrc,
+ workerSrc,
+ });
+
+ ...
+
+})();
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_node-build.md b/docs/tasks/includes/_node-build.md
new file mode 100644
index 00000000..72d1e2e5
--- /dev/null
+++ b/docs/tasks/includes/_node-build.md
@@ -0,0 +1,170 @@
+This is an example of how to assign a manifest to an asset and sign the claim using Node.js:
+
+```ts
+import { ManifestBuilder } from 'c2pa-node';
+
+const manifest = new ManifestBuilder({
+ claim_generator: 'my-app/1.0.0',
+ format: 'image/jpeg',
+ title: 'node_test_local_signer.jpg',
+ assertions: [
+ {
+ label: 'c2pa.actions',
+ data: {
+ actions: [
+ {
+ action: 'c2pa.created',
+ },
+ ],
+ },
+ },
+ {
+ label: 'com.custom.my-assertion',
+ data: {
+ description: 'My custom test assertion',
+ version: '1.0.0',
+ },
+ },
+ ],
+});
+```
+
+Use the `c2pa.sign()` method to sign an ingredient, either locally if you have a signing certificate and key available, or by using a remote signing API.
+
+## Signing a stream
+
+If you have an asset file's data loaded into a stream, you can use it to sign the asset
+
+**NOTE**: Signing using a stream is currently supported only for `image/jpeg` and `image/png` data. For all other file types, use the [file-based approach](#signing-files) .
+
+```ts
+import { readFile } from 'node:fs/promises';
+import { createC2pa, createTestSigner } from 'c2pa-node';
+
+// read an asset into a buffer
+const buffer = await readFile('to-be-signed.jpg');
+const asset: Asset = { buffer, mimeType: 'image/jpeg' };
+
+// build a manifest to use for signing
+const manifest = new ManifestBuilder(
+ {
+ claim_generator: 'my-app/1.0.0',
+ format: 'image/jpeg',
+ title: 'buffer_signer.jpg',
+ assertions: [
+ {
+ label: 'c2pa.actions',
+ data: {
+ actions: [
+ {
+ action: 'c2pa.created',
+ },
+ ],
+ },
+ },
+ {
+ label: 'com.custom.my-assertion',
+ data: {
+ description: 'My custom test assertion',
+ version: '1.0.0',
+ },
+ },
+ ],
+ },
+ { vendor: 'cai' },
+);
+
+// create a signing function
+async function sign(asset, manifest) {
+ const signer = await createTestSigner();
+ const c2pa = createC2pa({
+ signer,
+ });
+
+ const { signedAsset, signedManifest } = await c2pa.sign({
+ asset,
+ manifest,
+ });
+}
+
+// sign
+await sign(asset, manifest);
+```
+
+**Remote signing**
+
+If you have access to a web service that performs signing, you can use it to sign remotely; for example:
+
+```ts
+import { readFile } from 'node:fs/promises';
+import { fetch, Headers } from 'node-fetch';
+import { createC2pa, SigningAlgorithm } from 'c2pa-node';
+
+function createRemoteSigner() {
+ return {
+ type: 'remote',
+ async reserveSize() {
+ const url = `https://my.signing.service/box-size`;
+ const res = await fetch(url);
+ const data = (await res.json()) as { boxSize: number };
+ return data.boxSize;
+ },
+ async sign({ reserveSize, toBeSigned }) {
+ const url = `https://my.signing.service/sign?boxSize=${reserveSize}`;
+ const res = await fetch(url, {
+ method: 'POST',
+ headers: new Headers({
+ 'Content-Type': 'application/octet-stream',
+ }),
+ body: toBeSigned,
+ });
+ return res.buffer();
+ },
+ };
+}
+
+async function sign(asset, manifest) {
+ const signer = createRemoteSigner();
+ const c2pa = createC2pa({
+ signer,
+ });
+
+ const { signedAsset, signedManifest } = await c2pa.sign({
+ asset,
+ manifest,
+ });
+}
+
+const buffer = await readFile('to-be-signed.jpg');
+const asset: Asset = { buffer, mimeType: 'image/jpeg' };
+
+const manifest = new ManifestBuilder(
+ {
+ claim_generator: 'my-app/1.0.0',
+ format: 'image/jpeg',
+ title: 'buffer_signer.jpg',
+ assertions: [
+ {
+ label: 'c2pa.actions',
+ data: {
+ actions: [
+ {
+ action: 'c2pa.created',
+ },
+ ],
+ },
+ },
+ {
+ label: 'com.custom.my-assertion',
+ data: {
+ description: 'My custom test assertion',
+ version: '1.0.0',
+ },
+ },
+ ],
+ },
+ { vendor: 'cai' },
+);
+
+await sign(asset, manifest);
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_node-get-resources.md b/docs/tasks/includes/_node-get-resources.md
new file mode 100644
index 00000000..422062ba
--- /dev/null
+++ b/docs/tasks/includes/_node-get-resources.md
@@ -0,0 +1,5 @@
+The example below shows how to get resources from manifest data using the Node.js library.
+
+```js
+// TBD
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_node-read.md b/docs/tasks/includes/_node-read.md
new file mode 100644
index 00000000..9c1a1973
--- /dev/null
+++ b/docs/tasks/includes/_node-read.md
@@ -0,0 +1,24 @@
+
+Use the `c2pa.read()` function to read a manifest; for example:
+
+```ts
+import { createC2pa } from 'c2pa-node';
+import { readFile } from 'node:fs/promises';
+
+const c2pa = createC2pa();
+
+async function read(path, mimeType) {
+ const buffer = await readFile(path);
+ const result = await c2pa.read({ buffer, mimeType });
+
+ if (result) {
+ const { active_manifest, manifests, validation_status } = result;
+ console.log(active_manifest);
+ } else {
+ console.log('No claim found');
+ }
+ // If there are no validation errors, then validation_status will be an empty array
+}
+
+await read('my-c2pa-file.jpg', 'image/jpeg');
+```
diff --git a/docs/tasks/includes/_node-setup.md b/docs/tasks/includes/_node-setup.md
new file mode 100644
index 00000000..763985f6
--- /dev/null
+++ b/docs/tasks/includes/_node-setup.md
@@ -0,0 +1,16 @@
+This is how to set up your code to use the Node.js library.
+
+```js
+import { createC2pa } from 'c2pa-node';
+import { readFile } from 'node:fs/promises';
+
+const c2pa = createC2pa();
+```
+
+
\ No newline at end of file
diff --git a/docs/tasks/includes/_node-wip.md b/docs/tasks/includes/_node-wip.md
new file mode 100644
index 00000000..ca2cd717
--- /dev/null
+++ b/docs/tasks/includes/_node-wip.md
@@ -0,0 +1,3 @@
+:::note
+The Node.js library is being revised. The documentation will be updated as soon as possible with the latest changes.
+:::
\ No newline at end of file
diff --git a/docs/tasks/includes/_python-build.md b/docs/tasks/includes/_python-build.md
new file mode 100644
index 00000000..19338a0b
--- /dev/null
+++ b/docs/tasks/includes/_python-build.md
@@ -0,0 +1,61 @@
+
+This is an example of how to assign a manifest to an asset and sign the claim using Python.
+
+Use a `Builder` object to add a manifest to an asset.
+
+```python
+try:
+ # Define a function to sign the claim bytes.
+ # In this case we are using a pre-defined sign_ps256 method, passing in our private cert.
+ # Normally this cert would be kept safe in some other location.
+ def private_sign(data: bytes) -> bytes:
+ return sign_ps256(data, "tests/fixtures/ps256.pem")
+
+ # Read our public certs into memory.
+ certs = open(data_dir + "ps256.pub", "rb").read()
+
+ # Create a signer from the private signer, certs and a time stamp service URL.
+ signer = create_signer(private_sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com")
+
+ # Create a builder add a thumbnail resource and an ingredient file.
+ builder = Builder(manifest_json)
+
+ # Add the resource from a stream.
+ a_thumbnail_jpg_stream = open("tests/fixtures/A_thumbnail.jpg", "rb")
+ builder.add_resource("image/jpeg", a_thumbnail_jpg_stream)
+
+ # Add the resource from a file.
+ # The URI provided here, "thumbnail", must match an identifier in the manifest definition.
+ builder.add_resource_file("thumbnail", "tests/fixtures/A_thumbnail.jpg")
+
+ # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail.
+ ingredient_json = {
+ "title": "A.jpg",
+ "relationship": "parentOf", # "parentOf", "componentOf" or "inputTo"
+ "thumbnail": {
+ "identifier": "thumbnail",
+ "format": "image/jpeg"
+ }
+ }
+
+ # Add the ingredient from a stream.
+ a_jpg_stream = open("tests/fixtures/A.jpg", "rb")
+ builder.add_ingredient("image/jpeg", a_jpg_stream)
+
+ # At this point archive or unarchive Builder to continue later.
+ # This example uses a bytearray for the archive stream.
+ # All ingredients and resources are saved in the archive.
+ archive = io.BytesIO(bytearray())
+ builder.to_archive(archive)
+ archive.seek()
+ builder = builder.from_archive(archive)
+
+ # Sign the builder with a stream and output it to a stream.
+ # This returns the binary manifest data that could be uploaded to cloud storage.
+ input_stream = open("tests/fixtures/A.jpg", "rb")
+ output_stream = open("target/out.jpg", "wb")
+ c2pa_data = builder.sign(signer, "image/jpeg", input_stream, output_stream)
+
+except Exception as err:
+ print(err)
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_python-get-resources.md b/docs/tasks/includes/_python-get-resources.md
new file mode 100644
index 00000000..f2200c4c
--- /dev/null
+++ b/docs/tasks/includes/_python-get-resources.md
@@ -0,0 +1,23 @@
+
+The example below shows how to get resources from manifest data using the Python library.
+
+Retrieve binary resources such as thumbnails from the manifest data, use the `resource_to_stream` or `resource_to_file` methods using the associated `identifier` field values and a `uri`.
+
+_NOTE: Need to add example of using `reader.resource_to_stream()`._
+
+```py
+try:
+ # Create a reader from a file path.
+ reader = c2pa.Reader.from_file("path/to/media_file.jpg")
+
+ # Get the active manifest.
+ manifest = reader.get_active_manifest()
+ if manifest != None:
+
+ # get the uri to the manifest's thumbnail and write it to a file.
+ uri = manifest["thumbnail"]["identifier"]
+ reader.resource_to_file(uri, "thumbnail_v2.jpg")
+
+except Exception as err:
+ print(err)
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_python-read.md b/docs/tasks/includes/_python-read.md
new file mode 100644
index 00000000..4020ef6e
--- /dev/null
+++ b/docs/tasks/includes/_python-read.md
@@ -0,0 +1,22 @@
+
+Use the `Reader` object to read manifest data from a file or stream and perform validation on the manifest store.
+
+Use the `json()` method to return a JSON manifest report; If there are validation errors, the report includes a `validation_status` field.
+
+An asset file may contain many manifests in a manifest store. The most recent manifest is identified by the value of the `active_manifest` field in the manifests map.
+
+```py
+try:
+ # Create a reader from a file path.
+ reader = c2pa.Reader.from_file("path/to/media_file.jpg")
+
+ # Print the JSON for a manifest.
+ print("manifest store:", reader.json())
+
+except Exception as err:
+ print(err)
+```
+
+
\ No newline at end of file
diff --git a/docs/tasks/includes/_python-setup.md b/docs/tasks/includes/_python-setup.md
new file mode 100644
index 00000000..01388bfa
--- /dev/null
+++ b/docs/tasks/includes/_python-setup.md
@@ -0,0 +1,21 @@
+This is how to set up your code to use the Python library.
+
+```python
+# Import the C2PA Python package.
+from c2pa import *
+
+# Import standard general-purpose packages.
+import os
+import io
+import logging
+import json
+import base64
+
+# Import web packages used in example implementation.
+from flask import Flask, request, abort
+from flask_cors import CORS
+from waitress import serve
+
+# Import AWS SDK package (to use KMS).
+import boto3
+```
diff --git a/docs/tasks/includes/_rust-build.md b/docs/tasks/includes/_rust-build.md
new file mode 100644
index 00000000..4a569420
--- /dev/null
+++ b/docs/tasks/includes/_rust-build.md
@@ -0,0 +1,48 @@
+This is an example of how to assign a manifest to an asset and sign the claim using Rust.
+
+This example is from [`c2pa-rs/sdk/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L88C5-L134C1):
+
+```rust
+let json = manifest_def(title, format);
+
+let mut builder = Builder::from_json(&json)?;
+builder.add_ingredient_from_stream(
+ json!({
+ "title": parent_name,
+ "relationship": "parentOf"
+ })
+ .to_string(),
+ format,
+ &mut source,
+)?;
+
+let thumb_uri = builder
+ .definition
+ .thumbnail
+ .as_ref()
+ .map(|t| t.identifier.clone());
+
+// add a manifest thumbnail ( just reuse the image for now )
+if let Some(uri) = thumb_uri {
+ if !uri.starts_with("self#jumbf") {
+ source.rewind()?;
+ builder.add_resource(&uri, &mut source)?;
+ }
+}
+
+// write the manifest builder to a zipped stream
+let mut zipped = Cursor::new(Vec::new());
+builder.to_archive(&mut zipped)?;
+
+// unzip the manifest builder from the zipped stream
+zipped.rewind()?;
+
+let ed_signer =
+ |_context: *const (), data: &[u8]| CallbackSigner::ed25519_sign(data, PRIVATE_KEY);
+let signer = CallbackSigner::new(ed_signer, SigningAlg::Ed25519, CERTS);
+
+let mut builder = Builder::from_archive(&mut zipped)?;
+// sign the ManifestStoreBuilder and write it to the output stream
+let mut dest = Cursor::new(Vec::new());
+builder.sign(&signer, format, &mut source, &mut dest)?;
+```
diff --git a/docs/tasks/includes/_rust-get-resources.md b/docs/tasks/includes/_rust-get-resources.md
new file mode 100644
index 00000000..52347b94
--- /dev/null
+++ b/docs/tasks/includes/_rust-get-resources.md
@@ -0,0 +1,33 @@
+The example below shows how to get resources from manifest data using the Rust library.
+
+_NOTE: Need to clarify if/how these two code examples work together._
+
+This is from [`resource_to_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.resource_to_stream) API doc:
+
+```rust
+use c2pa::Reader;
+let stream = std::io::Cursor::new(Vec::new());
+let reader = Reader::from_file("path/to/file.jpg").unwrap();
+let manifest = reader.active_manifest().unwrap();
+let uri = &manifest.thumbnail_ref().unwrap().identifier;
+let bytes_written = reader.resource_to_stream(uri, stream).unwrap();
+```
+
+This is from [`c2pa-rs/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L138):
+
+```rust
+let reader = Reader::from_stream(format, &mut dest)?;
+
+// extract a thumbnail image from the ManifestStore
+let mut thumbnail = Cursor::new(Vec::new());
+if let Some(manifest) = reader.active_manifest() {
+ if let Some(thumbnail_ref) = manifest.thumbnail_ref() {
+ reader.resource_to_stream(&thumbnail_ref.identifier, &mut thumbnail)?;
+ println!(
+ "wrote thumbnail {} of size {}",
+ thumbnail_ref.format,
+ thumbnail.get_ref().len()
+ );
+ }
+}
+```
\ No newline at end of file
diff --git a/docs/tasks/includes/_rust-read.md b/docs/tasks/includes/_rust-read.md
new file mode 100644
index 00000000..d44ea756
--- /dev/null
+++ b/docs/tasks/includes/_rust-read.md
@@ -0,0 +1,28 @@
+
+Use the [`Reader`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html) struct to read manifest data from a file or stream.
+
+### Reading from a file
+
+Use [`from_file`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_file) to read manifest data from a file:
+
+```rust
+use c2pa::Reader;
+let reader = Reader::from_file("path/to/file.jpg").unwrap();
+```
+
+There is also an asynchronous version of this method, [`from_stream_async`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_file_async).
+
+### Reading from a stream
+
+Use [`from_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_stream) to read manifest data from a stream:
+
+```rust
+use std::io::Cursor;
+use c2pa::Reader;
+
+let mut stream = Cursor::new(include_bytes!("../tests/fixtures/CA.jpg"));
+let reader = Reader::from_stream("image/jpeg", stream).unwrap();
+println!("{}", reader.json());
+```
+
+There is also an asynchronous version of this method, [`from_stream_async`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_stream_async).
diff --git a/docs/tasks/includes/_rust-setup.md b/docs/tasks/includes/_rust-setup.md
new file mode 100644
index 00000000..017826cb
--- /dev/null
+++ b/docs/tasks/includes/_rust-setup.md
@@ -0,0 +1,21 @@
+This is how to setup your code to use the Rust library.
+
+:::tip
+The files used in this example are in the C2PA Rust library [`sdk/tests/fixtures`](https://github.com/contentauth/c2pa-rs/tree/main/sdk/tests/fixtures) directory.
+:::
+
+```rust
+use std::{
+ io::{Cursor, Write},
+ process::{Command, Stdio},
+};
+
+use anyhow::Result;
+use c2pa::{settings::load_settings_from_str, Builder, CallbackSigner, Reader};
+use c2pa_crypto::raw_signature::SigningAlg;
+use serde_json::json;
+
+const TEST_IMAGE: &[u8] = include_bytes!("../tests/fixtures/CA.jpg");
+const CERTS: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pub");
+const PRIVATE_KEY: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pem");
+```
\ No newline at end of file
diff --git a/docs/tasks/index.md b/docs/tasks/index.md
new file mode 100644
index 00000000..bedb3c62
--- /dev/null
+++ b/docs/tasks/index.md
@@ -0,0 +1,12 @@
+---
+id: working-manifests
+title: Working with manifests
+---
+
+There are a number of common tasks when working with manifests.
+The way you accomplish each task is specific to the language you're using, although at a high level the process is similar.
+
+- [Preliminary setup](./setup.mdx)
+- [Reading manifest data](./read.mdx)
+- [Getting resources from a manifest](./get-resources.mdx)
+- [Attaching a manifest to an asset and signing it](./build.mdx)
diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx
new file mode 100644
index 00000000..9c20c7bf
--- /dev/null
+++ b/docs/tasks/read.mdx
@@ -0,0 +1,51 @@
+---
+id: read
+title: Reading manifest data
+hide_table_of_contents: true
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+import JSRead from './includes/_js-read.md';
+import PythonRead from './includes/_python-read.md';
+/*
+import NodeRead from './includes/_node-read.md';
+*/
+import NodeWIP from './includes/_node-wip.md';
+import CppRead from './includes/_cpp-read.md';
+import RustRead from './includes/_rust-read.md';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/tasks/setup.mdx b/docs/tasks/setup.mdx
new file mode 100644
index 00000000..b272ae94
--- /dev/null
+++ b/docs/tasks/setup.mdx
@@ -0,0 +1,50 @@
+---
+id: setup
+title: Setup
+hide_table_of_contents: true
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import JSSetup from './includes/_js-setup.md';
+import PythonSetup from './includes/_python-setup.md';
+/*
+import NodeSetup from './includes/_node-setup.md';
+*/
+import NodeWIP from './includes/_node-wip.md';
+import CppSetup from './includes/_cpp-setup.md';
+import RustSetup from './includes/_rust-setup.md';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docusaurus.config.js b/docusaurus.config.js
index d60197ec..44f6af07 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -140,6 +140,7 @@ const config = {
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
+ additionalLanguages: ['rust'],
},
algolia: {
// The application ID provided by Algolia
diff --git a/sidebars.js b/sidebars.js
index 168ed59b..943562e7 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -61,6 +61,31 @@ const sidebars = {
],
},
+ {
+ type: 'category',
+ label: 'Working with manifests',
+ link: { type: 'doc', id: 'tasks/working-manifests' },
+ collapsed: true,
+ items: [
+ {
+ type: 'doc',
+ id: 'tasks/setup',
+ },
+ {
+ type: 'doc',
+ id: 'tasks/read',
+ },
+ {
+ type: 'doc',
+ id: 'tasks/get-resources',
+ },
+ {
+ type: 'doc',
+ id: 'tasks/build',
+ },
+ ],
+ },
+
{
type: 'category',
label: 'C2PA Tool',