aethel_vault/lib.rs
1//! # aethel-vault
2//!
3//! **aethel-vault** is the agent-held wallet: the crate is `aethel-vault`,
4//! the repository is `aethel-runtime`. Not to be confused with
5//! `pqc-privacy`'s unrelated AES-GCM + Reed-Solomon *shard* module
6//! (historically also called "vault" there — that is a research
7//! component; this crate owns the word "wallet").
8//!
9//! ## Two assets, not one (V-7)
10//!
11//! This crate holds and moves two entirely separate things, and never
12//! bridges between them:
13//!
14//! - **Confidential internal balance** (`fhe-state` feature,
15//! [`vault`]/[`client`]) — an `FheUint64` ciphertext the vault never
16//! decrypts, homomorphically transferred between agent-chosen ledger
17//! entries. It exists only in the agent's own storage (which may be a
18//! hostile host); privacy here is against *that storage*. **It is not a
19//! token, not on any chain, and not USDC.** Older docs in this crate
20//! described this as "on-chain" with "validator" processing — that
21//! language was wrong and has been corrected; there is no chain and no
22//! validator anywhere in this crate.
23//! - **Settlement asset** (`signer` feature, default,
24//! [`settlement`]) — USDC on Base, Ethereum, or Arbitrum One, moved by an
25//! EIP-3009 `transferWithAuthorization` signature the agent's *external*
26//! wallet produces. Public by nature; bounded by [`policy::SpendPolicy`].
27//!
28//! An FHE transfer never moves USDC, and an EIP-3009 authorization never
29//! changes an FHE balance unless the agent explicitly records it — there
30//! is no bridge in this crate. See [`vault`]'s module docs for more detail.
31//!
32//! ## Zero ECDSA / 100% PQC signer; secp256k1 lives in the agent's wallet
33//!
34//! This crate never implements, produces, or verifies a secp256k1 ECDSA
35//! signature. The [`settlement::SettlementSigner`] trait is the injection
36//! point: an agent's existing wallet (hardware, MPC, browser, HSM) signs
37//! the EIP-712 digest this crate builds. This crate's *own* signatures —
38//! spend intents, settlement receipts, wallet-bind attestations, HITL
39//! approvals — are all ML-DSA-65 via `aethel_core::signing::Identity`,
40//! under the purpose-separated contexts in `aethel_core::signing::purpose`.
41//! A hybrid (PQC + classical) posture, if wanted, belongs at SAGP (the
42//! gateway), not here. See [`settlement`]'s module docs for the full
43//! rationale and the locked rules it encodes.
44//!
45//! ## Custody rule: the ServerKey — and the settlement signer — never leave the agent
46//!
47//! - **ServerKey** (`fhe-state` feature): held only in a process-local,
48//! never-serialized runtime structure. It never appears in exported or
49//! persisted [`vault::VaultState`] bytes — see [`vault`]'s "Custody rule"
50//! docs (V-2).
51//! - **Settlement signer** (`signer` feature): the
52//! [`settlement::SettlementSigner`] trait is injected by the caller; no
53//! implementation ships in this crate except a test-only mock. This
54//! crate never holds a secp256k1 private key.
55//! - **There is no "hosted vault" mode.** Naming a Cargo feature `hosted`
56//! is a hard compile error (see below). Do not host `aethel-vault` on a
57//! third party's infrastructure; the agent runtime, or a wallet process
58//! the principal already runs, holds every secret this crate touches.
59//!
60//! ## Feature Flags
61//!
62//! - `signer` (**default**) — [`settlement`] (EIP-3009/x402 export),
63//! [`policy`] (standing spend policy), [`receipt`] (optional signed
64//! receipts). No `tfhe`, no HelixDB gRPC stack. This is the Wave-1
65//! "vault as signer of a normal USDC wallet" path.
66//! - `fhe-state` — the confidential internal ledger ([`vault`]'s
67//! `VaultState`/homomorphic transfer state machine, [`client`]'s
68//! `NativeVaultClient`). Pulls `tfhe`. Optional: a differentiator for
69//! agents storing state on a hostile host, not a Wave-1 gate.
70//! **Experimental.** Its core ledger operations have no real tests yet.
71//! - `helixdb` — the [`storage::helixdb`] gRPC adapter. Pulls `tokio`,
72//! `tonic`, `prost`, and runs `prost-build` in `build.rs`.
73//! **Experimental.** The adapter is stubs: every storage call returns
74//! `HelixError::NotImplemented`.
75//! - `std` (default) — standard library support. Does not, by itself,
76//! pull `tokio`/`tonic`/`prost` (that is `helixdb`'s job) or `tfhe`
77//! (that is `fhe-state`'s job).
78//! - `wasm` — `wasm-bindgen` exports for the confidential-ledger state
79//! machine and client.
80//! - `hosted` — **does not exist as a real feature.** Enabling it is a
81//! hard `compile_error!` (see below). This is the custody rule enforced
82//! structurally, not just documented.
83//!
84//! `default = ["std", "signer"]`: a plain `cargo add aethel-vault` builds
85//! the thin signer path only — no `tfhe`, no `tokio`, no `tonic`, no
86//! `prost` anywhere in the dependency graph (confirm with `cargo tree -e
87//! normal`).
88//!
89//! ## Modules
90//!
91//! - [`settlement`] — EIP-3009/x402 export (`signer`, default). `Chain`
92//! table, `TransferWithAuthorization`, `SettlementSigner` trait,
93//! `Wallet::authorize_eip3009`.
94//! - [`policy`] — standing spend policy (`signer`, default). `SpendPolicy`,
95//! `PolicyState`, `Rail`, `HitlApproval`.
96//! - [`receipt`] — optional signed settlement receipts (`signer`, default).
97//! - [`error`] — `VaultError`, the Rust-native error type for the
98//! `signer`-mode surface.
99//! - [`vault`] — the confidential-ledger state machine (`fhe-state`).
100//! Homomorphic balance management using `FheUint64` ciphertexts via
101//! `tfhe-rs`.
102//! - [`client`] — the confidential ledger's client SDK (`fhe-state`
103//! native, or `wasm` for JS).
104//! - [`storage`] — HelixDB storage adapter (`helixdb` feature only).
105//! - [`sdk`] — Client SDK module. TypeScript and Rust SDK utilities.
106//!
107//! ## Security Properties
108//!
109//! - **Zero Plaintext Leakage (confidential ledger)**: `fhe-state` balances
110//! exist solely as `FheUint64` ciphertexts in the agent's own storage;
111//! this crate never decrypts them.
112//! - **Post-Quantum Hardness**: the confidential ledger's security reduces
113//! to LWE hardness over the Torus; this crate's own signatures
114//! (spend intent, receipt, wallet bind, HITL approval) are ML-DSA-65.
115//! - **Ephemeral Vault IDs**: anonymous vault identifiers derived from
116//! Polymorphic Lattice Projections; no persistent on-chain footprint —
117//! and no on-chain footprint of any kind, since the confidential ledger
118//! is not on any chain.
119//! - **Policy before signature**: [`settlement::Wallet::authorize_eip3009`]
120//! checks [`policy::SpendPolicy`] before it ever calls the injected
121//! `SettlementSigner` or signs a `SpendIntent`. A denied or
122//! unsatisfied-HITL request produces no signature of any kind.
123//!
124//! ---
125//!
126//! ## The 0x307 crate family
127//!
128//! `aethel-vault` is one of six open-source crates from [0x307](https://0x307.com/crates), held to one
129//! audit and stability standard.
130//!
131//! | Crate | Tier | What it does |
132//! |---|---|---|
133//! | [pqc-sig](https://crates.io/crates/pqc-sig) | Production | ML-DSA, SLH-DSA and FN-DSA signatures (FIPS 204/205/206) |
134//! | [pqc-kem](https://crates.io/crates/pqc-kem) | Production | ML-KEM (FIPS 203), the X25519 + ML-KEM-768 hybrid, X-Wing, and sealed boxes |
135//! | [aethel-core](https://crates.io/crates/aethel-core) | Production | Post-quantum anonymous identity: a separate identifier per context, context-bound ML-DSA signing |
136//! | [aethel-sdk](https://crates.io/crates/aethel-sdk) | Preview | The SDK over aethel-core, and the place to start |
137//! | [aethel-vault](https://crates.io/crates/aethel-vault) (this crate) | Preview | Agent-held wallet: policy-gated x402 / EIP-3009 signing with ML-DSA-65 spend records |
138//! | [pqc-privacy](https://crates.io/crates/pqc-privacy) | Lab | Research bundle, kept off every identity and payment path |
139//!
140//! **Production** crates are thin, standards-bound libraries meant to be depended on today. **Preview** crates work and are published, with APIs still settling. **Lab** crates are research, never on an identity or payment path.
141//!
142//! **Runnable examples:** [0x307/examples](https://github.com/0x307/examples), one program per
143//! crate, pinned to the published versions.
144//!
145//! **Audit status:** None of these crates has been independently audited, and none holds a CMVP / FIPS 140-3 validation. "FIPS 203/204/205/206" means the algorithms follow those standards, not that the code is certified. Known issues for this crate are in
146//! [SECURITY.md](https://github.com/0x307/aethel-runtime/blob/main/SECURITY.md). Version policy:
147//! [STABILITY.md](https://github.com/0x307/aethel-runtime/blob/main/STABILITY.md).
148//!
149
150#![warn(missing_docs)]
151#![warn(clippy::all)]
152#![cfg_attr(not(feature = "std"), no_std)]
153
154// V-2: aethel-vault has no hosted mode, structurally. Naming this feature
155// is a hard compile error, not just a documented convention — see the
156// crate-level "Custody rule" docs above and `docs/TFHE-VAULT-SPEC.md`.
157#[cfg(feature = "hosted")]
158compile_error!(
159 "aethel-vault has no hosted mode: the ServerKey (fhe-state) and the settlement \
160 signer (signer) never leave the agent (SAGP-PG-001 V-2). Do not enable a \
161 `hosted` feature; there is nothing behind it and there never will be."
162);
163
164extern crate alloc;
165
166// ── WASM allocator + panic handler ────────────────────────────────────────────
167//
168// For wasm32-unknown-unknown without std, we need:
169// 1. A global allocator (wasm-bindgen provides one via its start function)
170// 2. A panic handler
171//
172// wasm-bindgen automatically provides both when it's linked in.
173// We just need to ensure the wasm-bindgen start function runs.
174
175// ── Module Declarations ───────────────────────────────────────────────────────
176
177/// The confidential-ledger state machine (`fhe-state` feature) —
178/// homomorphic balance management, `FheUint64` ciphertexts. See the
179/// module's own docs for the "Two assets, not one" statement and the V-2
180/// custody rule. [`vault::derive_vault_id`] and the `ERR_*` constants are
181/// available regardless of `fhe-state`.
182pub mod vault;
183
184/// The confidential ledger's client SDK — key generation, balance
185/// encryption, payload construction. [`client::LedgerPayload`] is always
186/// available; [`client::NativeVaultClient`] requires `fhe-state` and
187/// [`client::AethelVaultClient`] requires `wasm`.
188pub mod client;
189
190/// EIP-3009/x402 settlement export (`signer` feature, default). See the
191/// module's own docs for the "Zero ECDSA / 100% PQC signer" statement.
192#[cfg(feature = "signer")]
193pub mod settlement;
194
195/// Standing spend policy, enforced before any settlement intent is signed
196/// (`signer` feature, default).
197#[cfg(feature = "signer")]
198pub mod policy;
199
200/// Optional signed settlement receipts (`signer` feature, default).
201/// Emitted only on request — see the module's own docs (V-5 / UX-001).
202#[cfg(feature = "signer")]
203pub mod receipt;
204
205/// `VaultError` — the Rust-native error type for the `signer`-mode surface.
206#[cfg(feature = "signer")]
207pub mod error;
208
209/// Storage layer — HelixDB vector-graph hybrid database adapter.
210///
211/// Provides async gRPC client for encrypted vault state storage and retrieval.
212/// Only available with the `helixdb` feature.
213#[cfg(feature = "helixdb")]
214pub mod storage;
215
216/// SDK module — TypeScript and Rust utilities for HelixDB integration.
217pub mod sdk;
218
219// ── Re-exports ────────────────────────────────────────────────────────────────
220
221pub use vault::{
222 derive_vault_id, ERR_DESER, ERR_INSUFFICIENT_BALANCE, ERR_INVALID_KEY, ERR_NOT_FOUND, ERR_OK,
223 ERR_UNAUTHORIZED, ERR_WIRE_VERIFY_FAILED,
224};
225
226#[cfg(feature = "fhe-state")]
227pub use vault::{
228 homomorphic_transfer_authenticated, homomorphic_transfer_authenticated_bytes,
229 register_vault_with_identity, VaultState,
230};
231
232/// Re-export `LedgerPayload` for all targets (V-7: renamed from
233/// `ContractPayload`, which remains available as a deprecated alias).
234pub use client::LedgerPayload;
235
236#[cfg(feature = "wasm")]
237pub use client::{AethelVaultClient, SecretKeyContainer};
238
239#[cfg(all(not(target_arch = "wasm32"), feature = "fhe-state"))]
240pub use client::NativeVaultClient;
241
242#[cfg(feature = "signer")]
243pub use error::VaultError;
244
245#[cfg(feature = "signer")]
246pub use policy::{
247 Decision, HitlApproval, PolicyState, PolicyViolation, Rail, SpendPolicy, SpendRequest,
248};
249
250#[cfg(feature = "signer")]
251pub use receipt::{Receipt, SignedReceipt};
252
253#[cfg(feature = "signer")]
254pub use settlement::{
255 Chain, SettlementAuthorization, SettlementError, SettlementSigner, SpendIntent,
256 TransferWithAuthorization, Wallet,
257};
258
259#[cfg(feature = "helixdb")]
260pub use storage::helixdb::HelixDbAdapter;
261
262// ── Top-level WASM exports ────────────────────────────────────────────────────
263//
264// These re-export vault functions with wasm_bindgen for direct JS consumption.
265// Homomorphic-ledger exports additionally require `fhe-state`.
266
267#[cfg(feature = "wasm")]
268use wasm_bindgen::prelude::*;
269
270/// Initialize the vault with a TFHE ServerKey (WASM export, `fhe-state`).
271///
272/// Must be called before any other vault operations.
273///
274/// # Parameters
275///
276/// - `server_key_bytes`: Bincode-serialized `ServerKey`.
277///
278/// # Returns
279///
280/// `0` on success, non-zero error code on failure.
281#[cfg(all(feature = "wasm", feature = "fhe-state"))]
282#[wasm_bindgen]
283pub fn vault_init(server_key_bytes: &[u8]) -> u32 {
284 vault::vault_init_from_bytes(server_key_bytes)
285}
286
287/// Register a vault with an encrypted initial balance (WASM export, `fhe-state`).
288///
289/// # Parameters
290///
291/// - `vault_id`: 32-byte vault ID (derived from PLP projection via `vault_derive_id`).
292/// - `initial_balance_ct`: Bincode-serialized `FheUint64` initial balance ciphertext.
293///
294/// # Returns
295///
296/// `0` on success, non-zero error code on failure.
297#[cfg(all(feature = "wasm", feature = "fhe-state"))]
298#[wasm_bindgen]
299pub fn vault_register(vault_id: &[u8], initial_balance_ct: &[u8]) -> u32 {
300 vault::vault_register_from_bytes(vault_id, initial_balance_ct)
301}
302
303/// Execute a homomorphic transfer between two vaults (WASM export, `fhe-state`).
304///
305/// # Parameters
306///
307/// - `sender_id`: 32-byte sender vault ID.
308/// - `receiver_id`: 32-byte receiver vault ID.
309/// - `transfer_ct`: Bincode-serialized `FheUint64` transfer amount ciphertext.
310///
311/// # Returns
312///
313/// `0` on success, non-zero error code on failure.
314#[cfg(all(feature = "wasm", feature = "fhe-state"))]
315#[wasm_bindgen]
316pub fn vault_transfer(sender_id: &[u8], receiver_id: &[u8], transfer_ct: &[u8]) -> u32 {
317 vault::vault_transfer_from_bytes(sender_id, receiver_id, transfer_ct)
318}
319
320/// Execute an authenticated homomorphic transfer from `aethel-plp-1` wire
321/// bytes (WASM export, `fhe-state`). Bytes-taking counterpart of
322/// [`vault_transfer`] that also checks a PLP ownership proof against the
323/// sender vault's registered identity — see
324/// [`vault::homomorphic_transfer_authenticated_bytes`] for the full order
325/// of checks (SAGP-PG-001 V-3 residual / `docs/ROADMAP.md` §2).
326///
327/// # Parameters
328///
329/// - `sender_id` / `receiver_id`: 32-byte vault IDs.
330/// - `transfer_ct`: Bincode-serialized `FheUint64` transfer amount ciphertext.
331/// - `projection_bytes` / `proof_bytes`: `aethel-plp-1` wire envelopes.
332/// - `context`: the verifier's own context bytes.
333///
334/// # Returns
335///
336/// `0` on success, non-zero error code on failure (including the new
337/// `ERR_WIRE_VERIFY_FAILED` (6)).
338#[cfg(all(feature = "wasm", feature = "fhe-state"))]
339#[wasm_bindgen]
340pub fn vault_transfer_authenticated_bytes(
341 sender_id: &[u8],
342 receiver_id: &[u8],
343 transfer_ct: &[u8],
344 projection_bytes: &[u8],
345 proof_bytes: &[u8],
346 context: &[u8],
347) -> u32 {
348 vault::wasm_vault_transfer_authenticated_bytes(
349 sender_id,
350 receiver_id,
351 transfer_ct,
352 projection_bytes,
353 proof_bytes,
354 context,
355 )
356}
357
358/// Register a vault bound to an `aethel-core` PLP identity projection
359/// (WASM export, `fhe-state`).
360///
361/// Unlike [`vault_register`], the vault ID is derived server-side from
362/// `projection_bytes` rather than caller-supplied, so it cannot be
363/// registered under an ID unrelated to the projection.
364///
365/// # Parameters
366///
367/// - `projection_bytes`: Serialized `EphemeralProjection` from aethel-core PLP.
368/// - `initial_balance_ct`: Bincode-serialized `FheUint64` initial balance ciphertext.
369///
370/// # Returns
371///
372/// The derived 32-byte vault ID, or an empty `Vec` if `projection_bytes`
373/// does not decode as an `EphemeralProjection`.
374#[cfg(all(feature = "wasm", feature = "fhe-state"))]
375#[wasm_bindgen]
376pub fn vault_register_with_identity(
377 projection_bytes: &[u8],
378 initial_balance_ct: &[u8],
379) -> alloc::vec::Vec<u8> {
380 vault::wasm_vault_register_with_identity(projection_bytes, initial_balance_ct)
381}
382
383/// Export the entire vault state as bytes for HelixDB persistence (WASM
384/// export, `fhe-state`).
385///
386/// Never contains the ServerKey — see [`vault`]'s "Custody rule" docs (V-2).
387///
388/// # Returns
389///
390/// Bincode-serialized `VaultState` bytes (version-prefixed). Empty if
391/// state is uninitialized.
392#[cfg(all(feature = "wasm", feature = "fhe-state"))]
393#[wasm_bindgen]
394pub fn vault_export_state() -> alloc::vec::Vec<u8> {
395 vault::vault_export_state()
396}
397
398/// Import vault state from HelixDB bytes (WASM export, `fhe-state`).
399///
400/// # Parameters
401///
402/// - `state_bytes`: Bincode-serialized `VaultState` (version-prefixed).
403///
404/// # Returns
405///
406/// `0` on success, non-zero error code on failure.
407#[cfg(all(feature = "wasm", feature = "fhe-state"))]
408#[wasm_bindgen]
409pub fn vault_import_state(state_bytes: &[u8]) -> u32 {
410 vault::vault_import_state(state_bytes)
411}
412
413/// Derive a vault ID from PLP projection bytes (WASM export).
414///
415/// Available regardless of `fhe-state`: pure hashing.
416///
417/// # Parameters
418///
419/// - `projection_bytes`: Serialized `EphemeralProjection` from aethel-core PLP.
420///
421/// # Returns
422///
423/// 32-byte vault ID as `Vec<u8>`.
424#[cfg(feature = "wasm")]
425#[wasm_bindgen]
426pub fn vault_derive_id(projection_bytes: &[u8]) -> alloc::vec::Vec<u8> {
427 vault::derive_vault_id(projection_bytes).to_vec()
428}