Aqua ProtocolAqua Protocol
Aqua Protocol
Aqua ProtocolAqua Protocol, an open protocol for verifiable data provenance and accountability. Developed in the open as a proposed standard, with public specification, documentation, and reference implementations.

Documentation

Welcome to Aqua ProtocolQuick StartCore ConceptsRelease Status & Versioning
Developer Onboarding
aqua-rs-sdk-coreTemplate RegistryAudit Trails for AI Agents
Protocol Reference
Protocol ReferenceData ModelHashing and CanonicalizationTemplatesSignaturesAnchors and LinksSelective DisclosureVerification
Use Cases
Aqua Protocol Use CasesDocument VerificationIdentity AttestationSupply Chain Tracking

Documentation

Welcome to Aqua ProtocolQuick StartCore ConceptsRelease Status & Versioning
Developer Onboarding
aqua-rs-sdk-coreTemplate RegistryAudit Trails for AI Agents
Protocol Reference
Protocol ReferenceData ModelHashing and CanonicalizationTemplatesSignaturesAnchors and LinksSelective DisclosureVerification
Use Cases
Aqua Protocol Use CasesDocument VerificationIdentity AttestationSupply Chain Tracking
Docs
Quick Start

Quick Start

Create, sign, and verify your first Aqua tree in Rust with aqua-rs-sdk-core.

7 min read
Experimental release

Aqua Protocol v4 is under active development. The published components — aqua-rs-sdk-core and aqua-template-registry — are an early community release with a deliberately limited functionality scope. Version numbers have not yet been increased, backward compatibility is not yet provided, and breaking changes are expected and may occur without notice. The full aqua-rs-sdk, which extends this core, is scheduled for a later publication. For production use today, refer to the stable v3 documentation.

This guide goes from an empty directory to a verified Aqua tree in one small Rust program: create a genesis revision for a piece of content, sign it with an Ed25519 key, and run the full verification pipeline.

Prerequisites

  • A stable Rust toolchain (rustc and cargo; the crate uses edition 2021)
  • git — cargo fetches the SDK directly from GitHub

Create a project and add dependencies

Code
bash
1cargo new aqua-hello
2cd aqua-hello

Add the dependencies to Cargo.toml:

Code
toml
1[dependencies]
2aqua-rs-sdk-core = { git = "https://github.com/inblockio/aqua-rs-sdk-core" }
3tokio = { version = "1", features = ["full"] }
4hex = "0.4"

aqua-rs-sdk-core is not on crates.io yet (publication is planned), so the dependency points at the Git repository. tokio provides the async runtime for the signing and verification calls; hex decodes a signing key supplied as hex.

Write the program

Replace src/main.rs with:

Code
rust
1use aqua_rs_sdk_core::schema::{AquaTreeWrapper, FileData, SigningCredentials};
2use aqua_rs_sdk_core::Aquafier;
3use std::path::PathBuf;
4 
5#[tokio::main]
6async fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let aquafier = Aquafier::new();
8 
9 // 1. Create a genesis revision for some content.
10 let file = FileData::new(
11 "hello.txt".to_string(),
12 b"hello world".to_vec(),
13 PathBuf::from("hello.txt"),
14 );
15 let tree = aquafier.create_genesis_revision(file.clone(), None)?;
16 
17 // 2. Sign it with an Ed25519 key (a did:key identity).
18 // The fallback below is a fixed demo secret (the bytes 1..=32) so
19 // that this example runs out of the box. NEVER use a hard-coded or
20 // predictable key in production: generate a fresh one with
21 // aqua_rs_sdk_core::generate_ed25519() and keep it out of your
22 // source code.
23 let secret: Vec<u8> = std::env::var("MY_ED25519_SECRET_HEX")
24 .map(|h| hex::decode(h).expect("MY_ED25519_SECRET_HEX must be hex"))
25 .unwrap_or_else(|_| (1..=32).collect());
26 let creds = SigningCredentials::Did { did_key: secret };
27 let signed = aquafier
28 .sign_aqua_tree(AquaTreeWrapper::new(tree, None, None), &creds, None, None)
29 .await?;
30 
31 // 3. Verify the full pipeline: structure, hashes, schema, signature.
32 let result = aquafier
33 .verify_aqua_tree(
34 AquaTreeWrapper::new(signed.aqua_tree, Some(file.clone()), None),
35 vec![file],
36 )
37 .await?;
38 
39 println!("verified: {}", result.is_verified());
40 Ok(())
41}

The three calls mirror the protocol lifecycle: create_genesis_revision builds a new tree, sign_aqua_tree appends a signature revision attesting to the tree's tip, and verify_aqua_tree runs the complete verification pipeline over the result.

Run it

Code
bash
1cargo run

The first build fetches and compiles the SDK from GitHub. The program prints:

Code
text
1verified: true

true means the tree verified — is_verified() accepts both the verified and verified_with_warnings outcomes, and this program produces no warnings. Every check passed — structural validity, recomputed revision hashes, payload schema conformance, file content (checked by size and content hash, never by filename), and the Ed25519 signature — under the default strict verification policy.

The tree itself is a plain JSON document. The example below is abridged from one of the SDK's deterministic test fixtures (sign_did_example.aqua.json) and shows the same three revision kinds your program produced — a genesis anchor, a typed object revision, and a signature revision (hashes shortened with … for readability):

Code
json
1{
2 "revisions": {
3 "0x1620ce6f…b7ce0b": {
4 "revision_type": "0x1620479a…11e899",
5 "nonce": "0x1a5f4934…6b96d580",
6 "local_timestamp": 1783616147,
7 "version": "https://aqua-protocol.org/docs/v4/schema",
8 "method": "scalar",
9 "structural_links": [
10 "0x162000f3…a3fe53"
11 ]
12 },
13 "0x1620f791…d5ac28": {
14 "previous_revision": "0x1620ce6f…b7ce0b",
15 "revision_type": "0x162000f3…a3fe53",
16 "nonce": "0x4313fd67…e8ec226e",
17 "local_timestamp": 1783616148,
18 "version": "https://aqua-protocol.org/docs/v4/schema",
19 "method": "scalar",
20 "payloads": {
21 "type": "file",
22 "hash": "0xd87ca20f…04450b9d",
23 "hash_type": "FIPS_202-SHA3-256",
24 "descriptor": "",
25 "size": 12,
26 "content_type": "text/plain"
27 }
28 },
29 "0x16200ca0…803609": {
30 "previous_revision": "0x1620ce6f…b7ce0b",
31 "revision_type": "0x1620baf1…8b659a",
32 "nonce": "0x43b3b02c…ce008f2e",
33 "local_timestamp": 1783616148,
34 "version": "https://aqua-protocol.org/docs/v4/schema",
35 "method": "scalar",
36 "signer": "did:key:z6MkneMkZqwqRiU5mJzSG3kDwzt9P8C59N4NGTfBLfSGE7c7",
37 "signature": {
38 "signature_type": "ed25519",
39 "signature": "0xe52bceee…c170c20d",
40 "signature_public_identifier": "0x79b5562e…ad049664"
41 }
42 }
43 },
44 "file_index": {
45 "0x1620ce6f…b7ce0b": "anchor_0x1620ce",
46 "0x1620f791…d5ac28": "test.txt"
47 }
48}

Two differences in your run. First, the SDK hashes object revisions with the tree method by default, so your object revision also carries a leaves array of per-field digests — the fixture above was created with the scalar method, which has no leaves. Second, the fixture's signature revision targets the genesis anchor (its previous_revision names the anchor), while your program signed the tree's tip, so your signature's previous_revision names the object revision. To inspect your own tree, add serde_json = "1" to the dependencies and print serde_json::to_string_pretty(&signed.aqua_tree)? before the verification call.

What just happened

  • A genesis anchor declared the tree's type. create_genesis_revision built a two-revision tree: a genesis anchor whose structural_links name the built-in file template, followed by an object revision typed by that template. The object's payloads records the content hash, size, and MIME type of hello.txt — not the content itself.
  • A signature revision attested to a revision hash. sign_aqua_tree appended a signature revision branching off the tree's tip — in your program, the object revision. Its signer is the did:key identity derived from the Ed25519 key, and what it signs includes the target's revision hash — so the signature covers the target's entire content indirectly.
  • Every link is a multihash. Each revision is stored under the hash of its own canonical form, rendered as 0x + 68 hex characters. previous_revision and revision_type are such links, and the hash algorithm (SHA3-256 here, codec 0x16) travels inside the multihash — revisions carry no algorithm field.
  • revision_type is a template multihash, never a keyword. The anchor names the anchor_template, the object names the file template, and the signature names the signature_ed25519 template. Types are content hashes, not strings.
  • Verification recomputed everything. verify_aqua_tree resolved the anchor's structural links, recomputed every revision hash against its map key, validated the object's payload against the file template's JSON Schema, checked the file content by size and hash, and cryptographically verified the signature — failing closed on any mismatch.

Next steps

Core Concepts

The Aqua tree, the four revision kinds, templates, and verification — the mental model behind the three calls you just made

aqua-rs-sdk-core

The full aqua-rs-sdk-core API: typed objects, custom templates, exports, and selective disclosure

Template Registry

Publish and retrieve templates by publisher DID with aqua-template-registry

Audit Trails for AI Agents

Record AI-agent sessions as sequences of signed, individually verifiable artifacts

Protocol Reference

The readable reference for the v4 wire format, backed by the normative protocol specification

Troubleshooting

See also

  • Welcome — what Aqua Protocol v4 is and what is published today
  • Core Concepts — the data model behind this example
  • Release Status & Versioning — release status and scope of the v4 components
  • v3 documentation — the stable JavaScript SDK
  • aqua-rs-sdk-core on GitHub — source, examples, and the normative protocol specification
Edit this pageReport an issue
Previous
Welcome to Aqua Protocol
Next
Core Concepts

Documentation

  • Getting Started
  • Protocol Reference

Community

  • GitHub

Copyright © 2026 inblock.io assets GmbH. All rights reserved.

On this page

TroubleshootingSee also