<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mroczect</title>
    <description>The latest articles on DEV Community by mroczect (@mroczect).</description>
    <link>https://dev.to/mroczect</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4051862%2F32e3d6d2-86cc-46b6-b15c-dbcc04e29752.png</url>
      <title>DEV Community: mroczect</title>
      <link>https://dev.to/mroczect</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mroczect"/>
    <language>en</language>
    <item>
      <title>libvctrl 2.0.0: A Stable, Production-Ready Version Control Foundation</title>
      <dc:creator>mroczect</dc:creator>
      <pubDate>Mon, 10 Aug 2026 20:19:32 +0000</pubDate>
      <link>https://dev.to/mroczect/libvctrl-200-a-stable-production-ready-version-control-foundation-35c3</link>
      <guid>https://dev.to/mroczect/libvctrl-200-a-stable-production-ready-version-control-foundation-35c3</guid>
      <description>&lt;h1&gt;
  
  
  libvctrl
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;libvctrl&lt;/code&gt; is an all-in-one, batteries-included Software Development Kit for building custom version control systems. It aggregates three foundational layers -- contracts, reference implementations, and cryptography -- into a single coherent namespace, allowing developers to bootstrap a fully functional, content-addressable VCS backend without stitching multiple crates together manually.&lt;/p&gt;

&lt;p&gt;The SDK applies the &lt;strong&gt;Facade Pattern&lt;/strong&gt;: every essential type, trait, and implementation is re-exported at the crate root. A simple &lt;code&gt;use libvctrl::*;&lt;/code&gt; grants access to the entire stack. Low-level cryptographic primitives are namespaced under the &lt;code&gt;crypto&lt;/code&gt; module to prevent collision with the VCS-level &lt;code&gt;Hash&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;Every crate in the stack enforces &lt;code&gt;#![forbid(unsafe_code)]&lt;/code&gt;, denies &lt;code&gt;unwrap_used&lt;/code&gt;, &lt;code&gt;expect_used&lt;/code&gt;, and &lt;code&gt;panic&lt;/code&gt; at the compiler level, and passes &lt;code&gt;clippy::pedantic&lt;/code&gt; plus &lt;code&gt;clippy::nursery&lt;/code&gt;. The result is a memory-safe, panic-free, production-grade foundation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Three-Layer Design
&lt;/h3&gt;

&lt;p&gt;The SDK is composed of three re-exported sub-crates, each with a distinct responsibility:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Crate&lt;/th&gt;
&lt;th&gt;Module Alias&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Contracts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_handler&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;handler&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pure data types, behavior traits, error definitions, structural limits. Zero business logic.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementations&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reference&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ready-to-use backends: in-memory store, binary codec, SHA-512 hasher adapter, object builders, validators.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cryptography&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_sha512&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;crypto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pure-Rust, &lt;code&gt;no_std&lt;/code&gt;-compatible SHA-512, HMAC-SHA-512, HKDF-SHA-512.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Workspace Dependency Graph
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
    subgraph Workspace["libvctrl workspace"]
        handler["libvctrl_handler&amp;lt;br/&amp;gt;&amp;lt;b&amp;gt;Contracts&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;types + traits + errors&amp;lt;br/&amp;gt;(no dependencies)"]
        sha512["libvctrl_sha512&amp;lt;br/&amp;gt;&amp;lt;b&amp;gt;Cryptography&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;SHA-512 / HMAC / HKDF&amp;lt;br/&amp;gt;(standalone, no_std)"]
        core["libvctrl_core&amp;lt;br/&amp;gt;&amp;lt;b&amp;gt;Implementations&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;stores + codec + hash + builders"]
        sdk["libvctrl&amp;lt;br/&amp;gt;&amp;lt;b&amp;gt;SDK (this crate)&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;facade re-exports"]
        plumbing["libvctrl_plumbing&amp;lt;br/&amp;gt;(low-level CLI,&amp;lt;br/&amp;gt;not yet implemented)"]
        porcelain["libvctrl_porcelain&amp;lt;br/&amp;gt;(high-level CLI,&amp;lt;br/&amp;gt;not yet implemented)"]
        docs["libvctrl_docs&amp;lt;br/&amp;gt;(documentation)"]
    end

    core --&amp;gt;|depends on| handler
    core --&amp;gt;|depends on| sha512
    sdk --&amp;gt;|re-exports| handler
    sdk --&amp;gt;|re-exports| core
    sdk --&amp;gt;|re-exports| sha512
    plumbing -.-&amp;gt;|will depend on| handler
    porcelain -.-&amp;gt;|will depend on| handler

    style sdk fill:#4a7c59,stroke:#2d4a33,color:#fff,stroke-width:3px
    style handler fill:#5b7daa,stroke:#3a5278,color:#fff
    style core fill:#7a5daa,stroke:#4a3d78,color:#fff
    style sha512 fill:#8a6d3b,stroke:#5c4928,color:#fff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Internal Module Architecture
&lt;/h3&gt;

&lt;p&gt;The following diagram shows how the SDK's root-level re-exports map to the three sub-crates and their internal modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph LR
    subgraph SDK["libvctrl (SDK root)"]
        direction TB
        root_types["Blob, Commit, Tree, Tag,&amp;lt;br/&amp;gt;Hash, UserID, EntryKind,&amp;lt;br/&amp;gt;CommitMeta, TreeEntry"]
        root_traits["Encoder, Decoder, Hasher,&amp;lt;br/&amp;gt;ObjectStore, RefStore,&amp;lt;br/&amp;gt;Signer, Transport, Verifier"]
        root_errors["VctrlError"]
        root_constants["HASH_LENGTH,&amp;lt;br/&amp;gt;MAX_BLOB_SIZE,&amp;lt;br/&amp;gt;MAX_MESSAGE_LENGTH,&amp;lt;br/&amp;gt;MAX_NAME_LENGTH,&amp;lt;br/&amp;gt;MAX_TREE_ENTRIES"]
        root_impl["BinaryEncoder, BinaryDecoder,&amp;lt;br/&amp;gt;Sha512Hasher, MemoryStore,&amp;lt;br/&amp;gt;MemoryRefStore"]
        root_builders["BlobBuilder, CommitBuilder,&amp;lt;br/&amp;gt;TagBuilder, TreeBuilder,&amp;lt;br/&amp;gt;TreeEntryBuilder"]
        root_validate["validate_hash_bytes,&amp;lt;br/&amp;gt;validate_name"]
        root_crypto["crypto::&amp;lt;br/&amp;gt;SHA-512, HMAC, HKDF"]
    end

    subgraph Handler["handler (libvctrl_handler)"]
        h_types["types"]
        h_traits["traits"]
        h_errors["errors"]
        h_constants["constants"]
        h_enums["enums"]
        h_macros["macros"]
    end

    subgraph Reference["reference (libvctrl_core)"]
        r_codec["codec"]
        r_hash["hash"]
        r_object["object"]
        r_store["store"]
        r_validate["validate"]
    end

    subgraph CryptoLib["crypto (libvctrl_sha512)"]
        c_hash["Hash"]
        c_hmac["Hmac"]
        c_hkdf["Hkdf"]
    end

    root_types --&amp;gt; h_types
    root_traits --&amp;gt; h_traits
    root_errors --&amp;gt; h_errors
    root_constants --&amp;gt; h_constants
    root_impl --&amp;gt; r_codec
    root_impl --&amp;gt; r_hash
    root_impl --&amp;gt; r_store
    root_builders --&amp;gt; r_object
    root_validate --&amp;gt; r_validate
    root_crypto --&amp;gt; c_hash
    root_crypto --&amp;gt; c_hmac
    root_crypto --&amp;gt; c_hkdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Object Lifecycle and Data Flow
&lt;/h3&gt;

&lt;p&gt;The following sequence diagram illustrates the complete lifecycle of a VCS object: construction via a builder, validation, encoding into the binary wire format, hashing for content-addressable identity, storage, and retrieval:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant App as Application
    participant Builder as Object Builder
    participant Validate as validate module
    participant Encoder as BinaryEncoder
    participant Hasher as Sha512Hasher
    participant Store as MemoryStore

    rect rgb(230, 240, 230)
        Note over App,Builder: 1. CONSTRUCT
        App-&amp;gt;&amp;gt;Builder: TreeBuilder::new().entry(...).build()
        Builder-&amp;gt;&amp;gt;Validate: validate_name() for each entry
        Validate--&amp;gt;&amp;gt;Builder: Ok or VctrlError
        Builder--&amp;gt;&amp;gt;App: Tree (immutable)
    end

    rect rgb(230, 230, 245)
        Note over App,Encoder: 2. ENCODE
        App-&amp;gt;&amp;gt;Encoder: encode_tree(&amp;amp;tree)
        Encoder--&amp;gt;&amp;gt;App: Vec&amp;amp;lt;u8&amp;amp;gt; (versioned binary payload)
    end

    rect rgb(245, 235, 220)
        Note over App,Hasher: 3. HASH
        App-&amp;gt;&amp;gt;Hasher: hash(&amp;amp;encoded_bytes)
        Hasher--&amp;gt;&amp;gt;App: Hash (64-byte SHA-512 digest)
    end

    rect rgb(240, 230, 230)
        Note over App,Store: 4. STORE
        App-&amp;gt;&amp;gt;Store: put(&amp;amp;hash, &amp;amp;data)
        Store--&amp;gt;&amp;gt;App: Ok(())
    end

    rect rgb(235, 235, 235)
        Note over App,Store: 5. RETRIEVE
        App-&amp;gt;&amp;gt;Store: get(&amp;amp;hash)
        Store--&amp;gt;&amp;gt;App: Box&amp;amp;lt;dyn Read&amp;amp;gt;
        Note over App: BinaryDecoder.decode_*() restores original object
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Core Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Contracts Layer (handler)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Immutable Domain Models&lt;/strong&gt; -- &lt;code&gt;Blob&lt;/code&gt;, &lt;code&gt;Tree&lt;/code&gt;, &lt;code&gt;Commit&lt;/code&gt;, &lt;code&gt;Tag&lt;/code&gt;, &lt;code&gt;Hash&lt;/code&gt;, &lt;code&gt;UserID&lt;/code&gt;, &lt;code&gt;TreeEntry&lt;/code&gt;, &lt;code&gt;CommitMeta&lt;/code&gt; are strongly-typed, immutable value objects. Construction validates all invariants at the boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior Traits&lt;/strong&gt; -- &lt;code&gt;Encoder&lt;/code&gt;, &lt;code&gt;Decoder&lt;/code&gt;, &lt;code&gt;Hasher&lt;/code&gt;, &lt;code&gt;ObjectStore&lt;/code&gt;, &lt;code&gt;RefStore&lt;/code&gt;, &lt;code&gt;Signer&lt;/code&gt;, &lt;code&gt;Transport&lt;/code&gt;, &lt;code&gt;Verifier&lt;/code&gt; define the interfaces that any concrete backend must implement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified Error Type&lt;/strong&gt; -- &lt;code&gt;VctrlError&lt;/code&gt; is the single error enum returned by every fallible operation across the entire SDK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structural Limits&lt;/strong&gt; -- &lt;code&gt;HASH_LENGTH&lt;/code&gt;, &lt;code&gt;MAX_BLOB_SIZE&lt;/code&gt;, &lt;code&gt;MAX_MESSAGE_LENGTH&lt;/code&gt;, &lt;code&gt;MAX_NAME_LENGTH&lt;/code&gt;, &lt;code&gt;MAX_TREE_ENTRIES&lt;/code&gt; centralize all magic numbers. Every constructor, encoder, and decoder references these constants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logical Entry Kinds&lt;/strong&gt; -- &lt;code&gt;EntryKind&lt;/code&gt; (Blob, Executable, Symlink, Tree, Submodule) decouples object classification from raw filesystem mode bits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helper Macros&lt;/strong&gt; -- &lt;code&gt;vctrl_error_other!&lt;/code&gt; and similar macros simplify ergonomic error construction.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementations Layer (reference)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Binary Codec&lt;/strong&gt; -- &lt;code&gt;BinaryEncoder&lt;/code&gt; and &lt;code&gt;BinaryDecoder&lt;/code&gt; implement a deterministic, versioned, little-endian wire format with length-prefixed variable-length fields. Full round-trip fidelity for all four object types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defensive Decoding&lt;/strong&gt; -- &lt;code&gt;BinaryDecoder&lt;/code&gt; is panic-free. Every slice access is bounds-checked. Malformed or truncated payloads return &lt;code&gt;VctrlError::CorruptedData&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DoS-Resistant Allocation&lt;/strong&gt; -- Before allocating memory for variable-length fields (blob data, commit messages, tag messages), the decoder validates the requested length against &lt;code&gt;MAX_BLOB_SIZE&lt;/code&gt;, &lt;code&gt;MAX_MESSAGE_LENGTH&lt;/code&gt;, and &lt;code&gt;MAX_TREE_ENTRIES&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SHA-512 Content Addressing&lt;/strong&gt; -- &lt;code&gt;Sha512Hasher&lt;/code&gt; delegates to the audited, pure-Rust &lt;code&gt;libvctrl_sha512&lt;/code&gt; crate to produce 64-byte digests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-Memory Object Store&lt;/strong&gt; -- &lt;code&gt;MemoryStore&lt;/code&gt; implements &lt;code&gt;ObjectStore&lt;/code&gt; with &lt;code&gt;HashMap&amp;lt;Hash, Vec&amp;lt;u8&amp;gt;&amp;gt;&lt;/code&gt; for testing, simulation, and prototyping.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-Memory Reference Store&lt;/strong&gt; -- &lt;code&gt;MemoryRefStore&lt;/code&gt; implements &lt;code&gt;RefStore&lt;/code&gt; with a &lt;code&gt;RefsIterator&lt;/code&gt; for managing named references (branches, tags) in RAM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fluent Object Builders&lt;/strong&gt; -- &lt;code&gt;BlobBuilder&lt;/code&gt;, &lt;code&gt;CommitBuilder&lt;/code&gt;, &lt;code&gt;TagBuilder&lt;/code&gt;, &lt;code&gt;TreeBuilder&lt;/code&gt;, &lt;code&gt;TreeEntryBuilder&lt;/code&gt; solve the telescoping constructor problem with a step-by-step fluent API. Validation is deferred to the &lt;code&gt;.build()&lt;/code&gt; call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input Validation&lt;/strong&gt; -- &lt;code&gt;validate_hash_bytes&lt;/code&gt; enforces 64-byte length. &lt;code&gt;validate_name&lt;/code&gt; enforces non-empty, length-limited, path-traversal-free identifiers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cryptography Layer (crypto)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SHA-512&lt;/strong&gt; -- Pure-Rust, &lt;code&gt;no_std&lt;/code&gt;-compatible one-shot and incremental hashing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HMAC-SHA-512&lt;/strong&gt; -- Hash-based Message Authentication Code for authenticated object integrity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HKDF-SHA-512&lt;/strong&gt; -- HMAC-based Key Derivation Function for deriving session keys or object encryption keys.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cross-Cutting Guarantees
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Unsafe&lt;/strong&gt; -- &lt;code&gt;#![forbid(unsafe_code)]&lt;/code&gt; across all crates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Panics&lt;/strong&gt; -- &lt;code&gt;unwrap_used&lt;/code&gt;, &lt;code&gt;expect_used&lt;/code&gt;, and &lt;code&gt;panic&lt;/code&gt; are denied at the compiler level in the SDK crate. The decoder and validators return &lt;code&gt;Result&lt;/code&gt; for every fallible operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict Linting&lt;/strong&gt; -- &lt;code&gt;clippy::all&lt;/code&gt;, &lt;code&gt;clippy::pedantic&lt;/code&gt;, &lt;code&gt;clippy::nursery&lt;/code&gt;, &lt;code&gt;clippy::cargo&lt;/code&gt; are all denied.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wire Format Versioning&lt;/strong&gt; -- Every serialized payload begins with a version byte (currently &lt;code&gt;2&lt;/code&gt;). Incompatible formats are rejected early, enabling future breaking changes without silent data corruption.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technology Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;Edition 2024, toolchain 1.85+&lt;/td&gt;
&lt;td&gt;Primary implementation language&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contracts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_handler&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4.0.0&lt;/td&gt;
&lt;td&gt;Trait definitions, domain types, errors, limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementations&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.0.0&lt;/td&gt;
&lt;td&gt;Reference backends (store, codec, hash, builders, validate)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cryptography&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_sha512&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.0.0&lt;/td&gt;
&lt;td&gt;SHA-512, HMAC-SHA-512, HKDF-SHA-512&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Property Testing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;proptest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1.11.0&lt;/td&gt;
&lt;td&gt;Arbitrary generation and fuzz-style testing (dev-dependency)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Lint and Safety Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[lints.rust]&lt;/span&gt;
&lt;span class="py"&gt;unsafe_code&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"forbid"&lt;/span&gt;
&lt;span class="py"&gt;missing_docs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;rust_2018_idioms&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;unreachable_pub&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;unused_qualifications&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;

&lt;span class="nn"&gt;[lints.clippy]&lt;/span&gt;
&lt;span class="py"&gt;all&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;pedantic&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;nursery&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;cargo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;missing_const_for_fn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;redundant_clone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;unwrap_used&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;expect_used&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;panic&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Workspace Layout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;libvctrl/
├── libvctrl/                  # SDK facade (this crate)
│   ├── src/
│   │   └── lib.rs             # Root re-exports, lint config, documentation
│   └── Cargo.tom
├── libvctrl_handler/          # Contracts: types, traits, errors
│   ├── src/
│   │   ├── constants.rs
│   │   ├── enums.rs
│   │   ├── errors.rs
│   │   ├── lib.rs
│   │   ├── macros.rs
│   │   ├── traits.rs
│   │   └── types.rs
│   └── Cargo.tom
├── libvctrl_core/             # Reference implementations
│   ├── src/
│   │   ├── codec/
│   │   │   ├── binary_decoder.rs
│   │   │   ├── binary_encoder.rs
│   │   │   └── mod.rs
│   │   ├── hash/
│   │   │   ├── mod.rs
│   │   │   └── sha512.rs
│   │   ├── object/
│   │   │   ├── blob.rs
│   │   │   ├── commit.rs
│   │   │   ├── mod.rs
│   │   │   ├── tag.rs
│   │   │   └── tree.rs
│   │   ├── store/
│   │   │   ├── memory.rs
│   │   │   ├── mod.rs
│   │   │   └── ref_store.rs
│   │   ├── validate/
│   │   │   ├── hash.rs
│   │   │   ├── mod.rs
│   │   │   └── name.rs
│   │   └── lib.rs
│   └── Cargo.tom
├── libvctrl_sha512/           # Cryptographic primitives
│   ├── src/
│   │   └── ...
│   └── Cargo.tom
├── libvctrl_plumbing/         # Low-level CLI (not yet implemented)
├── libvctrl_porcelain/        # High-level CLI (not yet implemented)
├── libvctrl_docs/             # Documentation assets
└── Cargo.tom                  # Workspace root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SDK Crate Internal Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;libvctrl/src/
└── lib.rs         # All re-exports, module declarations, and crate-level documentation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK crate is intentionally minimal at the source level. Its sole purpose is to orchestrate re-exports and enforce the unified namespace. All logic lives in the three dependency crates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rust toolchain&lt;/strong&gt; -- Stable Rust 1.85 or later. Edition 2024 is required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cargo&lt;/strong&gt; -- Included with the Rust toolchain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internet access to crates.io&lt;/strong&gt; -- For resolving dependencies during the initial build.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install or update the toolchain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustup &lt;span class="nb"&gt;install &lt;/span&gt;stable
rustup default stable
rustup update stable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# Expected: rustc 1.85.0 or later&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  As a Dependency (crates.io)
&lt;/h4&gt;

&lt;p&gt;Add &lt;code&gt;libvctrl&lt;/code&gt; to your project's &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;libvctrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  From Source (Workspace)
&lt;/h4&gt;

&lt;p&gt;Clone the repository and build the entire workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/mroczect/libvctrl.git
&lt;span class="nb"&gt;cd &lt;/span&gt;libvctrl
cargo build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build only the SDK crate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo build &lt;span class="nt"&gt;-p&lt;/span&gt; libvctrl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Feature Flags
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sha384&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enabled&lt;/td&gt;
&lt;td&gt;Enables SHA-384 support via &lt;code&gt;libvctrl_sha512/sha384&lt;/code&gt;. Truncates SHA-512 to 384 bits for applications that prefer shorter digests.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;opt_size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disabled&lt;/td&gt;
&lt;td&gt;Optimizes the &lt;code&gt;libvctrl_sha512&lt;/code&gt; crate for binary size rather than speed. Useful for WASM or embedded targets.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To disable the default SHA-384 feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;libvctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;default-features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enable size optimization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;libvctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"opt_size"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Structural Limits
&lt;/h4&gt;

&lt;p&gt;These constants are defined in &lt;code&gt;libvctrl_handler&lt;/code&gt; and are referenced throughout the SDK. They cannot be changed at runtime but serve as documentation for system capacity:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constant&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HASH_LENGTH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;Expected byte length of a &lt;code&gt;Hash&lt;/code&gt; digest (SHA-512 = 64 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX_BLOB_SIZE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(defined in handler)&lt;/td&gt;
&lt;td&gt;Maximum blob data size in bytes. Decoders reject larger blobs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX_MESSAGE_LENGTH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(defined in handler)&lt;/td&gt;
&lt;td&gt;Maximum commit/tag message length in bytes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX_NAME_LENGTH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(defined in handler)&lt;/td&gt;
&lt;td&gt;Maximum identifier name length in bytes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX_TREE_ENTRIES&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(defined in handler)&lt;/td&gt;
&lt;td&gt;Maximum number of entries in a single &lt;code&gt;Tree&lt;/code&gt; object.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Importing the SDK
&lt;/h3&gt;

&lt;p&gt;The facade design allows two import styles:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wildcard import (recommended for application code):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Targeted imports (recommended for library code):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TreeBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sha512Hasher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MemoryStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Encoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Hasher&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building and Encoding a Blob
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;BlobBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Encoder&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BlobBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"file content"&lt;/span&gt;&lt;span class="nf"&gt;.to_vec&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="nf"&gt;.encode_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building a Tree with Entries
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;EntryKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TreeBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TreeEntryBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sha512Hasher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Encoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Hasher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;blob_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0xAB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TreeEntryBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"src/main.rs"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nn"&gt;EntryKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;blob_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TreeBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="nf"&gt;.encode_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tree_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sha512Hasher&lt;/span&gt;&lt;span class="nf"&gt;.hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building a Commit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;CommitBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EntryKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TreeBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TreeEntryBuilder&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tree_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CommitBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Initial commit"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="nf"&gt;.encode_commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building a Tag
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;TagBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0xFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tagger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"bob@example.com"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TagBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"v1.0.0"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Release 1.0.0"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.tagger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tagger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Full Lifecycle: Build, Encode, Hash, Store, Retrieve
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;EntryKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TreeBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TreeEntryBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BinaryDecoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sha512Hasher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MemoryStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Encoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Decoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Hasher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ObjectStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Build a Tree containing a single file entry&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;blob_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0xAB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TreeEntryBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file.txt"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;EntryKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blob_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TreeBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Encode the Tree into binary format&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;encoded_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="nf"&gt;.encode_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Hash the encoded bytes to get a content-addressable identifier&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hasher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sha512Hasher&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tree_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hasher&lt;/span&gt;&lt;span class="nf"&gt;.hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;encoded_bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Store the encoded object&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;MemoryStore&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tree_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;encoded_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 5. Retrieve and verify&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tree_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tree_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="nf"&gt;.read_to_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;VctrlError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IoError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoded_bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 6. Decode back to the original object&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BinaryDecoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;decoded_tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="nf"&gt;.decode_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded_tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using the Reference Store
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;MemoryRefStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RefStore&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ref_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;MemoryRefStore&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;commit_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0x42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Set a branch reference&lt;/span&gt;
&lt;span class="n"&gt;ref_store&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"refs/heads/main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;commit_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Resolve it later&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;resolved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref_store&lt;/span&gt;&lt;span class="nf"&gt;.resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"refs/heads/main"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resolved&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;commit_hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validating Inputs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;validate_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate_hash_bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Valid names&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validate_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/main.rs"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.is_ok&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validate_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"README.md"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.is_ok&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Path traversal attacks are rejected&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validate_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../../etc/passwd"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validate_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Hash validation&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;valid_hash_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validate_hash_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;valid_hash_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.is_ok&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Wrong length is rejected&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validate_hash_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Accessing Cryptographic Primitives
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;crypto&lt;/code&gt; module provides low-level access to SHA-512, HMAC, and HKDF, isolated from the VCS-level &lt;code&gt;Hash&lt;/code&gt; type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// One-shot SHA-512 hash&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"message"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Incremental SHA-512&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="nf"&gt;.update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"chunk 1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="nf"&gt;.update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"chunk 2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="nf"&gt;.finalize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  API Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Root-Level Re-exports
&lt;/h3&gt;

&lt;p&gt;The following table lists every public item available at the &lt;code&gt;libvctrl::&lt;/code&gt; root namespace:&lt;/p&gt;

&lt;h4&gt;
  
  
  Types (from handler)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Kind&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Blob&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Struct&lt;/td&gt;
&lt;td&gt;Immutable content-addressable file data object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Tree&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Struct&lt;/td&gt;
&lt;td&gt;Immutable directory listing of &lt;code&gt;TreeEntry&lt;/code&gt; objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TreeEntry&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Struct&lt;/td&gt;
&lt;td&gt;Named, typed pointer to a child object (file or subtree)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Struct&lt;/td&gt;
&lt;td&gt;Immutable snapshot metadata: tree, parents, author, committer, message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CommitMeta&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Struct&lt;/td&gt;
&lt;td&gt;Timestamp, timezone offset, optional encoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Struct&lt;/td&gt;
&lt;td&gt;Immutable named pointer to a target object with optional tagger&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Hash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Struct&lt;/td&gt;
&lt;td&gt;64-byte cryptographic digest wrapper (content-addressable identifier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Struct&lt;/td&gt;
&lt;td&gt;Name + email identity (author, committer, tagger)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EntryKind&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enum&lt;/td&gt;
&lt;td&gt;Blob, Executable, Symlink, Tree, Submodule&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Traits (from handler)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Encoder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Serialize VCS objects into bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Decoder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deserialize VCS objects from bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Hasher&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compute content-addressable &lt;code&gt;Hash&lt;/code&gt; values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ObjectStore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Persist and retrieve raw serialized objects by &lt;code&gt;Hash&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RefStore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manage named references (branches, tags) pointing to &lt;code&gt;Hash&lt;/code&gt; values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Signer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cryptographically sign objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Verifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verify cryptographic signatures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Transport&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Push/pull objects to/from remote stores&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Implementations (from reference)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Trait Implemented&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BinaryEncoder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Encoder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Versioned, little-endian, length-prefixed binary serialization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BinaryDecoder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Decoder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Panic-free, bounds-checked binary deserialization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Sha512Hasher&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Hasher&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SHA-512 digest adapter bridging &lt;code&gt;libvctrl_sha512&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MemoryStore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ObjectStore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;In-memory &lt;code&gt;HashMap&lt;/code&gt;-backed object storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MemoryRefStore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RefStore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;In-memory named reference storage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Builders (from reference)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Target Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BlobBuilder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Blob&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fluent builder for blob data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TreeBuilder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Tree&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Accumulates &lt;code&gt;TreeEntry&lt;/code&gt; objects, finalizes into immutable &lt;code&gt;Tree&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TreeEntryBuilder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TreeEntry&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Assembles name + kind + hash, defers validation to &lt;code&gt;build()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CommitBuilder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Step-by-step configuration of tree, parents, author, committer, message, metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TagBuilder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Step-by-step configuration of name, target, tagger, message, metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Validators (from reference)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Signature&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validate_hash_bytes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(&amp;amp;[u8]) -&amp;gt; Result&amp;lt;(), VctrlError&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ensures slice is exactly 64 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validate_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(&amp;amp;str) -&amp;gt; Result&amp;lt;(), VctrlError&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ensures non-empty, within length limit, no path traversal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Error and Constants
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VctrlError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unified error enum for all fallible operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HASH_LENGTH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Expected hash byte length (64)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX_BLOB_SIZE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum blob data size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX_MESSAGE_LENGTH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum commit/tag message length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX_NAME_LENGTH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum identifier name length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX_TREE_ENTRIES&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum entries per Tree&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Sub-Modules
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;Source Crate&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;handler&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_handler&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Direct access to the contracts layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reference&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Direct access to the implementations layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;crypto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_sha512&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SHA-512, HMAC-SHA-512, HKDF-SHA-512 primitives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_core::codec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Binary encoder/decoder modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;object&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_core::object&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Object builder modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;store&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_core::store&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Storage backend modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_core::validate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validation utility modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;constants&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_handler::constants&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System-wide limit constants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enums&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_handler::enums&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Logical type enumerations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;errors&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_handler::errors&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Error type definitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;macros&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_handler::macros&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Helper macros&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;traits&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_handler::traits&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Behavior trait definitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;types&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;libvctrl_handler::types&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Domain model type definitions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  BinaryEncoder Method Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Wire Format&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;encode_blob&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;Blob&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;VERSION(1B) + data_len(8B u64 LE) + data&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;encode_tree&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;Tree&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;VERSION(1B) + entry_count(4B u32 LE) + [entries]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;encode_commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;Commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;VERSION(1B) + tree_hash(64B) + parent_count(1B) + parents + author + committer + msg + metadata&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;encode_tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;Tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;VERSION(1B) + name + target_hash(64B) + tagger? + msg + metadata&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  BinaryDecoder Method Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Error Conditions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;decode_blob&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;[u8]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Empty data, version mismatch, truncated length prefix, blob exceeds &lt;code&gt;MAX_BLOB_SIZE&lt;/code&gt;, length mismatch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;decode_tree&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;[u8]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Truncated data, version mismatch, entry count exceeds &lt;code&gt;MAX_TREE_ENTRIES&lt;/code&gt;, invalid UTF-8, malformed hash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;decode_commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;[u8]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Truncated data, version mismatch, message exceeds &lt;code&gt;MAX_MESSAGE_LENGTH&lt;/code&gt;, invalid UTF-8 in any string field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;decode_tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;[u8]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Truncated data, version mismatch, invalid tagger presence byte, invalid UTF-8, message exceeds limit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Sha512Hasher Method Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;[u8]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (64-byte SHA-512 digest)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  MemoryStore Method Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates an empty in-memory store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put(&amp;amp;Hash, &amp;amp;mut dyn Read)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stores serialized object data under the given hash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get(&amp;amp;Hash)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retrieves a &lt;code&gt;Box&amp;lt;dyn Read&amp;gt;&lt;/code&gt; for the object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exists(&amp;amp;Hash)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks whether an object is present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remove(&amp;amp;Hash)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes an object from the store&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  MemoryRefStore Method Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates an empty reference store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resolve(&amp;amp;str)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resolves a reference name to its &lt;code&gt;Hash&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set(&amp;amp;str, &amp;amp;Hash)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets a reference name to point to a hash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iter()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns a &lt;code&gt;RefsIterator&lt;/code&gt; over all stored references&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Binary Wire Format Specification
&lt;/h2&gt;

&lt;p&gt;All payloads share a common structure: a leading version byte followed by type-specific fields. All multi-byte integers are little-endian. Variable-length data is length-prefixed. The current wire format version is &lt;strong&gt;2&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blob
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Offset  Size    Field
0       1       VERSION (u8, value = 2)
1       8       data_len (u64 LE)
9       N       data (N = data_len bytes)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total size: &lt;code&gt;9 + data_len&lt;/code&gt; bytes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tree
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Offset  Size    Field
0       1       VERSION (u8, value = 2)
1       4       entry_count (u32 LE)
5       ...     entries (repeated entry_count times):
                  +0      1       name_len (u8)
                  +1      N       name (UTF-8, N = name_len)
                  +1+N    1       kind (0=Blob, 1=Executable, 2=Symlink, 3=Tree, 4=Submodule)
                  +2+N    64      hash (raw SHA-512 bytes)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entry size: &lt;code&gt;1 + name_len + 1 + 64&lt;/code&gt; bytes each.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Offset  Size    Field
0       1       VERSION (u8, value = 2)
1       64      tree_hash
65      1       parent_count (u8)
66      P*64    parent_hashes (P = parent_count)
..      1       author_name_len (u8)
..      N       author_name (UTF-8)
..      1       author_email_len (u8)
..      N       author_email (UTF-8)
..      1       committer_name_len (u8)
..      N       committer_name (UTF-8)
..      1       committer_email_len (u8)
..      N       committer_email (UTF-8)
..      4       msg_len (u32 LE)
..      N       message (UTF-8)
..      8       timestamp (i64 LE, Unix epoch seconds)
..      2       timezone_offset (i16 LE, minutes from UTC)
..      1       encoding_len (u8)
..      N       encoding (UTF-8; 0 means None)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tag
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Offset  Size    Field
0       1       VERSION (u8, value = 2)
1       1       name_len (u8)
2       N       name (UTF-8)
..      64      target_hash
..      1       has_tagger (u8: 0 = absent, 1 = present)
..      [if has_tagger == 1:]
            1       tagger_name_len (u8)
            N       tagger_name (UTF-8)
            1       tagger_email_len (u8)
            N       tagger_email (UTF-8)
..      4       msg_len (u32 LE)
..      N       message (UTF-8)
..      8       timestamp (i64 LE)
..      2       timezone_offset (i16 LE)
..      1       encoding_len (u8)
..      N       encoding (UTF-8; 0 means None)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Running Tests
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run all tests across the workspace&lt;/span&gt;
cargo &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Run tests for the SDK crate only&lt;/span&gt;
cargo &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; libvctrl

&lt;span class="c"&gt;# Run tests with verbose output per test&lt;/span&gt;
cargo &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; libvctrl &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--nocapture&lt;/span&gt;

&lt;span class="c"&gt;# Run only round-trip encode/decode tests&lt;/span&gt;
cargo &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; libvctrl round_trip

&lt;span class="c"&gt;# Run property-based tests (proptest)&lt;/span&gt;
cargo &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; libvctrl proptest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test Coverage Areas
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;What Is Tested&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Encode/decode round-trips&lt;/td&gt;
&lt;td&gt;All four object types: &lt;code&gt;Blob&lt;/code&gt;, &lt;code&gt;Tree&lt;/code&gt;, &lt;code&gt;Commit&lt;/code&gt;, &lt;code&gt;Tag&lt;/code&gt; -- encoded then decoded must equal the original&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Corrupted data rejection&lt;/td&gt;
&lt;td&gt;Truncated payloads, wrong version bytes, invalid UTF-8 sequences, malformed hashes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DoS limit enforcement&lt;/td&gt;
&lt;td&gt;Blobs exceeding &lt;code&gt;MAX_BLOB_SIZE&lt;/code&gt;, messages exceeding &lt;code&gt;MAX_MESSAGE_LENGTH&lt;/code&gt;, trees exceeding &lt;code&gt;MAX_TREE_ENTRIES&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation edge cases&lt;/td&gt;
&lt;td&gt;Empty names, overly long names, path traversal sequences (&lt;code&gt;..&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;), incorrect hash lengths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Store operations&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;put&lt;/code&gt;/&lt;code&gt;get&lt;/code&gt; round-trips in &lt;code&gt;MemoryStore&lt;/code&gt;, &lt;code&gt;exists&lt;/code&gt; checks, reference resolution in &lt;code&gt;MemoryRefStore&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Builder validation&lt;/td&gt;
&lt;td&gt;Missing required fields, invalid field values, type invariants enforced at &lt;code&gt;build()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Property tests&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;proptest&lt;/code&gt;-driven fuzzing with arbitrary byte sequences, random name strings, random entry counts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;Contributions are welcome. All contributions must meet the following standards before merge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Quality Requirements
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No unsafe code.&lt;/strong&gt; The crate forbids it at the compiler level. Do not attempt to add &lt;code&gt;unsafe&lt;/code&gt; blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No panics.&lt;/strong&gt; &lt;code&gt;unwrap_used&lt;/code&gt;, &lt;code&gt;expect_used&lt;/code&gt;, and &lt;code&gt;panic&lt;/code&gt; are denied. All fallible operations must return &lt;code&gt;Result&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clippy compliance.&lt;/strong&gt; All contributions must pass &lt;code&gt;cargo clippy -- -D warnings&lt;/code&gt; with &lt;code&gt;clippy::pedantic&lt;/code&gt;, &lt;code&gt;clippy::nursery&lt;/code&gt;, and &lt;code&gt;clippy::cargo&lt;/code&gt; enabled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation.&lt;/strong&gt; Every public item must have a doc comment explaining its purpose, design rationale, and error conditions. Module-level docs must include at least one code example.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests.&lt;/strong&gt; Every new feature or bug fix must include tests covering both the happy path and all relevant failure modes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format.&lt;/strong&gt; Run &lt;code&gt;cargo fmt&lt;/code&gt; before committing. CI rejects unformatted code.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Architectural Requirements
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wire format stability.&lt;/strong&gt; Changes to the binary wire format must bump the &lt;code&gt;VERSION&lt;/code&gt; constant in both &lt;code&gt;binary_encoder.rs&lt;/code&gt; and &lt;code&gt;binary_decoder.rs&lt;/code&gt;. Never change the encoding of an existing version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contract stability.&lt;/strong&gt; Changes to trait signatures in &lt;code&gt;libvctrl_handler&lt;/code&gt; are breaking changes and require a major version bump. Update &lt;code&gt;libvctrl_core&lt;/code&gt; and &lt;code&gt;libvctrl&lt;/code&gt; together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Facade consistency.&lt;/strong&gt; Every new public item in a sub-crate that is intended for general use must be re-exported at the &lt;code&gt;libvctrl&lt;/code&gt; root. Keep the root namespace ergonomic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Local CI Checklist
&lt;/h3&gt;

&lt;p&gt;Run the following before opening a pull request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Format check&lt;/span&gt;
cargo &lt;span class="nb"&gt;fmt&lt;/span&gt; &lt;span class="nt"&gt;--check&lt;/span&gt;

&lt;span class="c"&gt;# Lint (workspace-wide)&lt;/span&gt;
cargo clippy &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; warnings

&lt;span class="c"&gt;# Test (workspace-wide)&lt;/span&gt;
cargo &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Documentation build (check for broken doc links)&lt;/span&gt;
cargo doc &lt;span class="nt"&gt;--no-deps&lt;/span&gt;

&lt;span class="c"&gt;# Individual crate checks (if modifying a specific crate)&lt;/span&gt;
cargo &lt;span class="nb"&gt;fmt&lt;/span&gt; &lt;span class="nt"&gt;--check&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; libvctrl
cargo clippy &lt;span class="nt"&gt;-p&lt;/span&gt; libvctrl &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; warnings
cargo &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; libvctrl
cargo doc &lt;span class="nt"&gt;-p&lt;/span&gt; libvctrl &lt;span class="nt"&gt;--no-deps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Future Feature Flags (Guidance)
&lt;/h3&gt;

&lt;p&gt;When adding feature flags in the future, follow these conventions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hash algorithm flags&lt;/strong&gt; (e.g., &lt;code&gt;sha512&lt;/code&gt;, &lt;code&gt;blake3&lt;/code&gt;) should gate the corresponding hasher implementation and its dependency. The &lt;code&gt;Sha512Hasher&lt;/code&gt; re-export should be conditional on the &lt;code&gt;sha512&lt;/code&gt; feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store backend flags&lt;/strong&gt; (e.g., &lt;code&gt;memory-store&lt;/code&gt;, &lt;code&gt;fs-store&lt;/code&gt;) should gate the corresponding store implementation. The &lt;code&gt;MemoryStore&lt;/code&gt; re-export should be conditional on &lt;code&gt;memory-store&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;no_std&lt;/code&gt; flag&lt;/strong&gt; should gate off all &lt;code&gt;std&lt;/code&gt;-dependent modules (stores, &lt;code&gt;std::io&lt;/code&gt;) and retain only the codec and hash modules that can operate without allocation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;This project is licensed under the &lt;strong&gt;MIT License&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Repository: &lt;a href="https://github.com/mroczect/libvctrl" rel="noopener noreferrer"&gt;https://github.com/mroczect/libvctrl&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Documentation: &lt;a href="https://docs.rs/libvctrl" rel="noopener noreferrer"&gt;https://docs.rs/libvctrl&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>opensource</category>
      <category>libary</category>
      <category>cryptography</category>
    </item>
    <item>
      <title>Introducing librage: A Safe, Simple, and Uniform Rust Wrapper for Age Encryption</title>
      <dc:creator>mroczect</dc:creator>
      <pubDate>Thu, 06 Aug 2026 13:29:16 +0000</pubDate>
      <link>https://dev.to/mroczect/introducing-librage-a-safe-simple-and-uniform-rust-wrapper-for-age-encryption-3b70</link>
      <guid>https://dev.to/mroczect/introducing-librage-a-safe-simple-and-uniform-rust-wrapper-for-age-encryption-3b70</guid>
      <description>&lt;h1&gt;
  
  
  Introducing librage: A Safe, Simple, and Uniform Rust Wrapper for Age Encryption
&lt;/h1&gt;

&lt;p&gt;Modern applications need encryption that is fast, secure, and easy to integrate. The &lt;a href="https://age-encryption.org" rel="noopener noreferrer"&gt;age&lt;/a&gt; format delivers the cryptography, and &lt;a href="https://github.com/str4d/rage" rel="noopener noreferrer"&gt;rage&lt;/a&gt; provides a superb Rust implementation. But the raw rage API, while powerful, can feel low‑level when all you want is a straightforward &lt;strong&gt;encrypt‑this&lt;/strong&gt; / &lt;strong&gt;decrypt‑that&lt;/strong&gt; interface with no surprises. That’s where &lt;strong&gt;librage&lt;/strong&gt; steps in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;librage&lt;/strong&gt; is a thin, idiomatic Rust wrapper around rage that gives every operation a &lt;strong&gt;uniform response envelope&lt;/strong&gt; (&lt;code&gt;LibrageResponse&amp;lt;T&amp;gt;&lt;/code&gt;), automatically zeroizes all sensitive material, and exposes the full power of age without requiring you to juggle error types or boxed trait objects manually. It even works with streaming, SSH keys, and ASCII‑armoured output right out of the box.&lt;/p&gt;

&lt;p&gt;In this post I’ll explain why I built librage, how it makes your life easier, and how you can start encrypting in three lines of code.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Great Library, Unfriendly API
&lt;/h2&gt;

&lt;p&gt;The rage crate is undoubtedly well‑designed. But using it directly often means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually parsing key strings into &lt;code&gt;x25519::Recipient&lt;/code&gt; or &lt;code&gt;ssh::Identity&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Boxing trait objects for identities and recipients.&lt;/li&gt;
&lt;li&gt;Handling diverse error variants (&lt;code&gt;EncryptError&lt;/code&gt;, &lt;code&gt;DecryptError&lt;/code&gt;, &lt;code&gt;ParseRecipientKeyError&lt;/code&gt;) and mapping them into your application’s error model.&lt;/li&gt;
&lt;li&gt;Remembering to zeroize plaintext and secrets yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I found myself writing the same boilerplate in every project: key parsing, error conversion, zeroize wrappers, and a custom result type that could speak JSON. &lt;strong&gt;librage&lt;/strong&gt; consolidates all of that into one ergonomic crate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uniform API: Everything Returns &lt;code&gt;LibrageResponse&amp;lt;T&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The central design decision is that every public function returns a &lt;code&gt;LibrageResponse&amp;lt;T&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;LibrageResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Serialize&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ErrorBody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the call succeeds, &lt;code&gt;success&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;data&lt;/code&gt; contains the result. If it fails, &lt;code&gt;success&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; holds a machine‑readable error code (&lt;code&gt;INVALID_PUBLIC_KEY&lt;/code&gt;, &lt;code&gt;DECRYPTION_FAILED&lt;/code&gt;, etc.) and a human‑readable message. A simple &lt;code&gt;.to_json()&lt;/code&gt; call gives you a ready‑to‑send JSON string—perfect for APIs, CLIs, or FFI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature‑Rich, Minimalist Surface
&lt;/h2&gt;

&lt;p&gt;librage exposes everything you need, and nothing you don’t:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;X25519 keys&lt;/strong&gt;: generate keypairs, encrypt/decrypt with one or multiple recipients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passphrase (scrypt)&lt;/strong&gt;: encrypt with a passphrase—no keys to manage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH keys&lt;/strong&gt;: encrypt to an SSH public key, decrypt with the corresponding private key (passphrase‑protected keys supported).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag &amp;amp; tagpq recipients&lt;/strong&gt;: full support for the age tagging mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming&lt;/strong&gt;: incremental encryption and decryption for large files or network streams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASCII armour&lt;/strong&gt;: opt‑in PEM encoding for safe transport over text channels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File utilities&lt;/strong&gt;: read recipients and identities from files, including multi‑line SSH private keys, ignoring comments and blank lines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zeroize by default&lt;/strong&gt;: secret keys (&lt;code&gt;KeyGenData::secret_key&lt;/code&gt;), ciphertexts, and plaintexts are all wrapped in &lt;code&gt;Zeroizing&lt;/code&gt; and automatically wiped on drop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean error model&lt;/strong&gt;: &lt;code&gt;LibrageError&lt;/code&gt; implements &lt;code&gt;std::error::Error&lt;/code&gt; and transparently converts from all relevant rage error types.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No need to box identities yourself—librage handles it internally. You just pass &lt;code&gt;&amp;amp;str&lt;/code&gt; keys or slices.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Example: From Keygen to Decrypted Text
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;librage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Generate a new X25519 keypair&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_keypair&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="py"&gt;.success&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;kp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Encrypt some bytes&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;plaintext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;b"Hello, librage!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;kp&lt;/span&gt;&lt;span class="py"&gt;.public_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ciphertext: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="nf"&gt;.as_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// hex or PEM&lt;/span&gt;

&lt;span class="c1"&gt;// Decrypt it back&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;dec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="py"&gt;.data.ciphertext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;kp&lt;/span&gt;&lt;span class="py"&gt;.secret_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same pattern holds for passphrase encryption, SSH, and streaming—just swap &lt;code&gt;encrypt&lt;/code&gt; for &lt;code&gt;encrypt_with_passphrase&lt;/code&gt;, &lt;code&gt;encrypt_with_ssh&lt;/code&gt;, etc. Every function returns the same &lt;code&gt;LibrageResponse&lt;/code&gt;, so error handling stays consistent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Without Headaches
&lt;/h2&gt;

&lt;p&gt;I baked several safety measures directly into the library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zeroization&lt;/strong&gt; – &lt;code&gt;KeyGenData::secret_key&lt;/code&gt; is &lt;code&gt;Zeroizing&amp;lt;String&amp;gt;&lt;/code&gt;. When the struct is dropped, the memory is overwritten. Similarly, &lt;code&gt;EncryptOutput&lt;/code&gt; and &lt;code&gt;DecryptOutput&lt;/code&gt; use &lt;code&gt;Zeroizing&amp;lt;Vec&amp;lt;u8&amp;gt;&amp;gt;&lt;/code&gt; for ciphertext and plaintext.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No hidden panics&lt;/strong&gt; – all fallible operations propagate &lt;code&gt;LibrageError&lt;/code&gt;. The library never calls &lt;code&gt;unwrap()&lt;/code&gt; or &lt;code&gt;expect()&lt;/code&gt; on user‑facing code paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegation to rage&lt;/strong&gt; – cryptographic operations are left entirely to the well‑audited rage crate; librage only smoothes the interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passphrase‑handling notes&lt;/strong&gt; – functions that accept a &lt;code&gt;&amp;amp;str&lt;/code&gt; passphrase do not zeroize the original input (Rust strings are immutable); for long‑lived secrets, consider using &lt;code&gt;SecretString&lt;/code&gt; externally. The documentation is explicit about this.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Who Should Use librage?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend services&lt;/strong&gt; that need to encrypt payloads or tokens before storing them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI tools&lt;/strong&gt; that want a JSON‑based interface for key management and encryption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desktop or mobile apps&lt;/strong&gt; (via Rust bindings) that require secure local storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developers learning age&lt;/strong&gt; who want a gentle, high‑level introduction without sacrificing access to the full feature set.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anyone who’s tired of re‑implementing the same error mapping and zeroize wrappers.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because librage re‑exports the underlying &lt;code&gt;age&lt;/code&gt; types where needed (e.g., &lt;code&gt;Box&amp;lt;dyn age::Identity&amp;gt;&lt;/code&gt; for advanced use), you can still drop down to the raw rage API if you ever need to.&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;The library is feature‑complete and ready for production. Potential future additions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A dedicated &lt;code&gt;EncryptOutput&lt;/code&gt;/&lt;code&gt;DecryptOutput&lt;/code&gt; &lt;strong&gt;to‑file&lt;/strong&gt; helpers.&lt;/li&gt;
&lt;li&gt;Integration with the &lt;code&gt;secrecy&lt;/code&gt; crate for even stricter passphrase handling.&lt;/li&gt;
&lt;li&gt;WASM support for browser‑based applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contributions are welcome! The code is MIT licensed and lives on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mroczect/librage" rel="noopener noreferrer"&gt;github.com/mroczect/librage&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you need encryption that’s secure by design and simple by choice, give librage a try. It removes the ceremony so you can focus on your application’s logic. I’d love to hear your feedback or see what you build with it—drop a comment or open an issue on the repo!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>cryptography</category>
      <category>encryption</category>
      <category>age</category>
    </item>
    <item>
      <title>Introducing age-credentials: A Backend‑Agnostic Account Engine for the Age Encryption Era</title>
      <dc:creator>mroczect</dc:creator>
      <pubDate>Thu, 06 Aug 2026 13:26:18 +0000</pubDate>
      <link>https://dev.to/mroczect/introducing-age-credentials-a-backend-agnostic-account-engine-for-the-age-encryption-era-2bnc</link>
      <guid>https://dev.to/mroczect/introducing-age-credentials-a-backend-agnostic-account-engine-for-the-age-encryption-era-2bnc</guid>
      <description>&lt;h1&gt;
  
  
  Introducing age-credentials: A Backend‑Agnostic Account Engine for the Age Encryption Era
&lt;/h1&gt;

&lt;p&gt;Managing user accounts and their cryptographic keys is one of the most critical parts of any secure application. Yet, many solutions either tie you to a specific storage backend, force you to use a command‑line tool, or leave the encryption details to you. &lt;strong&gt;age-credentials&lt;/strong&gt; changes that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;age-credentials&lt;/strong&gt; is a pure Rust library that provides a complete, backend‑agnostic account management engine built on the &lt;a href="https://age-encryption.org" rel="noopener noreferrer"&gt;age&lt;/a&gt; encryption format. It handles everything from key generation and passphrase‑protected private key storage, to encrypting and decrypting data for specific accounts, all through a clean, trait‑based API. You bring your storage (files, databases, vaults, memory – anything that implements a simple trait), and the engine does the rest.&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk through why I built age-credentials, how it works, and how you can start using it today.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Reinventing Account Management Every Time
&lt;/h2&gt;

&lt;p&gt;Whether you're building a CLI tool that needs to store API keys securely, a collaborative editor that manages identities, or a service that provisions short‑lived credentials, you always end up solving the same problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate a strong cryptographic key pair.&lt;/li&gt;
&lt;li&gt;Encrypt the private key with a user‑provided passphrase so it can be recovered later.&lt;/li&gt;
&lt;li&gt;Associate a public identity (name, email, public key) with the account.&lt;/li&gt;
&lt;li&gt;Provide an easy way to encrypt data for, and decrypt data from, a specific account.&lt;/li&gt;
&lt;li&gt;Support import/export workflows.&lt;/li&gt;
&lt;li&gt;Never let secret keys or plaintext leak into logs or core dumps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most solutions either hardcode a filesystem layout, assume a particular database, or leave the encryption entirely to you. age-credentials decouples the &lt;em&gt;business logic&lt;/em&gt; (what you want to do) from the &lt;em&gt;storage&lt;/em&gt; (how you do it).&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture: Traits, Not Assumptions
&lt;/h2&gt;

&lt;p&gt;At the heart of the library lies the &lt;strong&gt;&lt;code&gt;AccountBackend&lt;/code&gt;&lt;/strong&gt; trait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;AccountBackend&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;save_identity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;load_identity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Fingerprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;delete_identity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Fingerprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;store_encrypted_private_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Fingerprint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;load_encrypted_private_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Fingerprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Zeroizing&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;list_fingerprints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fingerprint&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;find_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fingerprint&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By implementing this trait, you decide where identities and encrypted private keys live. The library never touches a filesystem or a database directly. This makes it easy to integrate into any existing system – whether you're using SQLite, a cloud object store, or just a &lt;code&gt;HashMap&lt;/code&gt; for testing.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;code&gt;AccountEngine&lt;/code&gt;&lt;/strong&gt; struct (stateless) then provides all the high‑level operations on top of any backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;create_account&lt;/code&gt; – generates a key pair, encrypts the secret key with a passphrase, stores the identity and the encrypted blob.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;encrypt_for_account&lt;/code&gt; – encrypts data to a user's public key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;decrypt_for_account&lt;/code&gt; – unlocks the private key with the passphrase and decrypts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;change_passphrase&lt;/code&gt; – re‑wraps the private key with a new passphrase.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;export_account&lt;/code&gt; / &lt;code&gt;import_account&lt;/code&gt; – transfer accounts as passphrase‑encrypted hex blobs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delete_account&lt;/code&gt;, &lt;code&gt;list_accounts&lt;/code&gt;, &lt;code&gt;find_by_email&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All methods are synchronous, return &lt;code&gt;Result&amp;lt;_, AccountError&amp;gt;&lt;/code&gt;, and &lt;strong&gt;never panic&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Strong Typing and Validation
&lt;/h2&gt;

&lt;p&gt;Every domain object is validated at construction. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This validates that the name is between 2 and 255 characters, contains only allowed characters, and that the email has exactly one &lt;code&gt;@&lt;/code&gt; sign, non‑empty parts, and a valid length. You can't accidentally create an invalid user.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Fingerprint&lt;/code&gt; is always a non‑empty hexadecimal string. If you try to create one from garbage, you get an &lt;code&gt;AccountError::InvalidFingerprint&lt;/code&gt; immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security by Design
&lt;/h2&gt;

&lt;p&gt;Several features make age-credentials safe by default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zeroize&lt;/strong&gt; – Secret keys (&lt;code&gt;Zeroizing&amp;lt;String&amp;gt;&lt;/code&gt;) and decrypted plaintext (&lt;code&gt;Zeroizing&amp;lt;Vec&amp;lt;u8&amp;gt;&amp;gt;&lt;/code&gt;) are automatically wiped from memory when they go out of scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passphrase minimum length&lt;/strong&gt; – at least 8 characters, enforced at the engine level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No panics&lt;/strong&gt; – every fallible operation propagates a structured &lt;code&gt;AccountError&lt;/code&gt; enum. There are no hidden &lt;code&gt;unwrap()&lt;/code&gt; calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegation to librage&lt;/strong&gt; – all cryptographic operations are performed by the well‑audited &lt;code&gt;rage&lt;/code&gt; library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Armored encryption&lt;/strong&gt; – optionally wrap ciphertext in the age‑standard PEM armor, making it safe for transmission over text channels.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Example: Creating an Account and Sending an Encrypted Message
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;age_credentials&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;account&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AccountEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;age_credentials&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;traits&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AccountBackend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;age_credentials&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;types&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Your custom backend (e.g., files, memory, database)&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;MyBackend&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;AccountBackend&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;MyBackend&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;MyBackend&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Create an account for Alice&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;alice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice Example"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AccountEngine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create_account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"strong‑passphrase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// optional label&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Encrypt a message to Alice&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AccountEngine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;encrypt_for_account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="py"&gt;.fingerprint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;b"Hello, Alice!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Alice decrypts it later with her passphrase&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;plaintext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AccountEngine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;decrypt_for_account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="py"&gt;.fingerprint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"strong‑passphrase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ciphertext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;b"Hello, Alice!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No files were harmed in this example. Replace &lt;code&gt;MyBackend&lt;/code&gt; with your production storage, and you have a fully working account system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Export and Import
&lt;/h2&gt;

&lt;p&gt;Need to move an account between devices? The engine supports an export format that bundles the identity and encrypted private key into a single passphrase‑protected blob:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;exported&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AccountEngine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;export_account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="py"&gt;.fingerprint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"original‑passphrase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// `exported` is a hex‑encoded string&lt;/span&gt;

&lt;span class="c1"&gt;// On another device, with a different backend:&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;imported_identity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AccountEngine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;import_account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;backend2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;exported&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"original‑passphrase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No plaintext private key ever leaves the library.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Is This For?
&lt;/h2&gt;

&lt;p&gt;age-credentials is designed for &lt;strong&gt;Rust developers&lt;/strong&gt; who need an embeddable, secure account system without reinventing the wheel. Some use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLI tools&lt;/strong&gt; that store per‑user secrets (API tokens, SSH keys).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decentralised applications&lt;/strong&gt; where users manage their own identities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaborative editors&lt;/strong&gt; that need to encrypt documents for specific participants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provisioning systems&lt;/strong&gt; that generate one‑off credentials and encrypt them for a recipient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Games&lt;/strong&gt; that save per‑player encrypted profiles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the backend is abstract, you can even use it to prototype entirely in memory before committing to a persistent storage design.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;The library is fully functional and ready for production use. Some enhancements on the horizon:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reference filesystem backend implementation.&lt;/li&gt;
&lt;li&gt;Support for custom account metadata.&lt;/li&gt;
&lt;li&gt;Account status (active, suspended, revoked).&lt;/li&gt;
&lt;li&gt;Integration with version‑controlled storage via &lt;a href="https://github.com/mroczect/libvcrtl" rel="noopener noreferrer"&gt;libvctrl&lt;/a&gt; for an auditable account history.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contributions are very welcome! The code is MIT licensed and available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mroczect/age-credentials" rel="noopener noreferrer"&gt;github.com/mroczect/age-credentials&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you're tired of re‑implementing account management for every project, give age-credentials a try. It takes care of the hard parts so you can focus on building your application. I'd love to hear your feedback and use cases in the comments!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>cryptography</category>
      <category>security</category>
      <category>library</category>
    </item>
    <item>
      <title>Introducing libvctrl: A Content‑Addressed Version Control Engine for Rust Applications</title>
      <dc:creator>mroczect</dc:creator>
      <pubDate>Thu, 06 Aug 2026 13:23:43 +0000</pubDate>
      <link>https://dev.to/mroczect/introducing-libvctrl-a-content-addressed-version-control-engine-for-rust-applications-3236</link>
      <guid>https://dev.to/mroczect/introducing-libvctrl-a-content-addressed-version-control-engine-for-rust-applications-3236</guid>
      <description>&lt;p&gt;&lt;strong&gt;libvctrl&lt;/strong&gt; is an open‑source library that provides a complete, content‑addressed version control engine designed to be embedded directly into Rust applications. It offers the full machinery of modern version control – objects, references, commits, merges, cryptographic signatures, garbage collection, and much more – all without depending on an external CLI tool or shelling out to Git.&lt;/p&gt;

&lt;p&gt;The library is built on a clean trait‑based architecture. Every major component (hashing, encoding, storage, merging, signing) is defined as a trait, allowing you to plug in custom implementations or use the robust defaults. The default hasher is SHA‑512, the default encoding is a compact binary format with versioning, and the reference store can be backed by memory, an append‑only file, or wrapped in &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;&amp;gt;&amp;gt;&lt;/code&gt; for thread‑safe access.&lt;/p&gt;

&lt;p&gt;Whether you are building a collaborative editor, a game that needs to version its assets, a document management system, or a custom DevOps tool, libvctrl gives you a production‑ready foundation for versioning arbitrary data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature Overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complete object model&lt;/strong&gt; – Blob, Tree, Commit, and Tag. All objects are identified by a 64‑byte SHA‑512 hash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pluggable hashing&lt;/strong&gt; – &lt;code&gt;Hasher&lt;/code&gt; trait; default &lt;code&gt;Sha512Hasher&lt;/code&gt; uses type‑prefixed SHA‑512.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pluggable encoding&lt;/strong&gt; – &lt;code&gt;Encoder&lt;/code&gt; / &lt;code&gt;Decoder&lt;/code&gt; traits; default binary format with strict size limits to prevent malformed input attacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full version control operations&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Commit creation, checkout, diff, log, log with graph topology, blame, stash&lt;/li&gt;
&lt;li&gt;Merge (three‑way, octopus), rebase, cherry‑pick, revert&lt;/li&gt;
&lt;li&gt;Branch and tag management (lightweight and annotated, with cryptographic signing)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage backends&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MemoryStore&lt;/code&gt; – hash‑map‑based store, ideal for testing and in‑memory usage.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FileStore&lt;/code&gt; – append‑only binary file with tombstone deletion for garbage collection.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SyncAdapter&lt;/code&gt; – wraps any &lt;code&gt;ObjectStore&lt;/code&gt; / &lt;code&gt;RefStore&lt;/code&gt; in &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;&amp;gt;&amp;gt;&lt;/code&gt; for safe concurrent access.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cryptographic signing &amp;amp; verification&lt;/strong&gt; – trait‑based &lt;code&gt;Signer&lt;/code&gt; / &lt;code&gt;Verifier&lt;/code&gt; (example with Ed25519 included in tests).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced merging&lt;/strong&gt; – recursive three‑way merge with pluggable &lt;code&gt;ConflictResolver&lt;/code&gt; trait.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge base detection&lt;/strong&gt; – BFS‑based &lt;code&gt;find_merge_base&lt;/code&gt; and &lt;code&gt;is_ancestor&lt;/code&gt; helpers with a configurable search limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reflog&lt;/strong&gt; – optional log of all reference changes; &lt;code&gt;MemoryRefStore&lt;/code&gt; implements &lt;code&gt;ReflogStore&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RevWalk&lt;/strong&gt; – timestamp‑ordered commit iterator that returns &lt;code&gt;(Hash, Commit)&lt;/code&gt; pairs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patch generation &amp;amp; application&lt;/strong&gt; – binary patches for tree‑to‑tree transformations (blob‑only).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Garbage collection&lt;/strong&gt; – mark‑and‑sweep reachable objects; removes unreachable ones and writes tombstones in the file store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index&lt;/strong&gt; – a staging‑area abstraction (&lt;code&gt;Index&lt;/code&gt;) to incrementally build a tree before committing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility&lt;/strong&gt; – custom encoders, hashers, merge strategies, conflict resolvers, and transport layers.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Data Model
&lt;/h2&gt;

&lt;p&gt;All objects are content‑addressed. A &lt;code&gt;Hash&lt;/code&gt; is a fixed 64‑byte array, displayed as a 128‑character hex string.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Blob&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Raw byte content.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TreeEntry&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A named entry (&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;kind&lt;/code&gt; – Blob or Tree, &lt;code&gt;hash&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Tree&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An immutable, sorted list of &lt;code&gt;TreeEntry&lt;/code&gt;. Duplicate names are rejected.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Points to a tree, lists parent commits, and stores author, committer, timestamp, message, an optional signature, and custom headers (key‑value pairs).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lightweight (a direct ref) or annotated (a full object with tagger, message, timestamp, optional signature).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Object&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enum &lt;code&gt;Blob&lt;/code&gt;, &lt;code&gt;Tree&lt;/code&gt;, &lt;code&gt;Commit&lt;/code&gt;, or &lt;code&gt;Tag&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name and email, each 1–255 characters.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Hashing &amp;amp; Encoding
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Hasher&lt;/code&gt; Trait
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;Hasher&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;hash_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;hash_tree_encoded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;hash_commit_encoded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;hash_tag_encoded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default &lt;code&gt;Sha512Hasher&lt;/code&gt; follows the pattern &lt;code&gt;type SP length NUL data&lt;/code&gt;, hashed with SHA‑512. The &lt;code&gt;HashVerifier&lt;/code&gt; extension trait (auto‑implemented) adds &lt;code&gt;verify_*&lt;/code&gt; methods that recompute the hash and compare it to the expected value.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Encoder&lt;/code&gt; / &lt;code&gt;Decoder&lt;/code&gt; Traits
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;Encoder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;encode_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;encode_commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;encode_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;Decoder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;decode_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;decode_commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;decode_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default binary encoder/decoder uses a compact, versioned format. Commit version 2 and tag version 2 support additional headers and signatures, while enforcing tight limits on field sizes (e.g., max tree entries 100 000, max message length 1 MiB) to prevent resource exhaustion.&lt;/p&gt;




&lt;h2&gt;
  
  
  Storage Abstraction
&lt;/h2&gt;

&lt;p&gt;libvctrl separates concerns: object storage is handled by &lt;code&gt;ObjectStore&lt;/code&gt;, reference management by &lt;code&gt;RefStore&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ObjectStore&lt;/code&gt; Trait
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;ObjectStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;all_hashes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;RefStore&lt;/code&gt; Trait
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;RefStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;set_ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;delete_ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;set_head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;head_ref_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;list_refs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extension Trait &lt;code&gt;ObjectStoreExt&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Provides convenient helper methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_verified&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;Encoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hasher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;Hasher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;get_verified&lt;/code&gt; recomputes the hash of the encoded object and returns a &lt;code&gt;Corrupted&lt;/code&gt; error on mismatch, enabling integrity checks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built‑in Backends
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MemoryStore&lt;/code&gt; + &lt;code&gt;MemoryRefStore&lt;/code&gt;&lt;/strong&gt; – Fast, in‑memory hash maps. &lt;code&gt;MemoryRefStore&lt;/code&gt; also implements &lt;code&gt;ReflogStore&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;FileStore&lt;/code&gt;&lt;/strong&gt; – Append‑only binary file with a magic header, versioning, and record types for objects, ref operations, and tombstone deletions. It is crash‑safe and reads the entire file at startup to rebuild indices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SyncAdapter&amp;lt;S&amp;gt;&lt;/code&gt;&lt;/strong&gt; – Generic wrapper that takes an &lt;code&gt;ObjectStore&lt;/code&gt; or &lt;code&gt;RefStore&lt;/code&gt; and exposes &lt;code&gt;SyncObjectStore&lt;/code&gt; / &lt;code&gt;SyncRefStore&lt;/code&gt; with &lt;code&gt;&amp;amp;self&lt;/code&gt; methods, using an internal &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;S&amp;gt;&amp;gt;&lt;/code&gt;. Mutex poisoning is mapped to a &lt;code&gt;Backend&lt;/code&gt; error.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Command Trait
&lt;/h2&gt;

&lt;p&gt;Every operation is encapsulated as a struct that implements the &lt;code&gt;Command&lt;/code&gt; trait. This design keeps the library free of side effects and makes it straightforward to integrate with different runtimes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;Command&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;ObjectStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;refs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;RefStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Available Commands
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Struct Fields (abbreviated)&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;author&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (initial commit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CreateCommit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;tree_hash&lt;/code&gt;, &lt;code&gt;parents&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;committer&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (new commit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Checkout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tree_hash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Vec&amp;lt;(String, Vec&amp;lt;u8&amp;gt;)&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(none)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Vec&amp;lt;Commit&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LogGraph&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;head&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Vec&amp;lt;GraphCommit&amp;gt;&lt;/code&gt; (includes parent indices)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DiffCommits&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;old_commit&lt;/code&gt;, &lt;code&gt;new_commit&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Vec&amp;lt;DiffEntry&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DiffPatch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;old_tree_hash&lt;/code&gt;, &lt;code&gt;new_tree_hash&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; (binary patch)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ApplyPatch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;base_tree_hash&lt;/code&gt;, &lt;code&gt;patch_data&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (new tree)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CherryPick&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;commit_hash&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;committer&lt;/code&gt;, &lt;code&gt;merger&lt;/code&gt;, &lt;code&gt;resolver&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (new commit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Revert&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;commit_hash&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;committer&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (revert commit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MergeCommand&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;base&lt;/code&gt;, &lt;code&gt;ours&lt;/code&gt;, &lt;code&gt;theirs&lt;/code&gt;, &lt;code&gt;merger&lt;/code&gt;, &lt;code&gt;resolver&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (merged tree)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MergeBranch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;branch_name&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;committer&lt;/code&gt;, &lt;code&gt;merger&lt;/code&gt;, &lt;code&gt;resolver&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (merge commit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OctopusMerge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;branch_names&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;committer&lt;/code&gt;, &lt;code&gt;merger&lt;/code&gt;, &lt;code&gt;resolver&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (merge commit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Rebase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;upstream&lt;/code&gt;, &lt;code&gt;onto&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;committer&lt;/code&gt;, &lt;code&gt;merger&lt;/code&gt;, &lt;code&gt;resolver&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (new HEAD)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CreateBranch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;name&lt;/code&gt; (must start with &lt;code&gt;refs/heads/&lt;/code&gt;), &lt;code&gt;hash&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DeleteBranch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GetBranch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Option&amp;lt;Hash&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SetHead&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;target&lt;/code&gt; (branch name or hash)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ListBranches&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(none)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Vec&amp;lt;(String, Hash, bool)&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CreateLightweightTag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;target&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CreateAnnotatedTag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;target&lt;/code&gt;, &lt;code&gt;tagger&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;, optional &lt;code&gt;signer&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (tag object)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DeleteTag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ListTags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(none)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Vec&amp;lt;String&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VerifyCommit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;commit_hash&lt;/code&gt;, &lt;code&gt;verifier&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VerifyTag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;tag_hash&lt;/code&gt;, &lt;code&gt;verifier&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Describe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;commit_hash&lt;/code&gt;, &lt;code&gt;max_commits_to_search&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Option&amp;lt;String&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StashPush&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;tree_hash&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;, &lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Hash&lt;/code&gt; (stash commit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StashPop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(none)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Option&amp;lt;Hash&amp;gt;&lt;/code&gt; (tree hash)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StashList&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(none)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Vec&amp;lt;(String, Hash)&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Annotate&lt;/code&gt; (Blame)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;start_commit&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Vec&amp;lt;BlameEntry&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Show&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;commit_hash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ShowOutput { commit, diff }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Fsck&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;encoder&lt;/code&gt;, &lt;code&gt;hasher&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Vec&amp;lt;VctrlError&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All commands are synchronous and return a &lt;code&gt;Result&lt;/code&gt; with the appropriate output type.&lt;/p&gt;




&lt;h2&gt;
  
  
  Diff &amp;amp; Merge
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tree Diffing
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;TreeDiff&lt;/code&gt; trait is implemented by &lt;code&gt;TreeDiffer&lt;/code&gt;. It compares two trees and returns a list of &lt;code&gt;DiffEntry&lt;/code&gt;, each with a &lt;code&gt;DiffKind&lt;/code&gt; of &lt;code&gt;Added&lt;/code&gt;, &lt;code&gt;Removed&lt;/code&gt;, or &lt;code&gt;Modified&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;DiffKind&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Added&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;new_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Removed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Modified&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;old_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hash&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Three‑Way Merge
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;ThreeWayMerge&lt;/code&gt; trait is implemented by &lt;code&gt;ThreeWayMerger&lt;/code&gt;. It performs a recursive tree merge and uses a &lt;code&gt;ConflictResolver&lt;/code&gt; for blob conflicts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;ConflictResolver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;theirs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resolver can be a simple “ours” or “theirs” strategy, a line‑based merger, or any custom logic. When a conflict cannot be resolved, the merge returns a &lt;code&gt;VctrlError::MergeConflict&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merge Base
&lt;/h3&gt;

&lt;p&gt;Two utility functions assist with merging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;find_merge_base(store, a, b)&lt;/code&gt; – returns the best common ancestor, using a BFS limited to 100 000 steps.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;is_ancestor(store, ancestor, descendant)&lt;/code&gt; – checks fast‑forward conditions.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Additional Components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Reflog
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;ReflogStore&lt;/code&gt; trait and &lt;code&gt;ReflogEntry&lt;/code&gt; struct provide an audit trail of reference updates. &lt;code&gt;MemoryRefStore&lt;/code&gt; implements this trait automatically, recording every &lt;code&gt;set_ref&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  RevWalk
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;RevWalk&lt;/code&gt; is a timestamp‑ordered iterator over commits. Starting from one or more tips, it yields &lt;code&gt;(Hash, Commit)&lt;/code&gt; pairs using a binary heap. It is an efficient way to traverse history in chronological order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Patch
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;generate_patch&lt;/code&gt; and &lt;code&gt;apply_patch&lt;/code&gt; create and apply binary patches between two trees. The patch format is versioned and currently limited to blob changes (tree‑level patches return an error). These operations are exposed as &lt;code&gt;DiffPatch&lt;/code&gt; and &lt;code&gt;ApplyPatch&lt;/code&gt; commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Garbage Collection
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mark_reachable&lt;/code&gt; – collects the set of all objects reachable from any ref and HEAD.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gc&lt;/code&gt; – removes unreachable objects. In &lt;code&gt;FileStore&lt;/code&gt;, removal writes a tombstone record so that the object stays deleted across restarts.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Usage Example
&lt;/h2&gt;

&lt;p&gt;Below is a minimal example that initializes a repository, adds a blob and a tree, and creates a commit – all using the in‑memory backends.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;libvctrl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;MemoryStore&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;refs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;MemoryRefStore&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Initialize the repository&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;hasher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Sha512Hasher&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;root_commit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;refs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a blob&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;blob_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;b"Hello, libvctrl!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blob_data&lt;/span&gt;&lt;span class="nf"&gt;.to_vec&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hasher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sha512Hasher&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;blob_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hasher&lt;/span&gt;&lt;span class="nf"&gt;.hash_blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blob_data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;blob_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Build a tree&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TreeEntry&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"README.md"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;EntryKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blob_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="nf"&gt;.encode_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tree_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hasher&lt;/span&gt;&lt;span class="nf"&gt;.hash_tree_encoded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tree_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a commit on top of the initial commit&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;commit_cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CreateCommit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;tree_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;root_commit&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;committer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Add README"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BinaryEncoder&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;hasher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Sha512Hasher&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_commit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;commit_cmd&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;refs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New commit: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_commit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same pattern can be used with &lt;code&gt;FileStore&lt;/code&gt; for persistent storage – just replace &lt;code&gt;MemoryStore::new()&lt;/code&gt; with &lt;code&gt;FileStore::open("myrepo.vctrl")?&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Error Handling
&lt;/h2&gt;

&lt;p&gt;All fallible functions return &lt;code&gt;Result&amp;lt;T, VctrlError&amp;gt;&lt;/code&gt;. The error type is exhaustive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;VctrlError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HashError&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TreeError&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;InvalidRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;MergeConflict&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;Io&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Serialization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Other&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Corrupted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Unsupported&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are no hidden panics in the library. Every error path is propagated cleanly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Minimum Supported Rust Version
&lt;/h2&gt;

&lt;p&gt;libvctrl requires Rust &lt;strong&gt;1.85.0&lt;/strong&gt; (stable) or later.&lt;/p&gt;




&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;Licensed under the &lt;strong&gt;MIT License&lt;/strong&gt;. See the &lt;a href="https://github.com/mroczect/libvcrtl/blob/main/LICENSE" rel="noopener noreferrer"&gt;LICENSE&lt;/a&gt; file for details.&lt;/p&gt;




&lt;h2&gt;
  
  
  Repository &amp;amp; Contribution
&lt;/h2&gt;

&lt;p&gt;The source code, full documentation, and issue tracker are available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mroczect/libvcrtl" rel="noopener noreferrer"&gt;github.com/mroczect/libvcrtl&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contributions, bug reports, and feature suggestions are welcome. If you are building an application that requires embedded version control, give libvctrl a try – it is ready for real‑world use and open to extension.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>versioncontrol</category>
      <category>cryptography</category>
      <category>library</category>
    </item>
    <item>
      <title>Building an Offline Authenticator with Rust and Age Encryption</title>
      <dc:creator>mroczect</dc:creator>
      <pubDate>Thu, 06 Aug 2026 13:07:52 +0000</pubDate>
      <link>https://dev.to/mroczect/building-an-offline-authenticator-with-rust-and-age-encryption-28dn</link>
      <guid>https://dev.to/mroczect/building-an-offline-authenticator-with-rust-and-age-encryption-28dn</guid>
      <description>&lt;h1&gt;
  
  
  Building an Offline Authenticator with Rust and Age Encryption
&lt;/h1&gt;

&lt;p&gt;I'm building a Rust library called &lt;a href="https://github.com/mroczect/age-auth" rel="noopener noreferrer"&gt;&lt;strong&gt;age-auth&lt;/strong&gt;&lt;/a&gt; — a toolkit for creating offline, one‑time password systems where the secret is always encrypted with &lt;a href="https://age-encryption.org" rel="noopener noreferrer"&gt;age&lt;/a&gt; (X25519). Think of it as the cryptographic foundation for a super‑secure, offline‑first authenticator app like Google Authenticator, but without ever storing or transmitting the secret in plain text.&lt;/p&gt;

&lt;p&gt;In this post I'll walk through why I started the project, the architecture I chose, and how you can use it today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Most OTP apps store the shared secret in plain text (or with weak protection). If someone extracts the app's data, all accounts enrolled in that authenticator are compromised. I wanted a library that forces the secret to be &lt;strong&gt;encrypted at rest&lt;/strong&gt; and only decrypted on‑demand, using modern public‑key cryptography.&lt;/p&gt;

&lt;p&gt;The constraints I set for myself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user generates an age keypair offline (public key for provisioning, private key for decryption).&lt;/li&gt;
&lt;li&gt;The server never sees the private key — it only encrypts a fresh OTP secret to the user's public key.&lt;/li&gt;
&lt;li&gt;The user's client library can decrypt the ciphertext and generate TOTP/HOTP codes, all offline.&lt;/li&gt;
&lt;li&gt;No hidden network calls, no default configurations, no backdoors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Crate Architecture
&lt;/h2&gt;

&lt;p&gt;The workspace is split into four focused crates, plus a root re‑export crate:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Crate&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;libage_auth_handler&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Traits, error types, and validated wrapper types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;libage_crypto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Age encryption / decryption via &lt;a href="https://crates.io/crates/librage" rel="noopener noreferrer"&gt;librage&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;libage_otp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pure TOTP (RFC 6238) and HOTP (RFC 4226) engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;libage_authenticator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Combines crypto + OTP into a high‑level API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;age_auth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re‑exports everything as a single dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each crate can be used independently, so you could just use &lt;code&gt;libage_otp&lt;/code&gt; for a non‑encrypted OTP generator, or combine them to build a fully encrypted authenticator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strong Typing and Safety First
&lt;/h2&gt;

&lt;p&gt;Every cryptographic parameter is wrapped in a validated newtype from &lt;code&gt;libage_auth_handler&lt;/code&gt;. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Age public key – validated at construction&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Recipient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"age1..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Age secret key – never serialized, validated format&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;identity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AGE-SECRET-KEY-..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Secret data – automatically zeroized on drop&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"my secret"&lt;/span&gt;&lt;span class="nf"&gt;.to_vec&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This eliminates entire classes of bugs — you can't accidentally swap a public key with a private key, or use an invalid Base32 string, because the types enforce it at compile time.&lt;/p&gt;

&lt;p&gt;All sensitive containers (&lt;code&gt;Secret&lt;/code&gt;, &lt;code&gt;EncryptedPayload&lt;/code&gt;) use the &lt;code&gt;zeroize&lt;/code&gt; crate. When they go out of scope, the memory is scrubbed.&lt;/p&gt;

&lt;p&gt;Errors are unified under a single &lt;code&gt;AuthError&lt;/code&gt; enum (backed by &lt;code&gt;thiserror&lt;/code&gt;), and there's not a single &lt;code&gt;unwrap()&lt;/code&gt; or &lt;code&gt;expect()&lt;/code&gt; in production code — all failures propagate as &lt;code&gt;Result&amp;lt;T, AuthError&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Offline Flow
&lt;/h2&gt;

&lt;p&gt;Here's a minimal example of the complete workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;age_auth&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AgeAuthenticator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generate_keypair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;traits&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Authenticator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;types&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cursor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Create the authenticator&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AgeAuthenticator&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Generate a keypair (offline)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_keypair&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Provision a Base32 OTP secret&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"JBSWY3DPEHPK3PXP"&lt;/span&gt;&lt;span class="nf"&gt;.to_vec&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;encrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="nf"&gt;.provision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Later, when the user needs a code:&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;identity_reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Cursor&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;totp_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="nf"&gt;.generate_totp_from_encrypted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;identity_reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your one‑time code is: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;totp_code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The secret is never stored in plain text on disk — you keep the encrypted payload anywhere, and only when you present the private key do you get the TOTP code.&lt;/p&gt;

&lt;p&gt;Multiple recipients are also supported (&lt;code&gt;provision_multiple&lt;/code&gt;), so you can encrypt a secret for several devices at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  OTP Engine – RFC Compliance
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;libage_otp&lt;/code&gt; crate implements HOTP and TOTP from scratch, with full test vectors from RFC 4226 and RFC 6238. It supports SHA‑1, SHA‑256, and SHA‑512, with SHA‑256 as the default (because SHA‑1 is legacy). The dynamic truncation is done exactly as specified.&lt;/p&gt;

&lt;p&gt;Benchmarks show that a single HOTP generation takes ~442 ns (SHA‑1) to ~1.2 µs (SHA‑512) on a modern CPU, so performance is more than adequate for interactive use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development &amp;amp; CI
&lt;/h2&gt;

&lt;p&gt;The project uses a workspace with a custom version management script (&lt;code&gt;scripts/bump_version.sh&lt;/code&gt;) that keeps all crates at the same version. GitHub Actions runs formatting, clippy, and tests on every push, and a separate workflow automatically publishes all crates to crates.io when a new tag is pushed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;otpauth://&lt;/code&gt; URL generation&lt;/strong&gt; so you can scan a QR code directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming encryption/decryption&lt;/strong&gt; for very large secrets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fuzz testing&lt;/strong&gt; to harden the parser and crypto boundaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better documentation&lt;/strong&gt; on docs.rs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm actively developing this library and welcome feedback, contributions, and real‑world use cases. You can find the code at &lt;a href="https://github.com/mroczect/age-auth" rel="noopener noreferrer"&gt;github.com/mroczect/age-auth&lt;/a&gt; and the crates on &lt;a href="https://crates.io" rel="noopener noreferrer"&gt;crates.io&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Do you think an offline‑first authenticator approach like this could improve security in your workflows? Let me know in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>cryptography</category>
      <category>security</category>
      <category>otp</category>
    </item>
  </channel>
</rss>
