Rust

Create and manage Cyphr principals with the Rust crate

Installation

Requires Rust 1.85 or later.

cargo add cyphr

The crate depends on coz-rs for cryptographic signing and key management.

Creating a Principal

A principal is a self-sovereign identity. At Level 1, a principal is a single key — the key's thumbprint is the identity.

use cyphr::Principal;
use cyphr::key::Key;
use coz::SigningKey;

fn main() -> Result<(), cyphr::Error> {
    // Generate a new ES256 key pair.
    let sk = SigningKey::generate(coz::Alg::ES256);
    let key = Key::from_coz_key(sk.to_key());

    // Create a Level 1 principal (implicit genesis).
    let p = Principal::implicit(key)?;

    println!("Principal Root: {}", p.pr());
    println!("Level: {:?}", p.level());
    println!("Active keys: {}", p.active_key_count());
    Ok(())
}

At Level 1, implicit promotion means that the key thumbprint promotes through KR → AR → SR → PR. One value. One identity. No ceremony.

Inspecting State

Every principal exposes its internal Merkle state:

println!("KR: {:?}", p.key_root());    // Key Root
println!("AR: {:?}", p.auth_root());   // Auth Root
println!("SR: {:?}", p.sr());          // State Root
println!("PR: {:?}", p.pr());          // Principal Root

At Level 1 all four are identical — they diverge as you add keys, commit transactions, and record actions.

Type Safety: Nascent vs. Established

The Rust implementation encodes the principal lifecycle in the type system. Internally, a Principal is either Nascent (L1/L2 — no Principal Genesis exists) or Established (L3+ — PG is frozen by principal/create). This distinction is invisible to most code thanks to Deref, but it means invalid states — like a Level 1 principal with a fabricated PG — are structurally unrepresentable.

// Nascent: pg() returns None
assert!(p.pg().is_none());

// After principal/create, PG is frozen and pg() returns Some

Multi-Key Genesis (Level 3)

For principals with multiple concurrent keys, use explicit genesis:

let sk1 = SigningKey::generate(coz::Alg::ES256);
let sk2 = SigningKey::generate(coz::Alg::ES256);

let keys = vec![
    Key::from_coz_key(sk1.to_key()),
    Key::from_coz_key(sk2.to_key()),
];

let p = Principal::explicit(keys)?;

println!("Active keys: {}", p.active_key_count()); // 2
println!("Level: {:?}", p.level());                // L3

Applying Transactions

State mutations are applied as verified Coz messages. The borrow checker enforces that no code can observe intermediate state during a commit:

// Single-transaction atomic commit.
let commit = p.apply_transaction(verified_coz)?;

For multi-transaction commits, use the scoped API:

let mut scope = p.begin_commit();
scope.apply(vtx1)?;
scope.apply(vtx2)?;
let commit = scope.finalize()?;

The CommitScope holds &mut Principal, which means the Rust compiler statically prevents reading the principal's state while the commit is in-flight — a structural guarantee that the Go implementation enforces by convention.

Storage

The cyphr-storage crate provides export/import for principal state:

cargo add cyphr-storage
use cyphr_storage::FileStore;

// Export principal state to a directory.
let store = FileStore::new("./my-principal")?;
store.export(&principal)?;

// Import principal state from a directory.
let restored = store.import()?;

Next Steps

  • Read the Protocol Specification for the full formal treatment
  • Browse the source for implementation details
  • Run cargo test from the rs/ directory to execute the test suite
  • Explore the CLI: cargo install cyphr-cli for command-line operations