Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

rfc9162-log

rfc9162-log is an indexed log on top of the tile store. Keys from the caller’s function are sorted into runs. Each run’s manifest is appended to the log, so a checkpoint over the log covers the index. Sidecar stores a directory as entries in that log. The rfc9162-sidecar binary is the same operations from a shell.

The key function receives entry bytes and returns extra lookup keys. The identity hash is added by the tile layer and can be omitted here.

Seal a run and look up a key

//! An indexed log: seal, find, range, compact, recover.

use std::sync::Arc;

use rfc9162_log::{AuthenticatedLog, Keys, Store};
use rfc9162_tiles::MemoryStore;

fn keys() -> Keys {
    Arc::new(|entry: &[u8]| {
        entry
            .split(|byte| *byte == b' ')
            .filter(|field| !field.is_empty())
            .map(<[u8]>::to_vec)
            .collect()
    })
}

fn proves(log: &Store<MemoryStore>, index: u64, path: &[[u8; 32]], tree_size: u64) -> bool {
    let bytes = log
        .index()
        .expect("index")
        .reader()
        .get(index)
        .expect("get")
        .expect("present");
    rfc9162::verify_inclusion(
        &rfc9162::leaf_hash(&bytes),
        usize::try_from(index).unwrap(),
        usize::try_from(tree_size).unwrap(),
        path,
        &rfc9162::hex(&log.root()),
    )
}

fn main() {
    let mut log = Store::open(MemoryStore::new(), keys()).expect("log");
    let petsc = log.append(b"module=petsc pkg=petsc").expect("append");
    let openmpi = log.append(b"module=openmpi pkg=openmpi").expect("append");
    println!("appended {petsc} and {openmpi}, size {}", log.size());
    let run = log.seal().expect("seal");
    println!(
        "sealed {}..={} manifest {}",
        run.entries.start(),
        run.entries.end(),
        run.manifest_index.expect("logged"),
    );

    let found = log.find(b"module=petsc").expect("find");
    let hit = &found[0];
    println!(
        "find module=petsc index {} run {} inclusion {}",
        hit.entry.index,
        hit.run.is_some(),
        proves(&log, hit.entry.index, &hit.entry.path, hit.entry.tree_size)
    );
    let absent = log.find(b"module=absent").expect("find");
    println!("find module=absent -> {}", absent.len());
    let range = log
        .range(&b"module=o"[..]..=&b"module=petsc"[..])
        .expect("range");
    let indices: Vec<String> = range
        .iter()
        .map(|hit| hit.entry.index.to_string())
        .collect();
    println!(
        "range module=o..=module=petsc indices {}",
        indices.join(" ")
    );

    log.append(b"module=fftw pkg=fftw").expect("append");
    let second = log.seal().expect("seal");
    println!("second manifest {}", second.manifest_index.expect("logged"));
    let compaction = log.compact().expect("compact");
    println!(
        "compact replaced {:?} written {}",
        compaction.replaced, compaction.written
    );
    let again = log.find(b"module=fftw").expect("find");
    println!(
        "find after compact inclusion {}",
        proves(
            &log,
            again[0].entry.index,
            &again[0].entry.path,
            again[0].entry.tree_size,
        )
    );

    let mut recovered =
        Store::open_trusting(MemoryStore::new(), keys(), 0, rfc9162::root(&[])).expect("trust");
    recovered.append(b"module=petsc pkg=petsc").expect("append");
    let recovery = recovered.recover().expect("recover");
    println!(
        "recover checkpoint {} replayed {} quarantined {}",
        recovery.checkpoint_size, recovery.replayed, recovery.quarantined
    );

    let mut split =
        Store::open_split(MemoryStore::new(), MemoryStore::new(), keys()).expect("split");
    split.append(b"module=petsc pkg=petsc").expect("append");
    split.seal().expect("seal");
    println!(
        "split data size {} index size {}",
        split.size(),
        split.index_writer().expect("index log").size()
    );
}

seal covers entries 0 and 1 and logs the manifest at index 2. find returns a Located value: the entry’s inclusion path, and the manifest’s inclusion path when the entry was inside a sealed run. The entry path verifies against log.root() at tree_size. A key that no entry carries returns an empty list. range walks keys in order from the start key through the end key, inclusive.

//! An indexed log: seal, find, range, compact, recover.

use std::sync::Arc;

use rfc9162_log::{AuthenticatedLog, Keys, Store};
use rfc9162_tiles::MemoryStore;

fn keys() -> Keys {
    Arc::new(|entry: &[u8]| {
        entry
            .split(|byte| *byte == b' ')
            .filter(|field| !field.is_empty())
            .map(<[u8]>::to_vec)
            .collect()
    })
}

fn proves(log: &Store<MemoryStore>, index: u64, path: &[[u8; 32]], tree_size: u64) -> bool {
    let bytes = log
        .index()
        .expect("index")
        .reader()
        .get(index)
        .expect("get")
        .expect("present");
    rfc9162::verify_inclusion(
        &rfc9162::leaf_hash(&bytes),
        usize::try_from(index).unwrap(),
        usize::try_from(tree_size).unwrap(),
        path,
        &rfc9162::hex(&log.root()),
    )
}

fn main() {
    let mut log = Store::open(MemoryStore::new(), keys()).expect("log");
    let petsc = log.append(b"module=petsc pkg=petsc").expect("append");
    let openmpi = log.append(b"module=openmpi pkg=openmpi").expect("append");
    println!("appended {petsc} and {openmpi}, size {}", log.size());
    let run = log.seal().expect("seal");
    println!(
        "sealed {}..={} manifest {}",
        run.entries.start(),
        run.entries.end(),
        run.manifest_index.expect("logged"),
    );

    let found = log.find(b"module=petsc").expect("find");
    let hit = &found[0];
    println!(
        "find module=petsc index {} run {} inclusion {}",
        hit.entry.index,
        hit.run.is_some(),
        proves(&log, hit.entry.index, &hit.entry.path, hit.entry.tree_size)
    );
    let absent = log.find(b"module=absent").expect("find");
    println!("find module=absent -> {}", absent.len());
    let range = log
        .range(&b"module=o"[..]..=&b"module=petsc"[..])
        .expect("range");
    let indices: Vec<String> = range
        .iter()
        .map(|hit| hit.entry.index.to_string())
        .collect();
    println!(
        "range module=o..=module=petsc indices {}",
        indices.join(" ")
    );

    log.append(b"module=fftw pkg=fftw").expect("append");
    let second = log.seal().expect("seal");
    println!("second manifest {}", second.manifest_index.expect("logged"));
    let compaction = log.compact().expect("compact");
    println!(
        "compact replaced {:?} written {}",
        compaction.replaced, compaction.written
    );
    let again = log.find(b"module=fftw").expect("find");
    println!(
        "find after compact inclusion {}",
        proves(
            &log,
            again[0].entry.index,
            &again[0].entry.path,
            again[0].entry.tree_size,
        )
    );

    let mut recovered =
        Store::open_trusting(MemoryStore::new(), keys(), 0, rfc9162::root(&[])).expect("trust");
    recovered.append(b"module=petsc pkg=petsc").expect("append");
    let recovery = recovered.recover().expect("recover");
    println!(
        "recover checkpoint {} replayed {} quarantined {}",
        recovery.checkpoint_size, recovery.replayed, recovery.quarantined
    );

    let mut split =
        Store::open_split(MemoryStore::new(), MemoryStore::new(), keys()).expect("split");
    split.append(b"module=petsc pkg=petsc").expect("append");
    split.seal().expect("seal");
    println!(
        "split data size {} index size {}",
        split.size(),
        split.index_writer().expect("index log").size()
    );
}

module=openmpi sorts before module=petsc, so the range module=o through module=petsc returns index 1, then index 0.

Compact and recover

A second seal makes a second run. compact merges the live runs, appends the new manifest, and leaves the replaced manifests in the log. A reader at an older size still has those entries. After compaction, find for module=fftw still verifies.

//! An indexed log: seal, find, range, compact, recover.

use std::sync::Arc;

use rfc9162_log::{AuthenticatedLog, Keys, Store};
use rfc9162_tiles::MemoryStore;

fn keys() -> Keys {
    Arc::new(|entry: &[u8]| {
        entry
            .split(|byte| *byte == b' ')
            .filter(|field| !field.is_empty())
            .map(<[u8]>::to_vec)
            .collect()
    })
}

fn proves(log: &Store<MemoryStore>, index: u64, path: &[[u8; 32]], tree_size: u64) -> bool {
    let bytes = log
        .index()
        .expect("index")
        .reader()
        .get(index)
        .expect("get")
        .expect("present");
    rfc9162::verify_inclusion(
        &rfc9162::leaf_hash(&bytes),
        usize::try_from(index).unwrap(),
        usize::try_from(tree_size).unwrap(),
        path,
        &rfc9162::hex(&log.root()),
    )
}

fn main() {
    let mut log = Store::open(MemoryStore::new(), keys()).expect("log");
    let petsc = log.append(b"module=petsc pkg=petsc").expect("append");
    let openmpi = log.append(b"module=openmpi pkg=openmpi").expect("append");
    println!("appended {petsc} and {openmpi}, size {}", log.size());
    let run = log.seal().expect("seal");
    println!(
        "sealed {}..={} manifest {}",
        run.entries.start(),
        run.entries.end(),
        run.manifest_index.expect("logged"),
    );

    let found = log.find(b"module=petsc").expect("find");
    let hit = &found[0];
    println!(
        "find module=petsc index {} run {} inclusion {}",
        hit.entry.index,
        hit.run.is_some(),
        proves(&log, hit.entry.index, &hit.entry.path, hit.entry.tree_size)
    );
    let absent = log.find(b"module=absent").expect("find");
    println!("find module=absent -> {}", absent.len());
    let range = log
        .range(&b"module=o"[..]..=&b"module=petsc"[..])
        .expect("range");
    let indices: Vec<String> = range
        .iter()
        .map(|hit| hit.entry.index.to_string())
        .collect();
    println!(
        "range module=o..=module=petsc indices {}",
        indices.join(" ")
    );

    log.append(b"module=fftw pkg=fftw").expect("append");
    let second = log.seal().expect("seal");
    println!("second manifest {}", second.manifest_index.expect("logged"));
    let compaction = log.compact().expect("compact");
    println!(
        "compact replaced {:?} written {}",
        compaction.replaced, compaction.written
    );
    let again = log.find(b"module=fftw").expect("find");
    println!(
        "find after compact inclusion {}",
        proves(
            &log,
            again[0].entry.index,
            &again[0].entry.path,
            again[0].entry.tree_size,
        )
    );

    let mut recovered =
        Store::open_trusting(MemoryStore::new(), keys(), 0, rfc9162::root(&[])).expect("trust");
    recovered.append(b"module=petsc pkg=petsc").expect("append");
    let recovery = recovered.recover().expect("recover");
    println!(
        "recover checkpoint {} replayed {} quarantined {}",
        recovery.checkpoint_size, recovery.replayed, recovery.quarantined
    );

    let mut split =
        Store::open_split(MemoryStore::new(), MemoryStore::new(), keys()).expect("split");
    split.append(b"module=petsc pkg=petsc").expect("append");
    split.seal().expect("seal");
    println!(
        "split data size {} index size {}",
        split.size(),
        split.index_writer().expect("index log").size()
    );
}

open_trusting records a size and root for recover to check. Keep that pair outside the store the writer updates. Here the trusted head is the empty tree. One append is replayed, and the consistency path from size 0 is empty.

//! An indexed log: seal, find, range, compact, recover.

use std::sync::Arc;

use rfc9162_log::{AuthenticatedLog, Keys, Store};
use rfc9162_tiles::MemoryStore;

fn keys() -> Keys {
    Arc::new(|entry: &[u8]| {
        entry
            .split(|byte| *byte == b' ')
            .filter(|field| !field.is_empty())
            .map(<[u8]>::to_vec)
            .collect()
    })
}

fn proves(log: &Store<MemoryStore>, index: u64, path: &[[u8; 32]], tree_size: u64) -> bool {
    let bytes = log
        .index()
        .expect("index")
        .reader()
        .get(index)
        .expect("get")
        .expect("present");
    rfc9162::verify_inclusion(
        &rfc9162::leaf_hash(&bytes),
        usize::try_from(index).unwrap(),
        usize::try_from(tree_size).unwrap(),
        path,
        &rfc9162::hex(&log.root()),
    )
}

fn main() {
    let mut log = Store::open(MemoryStore::new(), keys()).expect("log");
    let petsc = log.append(b"module=petsc pkg=petsc").expect("append");
    let openmpi = log.append(b"module=openmpi pkg=openmpi").expect("append");
    println!("appended {petsc} and {openmpi}, size {}", log.size());
    let run = log.seal().expect("seal");
    println!(
        "sealed {}..={} manifest {}",
        run.entries.start(),
        run.entries.end(),
        run.manifest_index.expect("logged"),
    );

    let found = log.find(b"module=petsc").expect("find");
    let hit = &found[0];
    println!(
        "find module=petsc index {} run {} inclusion {}",
        hit.entry.index,
        hit.run.is_some(),
        proves(&log, hit.entry.index, &hit.entry.path, hit.entry.tree_size)
    );
    let absent = log.find(b"module=absent").expect("find");
    println!("find module=absent -> {}", absent.len());
    let range = log
        .range(&b"module=o"[..]..=&b"module=petsc"[..])
        .expect("range");
    let indices: Vec<String> = range
        .iter()
        .map(|hit| hit.entry.index.to_string())
        .collect();
    println!(
        "range module=o..=module=petsc indices {}",
        indices.join(" ")
    );

    log.append(b"module=fftw pkg=fftw").expect("append");
    let second = log.seal().expect("seal");
    println!("second manifest {}", second.manifest_index.expect("logged"));
    let compaction = log.compact().expect("compact");
    println!(
        "compact replaced {:?} written {}",
        compaction.replaced, compaction.written
    );
    let again = log.find(b"module=fftw").expect("find");
    println!(
        "find after compact inclusion {}",
        proves(
            &log,
            again[0].entry.index,
            &again[0].entry.path,
            again[0].entry.tree_size,
        )
    );

    let mut recovered =
        Store::open_trusting(MemoryStore::new(), keys(), 0, rfc9162::root(&[])).expect("trust");
    recovered.append(b"module=petsc pkg=petsc").expect("append");
    let recovery = recovered.recover().expect("recover");
    println!(
        "recover checkpoint {} replayed {} quarantined {}",
        recovery.checkpoint_size, recovery.replayed, recovery.quarantined
    );

    let mut split =
        Store::open_split(MemoryStore::new(), MemoryStore::new(), keys()).expect("split");
    split.append(b"module=petsc pkg=petsc").expect("append");
    split.seal().expect("seal");
    println!(
        "split data size {} index size {}",
        split.size(),
        split.index_writer().expect("index log").size()
    );
}

open_split sends entries to one store and run manifests to another. After one append and one seal, the data log’s size is 1 and the index log’s size is 1.

//! An indexed log: seal, find, range, compact, recover.

use std::sync::Arc;

use rfc9162_log::{AuthenticatedLog, Keys, Store};
use rfc9162_tiles::MemoryStore;

fn keys() -> Keys {
    Arc::new(|entry: &[u8]| {
        entry
            .split(|byte| *byte == b' ')
            .filter(|field| !field.is_empty())
            .map(<[u8]>::to_vec)
            .collect()
    })
}

fn proves(log: &Store<MemoryStore>, index: u64, path: &[[u8; 32]], tree_size: u64) -> bool {
    let bytes = log
        .index()
        .expect("index")
        .reader()
        .get(index)
        .expect("get")
        .expect("present");
    rfc9162::verify_inclusion(
        &rfc9162::leaf_hash(&bytes),
        usize::try_from(index).unwrap(),
        usize::try_from(tree_size).unwrap(),
        path,
        &rfc9162::hex(&log.root()),
    )
}

fn main() {
    let mut log = Store::open(MemoryStore::new(), keys()).expect("log");
    let petsc = log.append(b"module=petsc pkg=petsc").expect("append");
    let openmpi = log.append(b"module=openmpi pkg=openmpi").expect("append");
    println!("appended {petsc} and {openmpi}, size {}", log.size());
    let run = log.seal().expect("seal");
    println!(
        "sealed {}..={} manifest {}",
        run.entries.start(),
        run.entries.end(),
        run.manifest_index.expect("logged"),
    );

    let found = log.find(b"module=petsc").expect("find");
    let hit = &found[0];
    println!(
        "find module=petsc index {} run {} inclusion {}",
        hit.entry.index,
        hit.run.is_some(),
        proves(&log, hit.entry.index, &hit.entry.path, hit.entry.tree_size)
    );
    let absent = log.find(b"module=absent").expect("find");
    println!("find module=absent -> {}", absent.len());
    let range = log
        .range(&b"module=o"[..]..=&b"module=petsc"[..])
        .expect("range");
    let indices: Vec<String> = range
        .iter()
        .map(|hit| hit.entry.index.to_string())
        .collect();
    println!(
        "range module=o..=module=petsc indices {}",
        indices.join(" ")
    );

    log.append(b"module=fftw pkg=fftw").expect("append");
    let second = log.seal().expect("seal");
    println!("second manifest {}", second.manifest_index.expect("logged"));
    let compaction = log.compact().expect("compact");
    println!(
        "compact replaced {:?} written {}",
        compaction.replaced, compaction.written
    );
    let again = log.find(b"module=fftw").expect("find");
    println!(
        "find after compact inclusion {}",
        proves(
            &log,
            again[0].entry.index,
            &again[0].entry.path,
            again[0].entry.tree_size,
        )
    );

    let mut recovered =
        Store::open_trusting(MemoryStore::new(), keys(), 0, rfc9162::root(&[])).expect("trust");
    recovered.append(b"module=petsc pkg=petsc").expect("append");
    let recovery = recovered.recover().expect("recover");
    println!(
        "recover checkpoint {} replayed {} quarantined {}",
        recovery.checkpoint_size, recovery.replayed, recovery.quarantined
    );

    let mut split =
        Store::open_split(MemoryStore::new(), MemoryStore::new(), keys()).expect("split");
    split.append(b"module=petsc pkg=petsc").expect("append");
    split.seal().expect("seal");
    println!(
        "split data size {} index size {}",
        split.size(),
        split.index_writer().expect("index log").size()
    );
}
appended 0 and 1, size 2
sealed 0..=1 manifest 2
find module=petsc index 0 run true inclusion true
find module=absent -> 0
range module=o..=module=petsc indices 1 0
second manifest 4
compact replaced [2, 4] written 5
find after compact inclusion true
recover checkpoint 0 replayed 1 quarantined 0
split data size 1 index size 1

The replaced manifests are indexes 2 and 4. The written manifest is index 5. See runs and recovery.

Record a directory in the library

Sidecar::put writes the file bytes once, under blob/<sha256>, then appends a JSON entry. snapshot seals the tail and signs a checkpoint. history returns each change with its proof. The entry JSON below is the bytes that were hashed.

//! Library sidecar: put, snapshot, history, sync a directory, include a child.

use std::collections::BTreeMap;

use ed25519_dalek::SigningKey;
use rfc9162_log::Sidecar;
use rfc9162_tiles::{MemoryStore, Signer};

fn signer(origin: &str, seed: u8) -> Signer {
    Signer::new(origin, SigningKey::from_bytes(&[seed; 32])).expect("signer")
}

fn meta(actor: &str) -> BTreeMap<String, String> {
    let mut meta = BTreeMap::new();
    meta.insert("actor".to_string(), actor.to_string());
    meta
}

fn main() {
    let mut sidecar = Sidecar::open(MemoryStore::new()).expect("sidecar");
    let index = sidecar
        .put("ssh/sshd_config", b"Port 22\n", meta("root"))
        .expect("put");
    println!("put {index}");
    let child_signer = signer("example.org/demo", 7);
    let note = sidecar
        .snapshot(&child_signer, "example.org/demo")
        .expect("snapshot");
    println!("snapshot bytes {}", note.to_text().len());
    let found = sidecar.history("ssh/sshd_config").expect("history");
    let item = &found[0];
    let bytes = sidecar
        .log()
        .index()
        .expect("index")
        .reader()
        .get(item.index)
        .expect("get")
        .expect("present");
    println!("entry {}", String::from_utf8(bytes.clone()).expect("utf-8"));
    println!(
        "history {} inclusion {} run {}",
        item.change.path,
        rfc9162::verify_inclusion(
            &rfc9162::leaf_hash(&bytes),
            usize::try_from(item.index).unwrap(),
            usize::try_from(item.located.entry.tree_size).unwrap(),
            &item.located.entry.path,
            &rfc9162::hex(&sidecar.root()),
        ),
        item.located.run.is_some()
    );
    let blob = sidecar
        .blob(item.change.blob.as_deref().expect("blob"))
        .expect("blob")
        .expect("stored");
    println!("blob {}", String::from_utf8(blob).expect("utf-8"));

    let root = tempfile::tempdir().expect("temp dir");
    let project = root.path().join("project");
    std::fs::create_dir_all(project.join("ssh")).expect("mkdir");
    std::fs::write(project.join("ssh/sshd_config"), b"Port 22\n").expect("write");
    std::fs::write(project.join("motd"), b"hello\n").expect("write");
    let mut synced_log = Sidecar::open(MemoryStore::new()).expect("sidecar");
    let synced = synced_log
        .sync_dir(&project, &[], &meta("root"))
        .expect("sync");
    println!(
        "sync added {} changed {} deleted {} unchanged {}",
        synced.added.len(),
        synced.changed.len(),
        synced.deleted.len(),
        synced.unchanged
    );
    std::fs::write(project.join("ssh/sshd_config"), b"Port 2222\n").expect("rewrite");
    std::fs::remove_file(project.join("motd")).expect("remove");
    let again = synced_log
        .sync_dir(&project, &[], &meta("root"))
        .expect("sync");
    println!(
        "resync added {} changed {} deleted {}",
        again.added.len(),
        again.changed.len(),
        again.deleted.len()
    );
    let tree = synced_log.tree_at(synced_log.size()).expect("tree");
    println!("tree");
    for (path, (index, change)) in &tree {
        println!(
            "  {path} index {index} bytes {}",
            change.size.expect("size")
        );
    }

    let mut child = Sidecar::open(MemoryStore::new()).expect("child");
    child
        .put("modules/petsc.lua", b"root = '/opt/petsc'\n", meta("stack"))
        .expect("put");
    let child_note = child
        .snapshot(&signer("example.org/stack", 8), "example.org/stack")
        .expect("child snapshot");
    let mut parent = Sidecar::open(MemoryStore::new()).expect("parent");
    let included = parent
        .include_checkpoint(
            "stacks/2026",
            &child_note,
            &[signer("example.org/stack", 8).verifier()],
            meta("site"),
        )
        .expect("include");
    println!("included stacks/2026 at {included}");
    let checkpoints = parent.checkpoints("stacks/2026").expect("checkpoints");
    println!(
        "child origin {} size {}",
        checkpoints[0].1.origin, checkpoints[0].1.size
    );
}

sync_dir compares a directory to the tree the log holds. A new file is a put, a changed file is a put, a missing file is a delete, and an unchanged file is left alone. tree_at is the latest put for each path that is still present.

//! Library sidecar: put, snapshot, history, sync a directory, include a child.

use std::collections::BTreeMap;

use ed25519_dalek::SigningKey;
use rfc9162_log::Sidecar;
use rfc9162_tiles::{MemoryStore, Signer};

fn signer(origin: &str, seed: u8) -> Signer {
    Signer::new(origin, SigningKey::from_bytes(&[seed; 32])).expect("signer")
}

fn meta(actor: &str) -> BTreeMap<String, String> {
    let mut meta = BTreeMap::new();
    meta.insert("actor".to_string(), actor.to_string());
    meta
}

fn main() {
    let mut sidecar = Sidecar::open(MemoryStore::new()).expect("sidecar");
    let index = sidecar
        .put("ssh/sshd_config", b"Port 22\n", meta("root"))
        .expect("put");
    println!("put {index}");
    let child_signer = signer("example.org/demo", 7);
    let note = sidecar
        .snapshot(&child_signer, "example.org/demo")
        .expect("snapshot");
    println!("snapshot bytes {}", note.to_text().len());
    let found = sidecar.history("ssh/sshd_config").expect("history");
    let item = &found[0];
    let bytes = sidecar
        .log()
        .index()
        .expect("index")
        .reader()
        .get(item.index)
        .expect("get")
        .expect("present");
    println!("entry {}", String::from_utf8(bytes.clone()).expect("utf-8"));
    println!(
        "history {} inclusion {} run {}",
        item.change.path,
        rfc9162::verify_inclusion(
            &rfc9162::leaf_hash(&bytes),
            usize::try_from(item.index).unwrap(),
            usize::try_from(item.located.entry.tree_size).unwrap(),
            &item.located.entry.path,
            &rfc9162::hex(&sidecar.root()),
        ),
        item.located.run.is_some()
    );
    let blob = sidecar
        .blob(item.change.blob.as_deref().expect("blob"))
        .expect("blob")
        .expect("stored");
    println!("blob {}", String::from_utf8(blob).expect("utf-8"));

    let root = tempfile::tempdir().expect("temp dir");
    let project = root.path().join("project");
    std::fs::create_dir_all(project.join("ssh")).expect("mkdir");
    std::fs::write(project.join("ssh/sshd_config"), b"Port 22\n").expect("write");
    std::fs::write(project.join("motd"), b"hello\n").expect("write");
    let mut synced_log = Sidecar::open(MemoryStore::new()).expect("sidecar");
    let synced = synced_log
        .sync_dir(&project, &[], &meta("root"))
        .expect("sync");
    println!(
        "sync added {} changed {} deleted {} unchanged {}",
        synced.added.len(),
        synced.changed.len(),
        synced.deleted.len(),
        synced.unchanged
    );
    std::fs::write(project.join("ssh/sshd_config"), b"Port 2222\n").expect("rewrite");
    std::fs::remove_file(project.join("motd")).expect("remove");
    let again = synced_log
        .sync_dir(&project, &[], &meta("root"))
        .expect("sync");
    println!(
        "resync added {} changed {} deleted {}",
        again.added.len(),
        again.changed.len(),
        again.deleted.len()
    );
    let tree = synced_log.tree_at(synced_log.size()).expect("tree");
    println!("tree");
    for (path, (index, change)) in &tree {
        println!(
            "  {path} index {index} bytes {}",
            change.size.expect("size")
        );
    }

    let mut child = Sidecar::open(MemoryStore::new()).expect("child");
    child
        .put("modules/petsc.lua", b"root = '/opt/petsc'\n", meta("stack"))
        .expect("put");
    let child_note = child
        .snapshot(&signer("example.org/stack", 8), "example.org/stack")
        .expect("child snapshot");
    let mut parent = Sidecar::open(MemoryStore::new()).expect("parent");
    let included = parent
        .include_checkpoint(
            "stacks/2026",
            &child_note,
            &[signer("example.org/stack", 8).verifier()],
            meta("site"),
        )
        .expect("include");
    println!("included stacks/2026 at {included}");
    let checkpoints = parent.checkpoints("stacks/2026").expect("checkpoints");
    println!(
        "child origin {} size {}",
        checkpoints[0].1.origin, checkpoints[0].1.size
    );
}

include_checkpoint appends a child log’s signed note after checkpoint::verify accepts it. A later checkpoint for that path has to extend the one already logged: a smaller size, or the same size with another root, is refused. checkpoints parses the notes back.

//! Library sidecar: put, snapshot, history, sync a directory, include a child.

use std::collections::BTreeMap;

use ed25519_dalek::SigningKey;
use rfc9162_log::Sidecar;
use rfc9162_tiles::{MemoryStore, Signer};

fn signer(origin: &str, seed: u8) -> Signer {
    Signer::new(origin, SigningKey::from_bytes(&[seed; 32])).expect("signer")
}

fn meta(actor: &str) -> BTreeMap<String, String> {
    let mut meta = BTreeMap::new();
    meta.insert("actor".to_string(), actor.to_string());
    meta
}

fn main() {
    let mut sidecar = Sidecar::open(MemoryStore::new()).expect("sidecar");
    let index = sidecar
        .put("ssh/sshd_config", b"Port 22\n", meta("root"))
        .expect("put");
    println!("put {index}");
    let child_signer = signer("example.org/demo", 7);
    let note = sidecar
        .snapshot(&child_signer, "example.org/demo")
        .expect("snapshot");
    println!("snapshot bytes {}", note.to_text().len());
    let found = sidecar.history("ssh/sshd_config").expect("history");
    let item = &found[0];
    let bytes = sidecar
        .log()
        .index()
        .expect("index")
        .reader()
        .get(item.index)
        .expect("get")
        .expect("present");
    println!("entry {}", String::from_utf8(bytes.clone()).expect("utf-8"));
    println!(
        "history {} inclusion {} run {}",
        item.change.path,
        rfc9162::verify_inclusion(
            &rfc9162::leaf_hash(&bytes),
            usize::try_from(item.index).unwrap(),
            usize::try_from(item.located.entry.tree_size).unwrap(),
            &item.located.entry.path,
            &rfc9162::hex(&sidecar.root()),
        ),
        item.located.run.is_some()
    );
    let blob = sidecar
        .blob(item.change.blob.as_deref().expect("blob"))
        .expect("blob")
        .expect("stored");
    println!("blob {}", String::from_utf8(blob).expect("utf-8"));

    let root = tempfile::tempdir().expect("temp dir");
    let project = root.path().join("project");
    std::fs::create_dir_all(project.join("ssh")).expect("mkdir");
    std::fs::write(project.join("ssh/sshd_config"), b"Port 22\n").expect("write");
    std::fs::write(project.join("motd"), b"hello\n").expect("write");
    let mut synced_log = Sidecar::open(MemoryStore::new()).expect("sidecar");
    let synced = synced_log
        .sync_dir(&project, &[], &meta("root"))
        .expect("sync");
    println!(
        "sync added {} changed {} deleted {} unchanged {}",
        synced.added.len(),
        synced.changed.len(),
        synced.deleted.len(),
        synced.unchanged
    );
    std::fs::write(project.join("ssh/sshd_config"), b"Port 2222\n").expect("rewrite");
    std::fs::remove_file(project.join("motd")).expect("remove");
    let again = synced_log
        .sync_dir(&project, &[], &meta("root"))
        .expect("sync");
    println!(
        "resync added {} changed {} deleted {}",
        again.added.len(),
        again.changed.len(),
        again.deleted.len()
    );
    let tree = synced_log.tree_at(synced_log.size()).expect("tree");
    println!("tree");
    for (path, (index, change)) in &tree {
        println!(
            "  {path} index {index} bytes {}",
            change.size.expect("size")
        );
    }

    let mut child = Sidecar::open(MemoryStore::new()).expect("child");
    child
        .put("modules/petsc.lua", b"root = '/opt/petsc'\n", meta("stack"))
        .expect("put");
    let child_note = child
        .snapshot(&signer("example.org/stack", 8), "example.org/stack")
        .expect("child snapshot");
    let mut parent = Sidecar::open(MemoryStore::new()).expect("parent");
    let included = parent
        .include_checkpoint(
            "stacks/2026",
            &child_note,
            &[signer("example.org/stack", 8).verifier()],
            meta("site"),
        )
        .expect("include");
    println!("included stacks/2026 at {included}");
    let checkpoints = parent.checkpoints("stacks/2026").expect("checkpoints");
    println!(
        "child origin {} size {}",
        checkpoints[0].1.origin, checkpoints[0].1.size
    );
}
put 0
snapshot bytes 179
entry {"sidecar":1,"op":"put","path":"ssh/sshd_config","blob":"e807422bad84e7926a98fb262153b3d9c84244080e69b0e5d20446c183ffafc4","size":8,"meta":{"actor":"root"}}
history ssh/sshd_config inclusion true run true
blob Port 22

sync added 2 changed 0 deleted 0 unchanged 0
resync added 0 changed 1 deleted 1
tree
  ssh/sshd_config index 2 bytes 10
included stacks/2026 at 0
child origin example.org/stack size 2

The first sync adds two files. The resync changes ssh/sshd_config (10 bytes, Port 2222\n) and deletes motd. The child checkpoint has size 2: the put, then the manifest snapshot sealed. The parent logs it at index 0 under stacks/2026.

The sidecar from a shell

rfc9162-sidecar keeps the pack at DIR/.sidecar/pack. Install it with cargo install or run it from a checkout:

cargo run -q -p rfc9162-log --bin rfc9162-sidecar -- init --dir demo

The transcript below is that binary, with a PKCS#8 Ed25519 key whose seed is the byte 7 repeated. The commands run in an empty directory that contains demo.pem and, later, child.pem. File contents are demo/ssh/sshd_config = Port 22\n, then Port 2222\n; demo/motd = hello\n, deleted on the second sync; demo/issue = new\n; child/modules/petsc.lua = root = '/opt/petsc'\n.

$ rfc9162-sidecar init --dir demo
sidecar at demo/.sidecar size 0

$ rfc9162-sidecar sync --dir demo --meta actor=root
added motd
added ssh/sshd_config
synced: 2 added, 0 changed, 0 deleted, 0 unchanged; entries 0..2; size 2

$ rfc9162-sidecar snapshot --dir demo --key demo.pem --origin example.org/demo
snapshot example.org/demo size 3 root 4bc522505fc7ad765e208f1d45c73fb8ddfc3a9225ab3d0e1e5e8e78b8986a92

$ rfc9162-sidecar sync --dir demo --meta actor=root
added issue
changed ssh/sshd_config
deleted motd
synced: 1 added, 1 changed, 1 deleted, 0 unchanged; entries 3..6; size 6

$ rfc9162-sidecar history --dir demo ssh/sshd_config
1	put	ssh/sshd_config	e807422bad84e7926a98fb262153b3d9c84244080e69b0e5d20446c183ffafc4	8
4	put	ssh/sshd_config	bdc45620d3cbb299dd7f748340f858226752470f6ce378000a4f5485bf35070e	10

$ rfc9162-sidecar tree --dir demo
issue	3	7aa7a5359173d05b63cfd682e3c38487f3cb4f7f1d60659fe59fab1505977d4c	4
ssh/sshd_config	4	bdc45620d3cbb299dd7f748340f858226752470f6ce378000a4f5485bf35070e	10

$ rfc9162-sidecar diff --dir demo 0 6
0	put	motd	5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03	6
1	put	ssh/sshd_config	e807422bad84e7926a98fb262153b3d9c84244080e69b0e5d20446c183ffafc4	8
3	put	issue	7aa7a5359173d05b63cfd682e3c38487f3cb4f7f1d60659fe59fab1505977d4c	4
4	put	ssh/sshd_config	bdc45620d3cbb299dd7f748340f858226752470f6ce378000a4f5485bf35070e	10
5	delete	motd

$ rfc9162-sidecar blob --dir demo bdc45620d3cbb299dd7f748340f858226752470f6ce378000a4f5485bf35070e
Port 2222

$ rfc9162-sidecar init --dir child
sidecar at child/.sidecar size 0

$ rfc9162-sidecar sync --dir child --meta actor=stack
added modules/petsc.lua
synced: 1 added, 0 changed, 0 deleted, 0 unchanged; entries 0..1; size 1

$ rfc9162-sidecar snapshot --dir child --key child.pem --origin example.org/stack
snapshot example.org/stack size 2 root 1fd49b5570bfe39c2697d7473081906f6999aae451743492d132c83705f340e3

$ cargo run -q -p rfc9162-docs-examples --bin show_checkpoint -- child/.sidecar/pack > child-note.txt
$ rfc9162-sidecar init --dir site
sidecar at site/.sidecar size 0

$ rfc9162-sidecar include --dir site stacks/2026 --note child-note.txt --trusted example.org/stack+77af48a1+AepKbGPinFIKvvVQexMuxfmVR3auvr57kkIe6mkURtIs --meta actor=site
included stacks/2026 at 0

init creates the pack and prints size 0. The first sync appends motd and ssh/sshd_config at indexes 0 and 1. snapshot seals a run, which appends a manifest, so the signed size is 3. The root is 4bc522505fc7ad765e208f1d45c73fb8ddfc3a9225ab3d0e1e5e8e78b8986a92.

The second sync appends the new and changed paths and the deletion at indexes 3, 4 and 5. Index 2 is the manifest from the snapshot, and diff skips manifest entries. history prints both puts of ssh/sshd_config. tree prints the live paths. blob writes the bytes of the latest put, Port 2222\n.

include reads the child’s signed note. show_checkpoint in docs/examples prints the checkpoint record from a pack; a caller that already has the note file skips that step. The trusted line is the child’s verifier. The command logs the note at index 0 under stacks/2026.

The command reference is the sidecar page.