auths-id¶
Identity management, attestation logic, and Git-native storage for Auths.
Role in the Architecture¶
flowchart LR
CORE["auths-core<br/><i>crypto, keychains</i>"]
ID["auths-id<br/><i>identity, git storage</i>"]
SDK["auths-sdk<br/><i>orchestration</i>"]
CLI["auths-cli<br/><i>I/O adapter</i>"]
CORE --> ID --> SDK --> CLI
auths-id sits between the cryptographic foundation (auths-core) and the workflow orchestration layer (auths-sdk). It owns the domain model for identities and attestations, the KERI key event log implementation, and the Git ref-based storage layer. All identity data is persisted as Git objects referenced by well-defined ref paths.
Public Modules¶
| Module | Feature Gate | Purpose |
|---|---|---|
attestation |
(always) | Attestation creation, verification, export, revocation, grouping |
identity |
git-storage for most submodules |
Identity creation, DID resolution, key rotation |
storage |
git-storage for Git-backed impls |
Storage traits and Git implementations |
keri |
(always) | KERI Key Event Log: inception, rotation, validation, sealing |
domain |
(always) | Domain types for attestation messages and KEL ports |
ports |
(always) | Hexagonal architecture port traits (re-exports from storage) |
policy |
(always) | Policy evaluation types |
freeze |
(always) | Identity freeze/thaw operations |
trailer |
(always) | Git commit trailer parsing/writing |
agent_identity |
git-storage |
Agent identity management |
trust |
git-storage |
Trust anchor evaluation |
witness |
git-storage |
Witness integration |
witness_config |
(always) | Witness configuration types |
Key Traits¶
IdentityStorage¶
Abstracts creation and retrieval of the primary identity document.
pub trait IdentityStorage {
fn create_identity(
&self,
controller_did: &str,
metadata: Option<serde_json::Value>,
) -> Result<(), Error>;
fn load_identity(&self) -> Result<ManagedIdentity, Error>;
fn get_identity_ref(&self) -> Result<String, Error>;
}
The concrete implementation is GitIdentityStorage, which stores identity data as a JSON blob (identity.json by default) in a Git commit pointed to by a configurable ref (e.g., refs/auths/identity). The internal StoredIdentityData structure contains version, controller_did, and an optional arbitrary metadata JSON field that consumers define.
AttestationSource¶
Read-side trait for loading attestations from storage. Implementations may be backed by Git refs, SQLite indexes, or in-memory stores.
pub trait AttestationSource {
fn load_attestations_for_device(&self, device_did: &DeviceDID) -> Result<Vec<Attestation>, Error>;
fn load_all_attestations(&self) -> Result<Vec<Attestation>, Error>;
fn load_all_attestations_paginated(&self, limit: usize, offset: usize) -> Result<Vec<Attestation>, Error>;
fn discover_device_dids(&self) -> Result<Vec<DeviceDID>, Error>;
}
The concrete implementation GitAttestationStorage walks Git history on per-device refs to reconstruct the attestation timeline. Paginated loading avoids stalling on repositories with thousands of devices.
StorageDriver¶
Low-level async blob storage abstraction supporting both local and remote backends.
#[async_trait]
pub trait StorageDriver: Send + Sync {
async fn get_blob(&self, path: &str) -> Result<Vec<u8>, StorageError>;
async fn put_blob(&self, path: &str, data: &[u8]) -> Result<(), StorageError>;
async fn cas_update(&self, ref_key: &str, expected: Option<&[u8]>, new: &[u8]) -> Result<(), StorageError>;
async fn list_prefix(&self, prefix: &str) -> Result<Vec<String>, StorageError>;
async fn exists(&self, path: &str) -> Result<bool, StorageError>;
async fn delete(&self, path: &str) -> Result<(), StorageError>;
}
StorageError distinguishes three failure modes: NotFound, CasConflict (for optimistic concurrency), and Io (backend-specific).
RegistryBackend (from ports::registry)¶
Trait for packed registry storage operations (single-ref storage under refs/auths/registry). Provides higher-level operations like append_event, get_key_state, and store_attestation.
Git Ref Layout¶
Configurable Layout (StorageLayoutConfig)¶
The StorageLayoutConfig struct allows consumers to define custom Git reference paths. Three presets are available:
| Preset | Identity Ref | Attestation Prefix | Identity Blob | Attestation Blob |
|---|---|---|---|---|
default() |
refs/auths/identity |
refs/auths/devices/nodes |
identity.json |
attestation.json |
radicle() |
refs/rad/id |
refs/keys |
radicle-identity.json |
link-attestation.json |
gitoxide() |
refs/auths/id |
refs/auths/devices |
identity.json |
attestation.json |
Full device attestation ref path: {device_attestation_prefix}/{sanitized_did}/signatures
KERI Ref Layout (Fixed)¶
KERI refs follow a standardized pattern that is not configurable:
| Ref Pattern | Content |
|---|---|
refs/did/keri/{prefix}/kel |
Key Event Log (inception + rotations) |
refs/did/keri/{prefix}/receipts/{said} |
Witness receipts for a specific event |
refs/did/keri/{prefix}/document |
Cached DID document (optional) |
Organization Refs¶
Organization membership attestations use a fixed pattern:
| Ref Pattern | Content |
|---|---|
refs/auths/org/{sanitized_org_did}/identity |
Organization identity/metadata |
refs/auths/org/{sanitized_org_did}/members/{sanitized_member_did} |
Member attestation |
DID sanitization replaces all non-alphanumeric characters with underscores.
KERI Implementation¶
The keri module implements a KERI-inspired Key Event Log with the following components:
| Submodule | Purpose |
|---|---|
inception |
ICP event creation with initial key pair and next-key commitment |
rotation |
ROT event creation with pre-committed key promotion |
event |
Event type definitions (IcpEvent, RotEvent, IxnEvent) |
kel |
GitKel struct for reading/writing KEL from Git refs |
state |
KeriKeyState for tracking current key state after replaying events |
validate |
KEL validation: signature checks, commitment verification, sequence ordering |
seal |
Anchoring data (attestation SAIDs) into interaction events |
anchor |
Anchoring attestations to the KEL via ixn events |
resolve |
KERI DID resolution to current public key |
cache |
Caching layer for resolved key states |
types |
Prefix, Said, and other KERI primitive types |
witness_integration |
Witness receipt collection and storage |
Rotation Protocol¶
Key rotation follows the KERI pre-commitment pattern:
- At inception: Generate two key pairs (current + next). The current key is stored in the keychain. The next key's public key is hashed into a commitment stored in the ICP event.
- At rotation: Verify the pre-committed next key matches the commitment. The next key becomes the new current key. A fresh next key pair is generated, and its commitment is embedded in the ROT event.
- Keychain management: The new current key is stored under the
next_alias. A future next key is stored under{next_alias}--next-{sequence}. The old pre-committed key alias is deleted.
Two rotation backends exist:
rotate_keri_identity()-- GitKel backend (per-identity refs underrefs/did/keri/)rotate_registry_identity()-- Packed registry backend (singlerefs/auths/registryref)
Both follow the same protocol but differ in how they read/write the KEL.
Attestation Subsystem¶
The attestation module handles the full attestation lifecycle:
| Submodule | Purpose |
|---|---|
core |
Attestation struct shared with auths-verifier |
create |
Attestation creation with dual signing (issuer + device) |
verify |
Local attestation verification |
revoke |
Setting revoked_at timestamp on existing attestations |
export |
AttestationSink trait for writing attestations to storage |
load |
Loading attestations from storage |
group |
Grouping attestations by device or identity |
encoders |
Encoding attestation data for signing |
json_schema_encoder |
JSON Schema validation for attestation documents |
Storage Error Types¶
StorageError (from storage::driver) provides three variants:
NotFound(String)-- normal condition, path does not existCasConflict { expected, found }-- optimistic concurrency violationIo(Box<dyn Error + Send + Sync>)-- backend-specific errors
Feature Flags¶
| Feature | Default | What it enables | Dependencies added |
|---|---|---|---|
git-storage |
Yes | Git-backed storage implementations, identity init/resolve/rotate, agent identity, trust, witness modules | git2, dirs, tempfile, tokio |
indexed-storage |
No | SQLite-backed O(1) attestation lookups | auths-index |
witness-client |
No | HTTP client for remote witness servers | auths-infra-http |
Without git-storage¶
When git-storage is disabled, the crate provides only the trait definitions (IdentityStorage, AttestationSource, StorageDriver), attestation types, KERI types, and layout configuration. This allows embedding the domain model in environments without libgit2 (e.g., WASM or cloud lambdas).
Dependency Direction¶
The hexagonal architecture enforces strict dependency direction:
flowchart LR
STORAGE["auths-storage"] -- "depends on" --> PORTS["auths-id::ports"]
ID["auths-id"] -. "never imports" .-> STORAGE
style ID stroke-dasharray: 5 5
auths-id defines port traits in ports/. Storage backend crates implement these traits. The SDK and CLI wire the implementations together at the composition root.
Key Dependencies¶
| Crate | Purpose |
|---|---|
auths-core |
Keychain, signing, encryption |
auths-crypto |
Ed25519 operations via CryptoProvider trait |
auths-policy |
Policy evaluation |
auths-verifier |
Attestation and DID types (shared with verification layer) |
git2 |
Git repository operations (optional, behind git-storage) |
json-canon |
Deterministic JSON canonicalization for signatures |
jsonschema |
Attestation JSON Schema validation |
ring |
Ed25519 key generation and signing |
Lint Configuration¶
deny(clippy::print_stdout, clippy::print_stderr, clippy::exit, clippy::dbg_macro)-- no accidental I/Odeny(clippy::disallowed_methods)--Utc::now()is banned; all time-sensitive functions acceptnow: DateTime<Utc>as a parameter