Skip to content

heyoub/batpak

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

595 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

crates.io docs.rs CI license

batpak

The Free Battery Factory makes batteries for software boundaries.

A battery does not own the machine. It powers one boundary.

batpak is the core battery: an embedded, sync-first append-only journal with typed payloads, Blake3 hash-chained ancestry, verifiable receipts, deterministic replay, and derived projections. The family around it wires that journal into larger hosts — syncbat for runtime dispatch, netbat for NETBAT/1 network terminals, hbat as the reference host, and @batpak/sdk for TypeScript clients on the other side of the wire. Circuits connect batteries without one owning another's state.

Use it when you need a tamper-evident, replayable record of what happened: agent action audit trails, local-first app logs, compliance evidence, event-sourced application state.

batpak is not a database server, queue, ORM, workflow engine, async runtime, network framework, or agent framework. Callers own process model, disk placement, runtime integration, network boundaries, and application authority.

What Ships

Battery / surface Crate / package Role
Core journal batpak Append-only store, HLC frontier, receipts, replay, projections
Runtime dispatch syncbat Operation descriptors, handler registration, runtime receipts
Network terminal netbat NETBAT/1 frames, bounded request/response
Reference host hbat Live operation handling (manifest-owned)
TypeScript clients @batpak/sdk Wire client, canonical codec, generated types

See BATTERIES.md for the full battery map and bpk-ts/README.md for npm install and the ten-op NETBAT profile.

Two Doors

Door A — Rust embedded. Add the core crate and open a Store on a directory you own:

cargo add batpak

Door B — Networked host. Install the TypeScript SDK and prove the live host loop against hbat:

npm install @batpak/sdk
just host-dev

just host-dev exports the manifest, builds the workspace, boots hbat on an ephemeral store, and runs the heartbeat-spike through commit, query, and get. The ten reference NETBAT terminals — bank.commit, event.query, event.get, receipt.verify, event.walk, and the four evidence.* ops — are documented in TERMINALS.md.

First Shape

use batpak::prelude::*;

// One struct binds a Rust type to its event kind at compile time.
#[derive(serde::Serialize, serde::Deserialize, EventPayload)]
#[batpak(category = 0xF, type_id = 1)]
struct PlayerMoved {
    x: i32,
    y: i32,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let dir = tempfile::tempdir()?;

    // Open the Store: the battery. It owns this directory and nothing else.
    let store = Store::open(StoreConfig::new(dir.path()))?;

    // A Coordinate names where events belong: an entity within a scope.
    let coord = Coordinate::new("player:alice", "room:dungeon")?;

    // Append a cell of source truth. The receipt is verifiable evidence
    // of exactly what was accepted.
    let receipt = store.append_typed(&coord, &PlayerMoved { x: 10, y: 20 })?;

    // Read it back. Accepted events are immutable.
    let fetched = store.get(receipt.event_id)?;
    println!(
        "stored {} at sequence {} in scope {}",
        fetched.event.header.event_id,
        receipt.sequence,
        fetched.coordinate.scope()
    );

    store.close()?;
    Ok(())
}

Run it for real: cargo run --example quickstart under bpk-lib/crates/core.

The full beginner path is eight jobs — open, append, page commit order with query_entries_after, point-read with get, walk hash-chain ancestry with walk_ancestors, verify receipts, derive projections, close. See bpk-lib/crates/core/examples/eight_jobs.rs for the contract example and the cookbook for task-shaped recipes. The cookbook is the task-shaped field guide: each recipe maps intent to API to proof surface, and its index lives at cookbook/README.md.

Scale And Composition

A journal is one Store on one data_dir with one exclusive writer. That scope is the local truth boundary — truth is bounded to that journal, not denied to distributed systems.

Scale out with multiple journals and explicit circuits: netbat routes, cross-store observations, and host wiring documented in CIRCUITS.md and INTEGRATION.md. There is no single global_sequence across separate store roots, and no in-core Raft over one mutable directory.

HLC (hybrid logical clock) is the per-journal frontier inside one writer: accepted → written → durable → visible → applied watermarks. wait_for_durable, batch gates, and projection progress use those watermarks. HLC coordinates durability and visibility inside a journal; it is not cross-machine consensus.

Why Not SQLite With An Events Table?

SQLite gives you durable rows. batpak gives you durable rows plus proof:

  • Every event is hash-bound to its per-entity ancestor with Blake3, so tampering and reordering are detectable, not silent.
  • Every accepted write returns a receipt you can verify later — against the committed store, and against an Ed25519 signature when keys are configured.
  • Projections are derived views rebuilt from the log by construction, so read models can never silently drift from source truth.
  • Canonical bytes are stable across languages: the TypeScript codec is byte-for-byte parity-tested against the Rust encoder in CI.

When batpak is the wrong tool:

Need Reach for
Ad-hoc SQL over relational data SQLite or Postgres
Many writers on one mutable directory with leader election A database server or etcd — batpak is one writer per data_dir
Maximum write throughput over verifiable history batpak serializes appends through a single writer on purpose
Automatic Raft replication inside the core crate Compose multiple journals and explicit host circuits instead

Can You Trust A 0.x Store?

Judge the evidence, not the version number:

  • Roughly one line of tests per line of source, including crash-recovery and cold-start suites.
  • Deterministic concurrency proofs with loom, not just stress tests.
  • Property-based tests over hash-chain integrity and canonical encoding.
  • Chaos testing with fault injection, including disk-fault integration.
  • Mutation testing on critical seams, so the tests are themselves tested.
  • 73 named invariants traced to 124 concrete artifacts, enforced by an integrity gate that fails CI on orphaned or stale claims — see INVARIANTS.md and CONFORMANCE.md.

All of it runs from one command surface: just verify.

The Mental Model

batpak ships with an opinionated mental model. You can use the API without ever adopting it — the code above is the whole beginner story. But composition gets much easier once you think in it, because every boundary question ("who owns this state? where may it cross?") already has a name.

The Rosetta table — factory words on the left, the precise engineering surface on the right:

Factory word Rust surface Plain engineering meaning
Battery Store An embedded append log that owns one directory, one boundary.
Journal one data_dir One append-only store root; one exclusive writer.
Cell Event An immutable typed record; source truth.
Coordinate Names where an event belongs: an entity within a scope.
Terminal named API entry points The only places where state or evidence crosses the boundary.
Receipt Receipt Verifiable evidence of what was accepted, denied, or replayed.
Discharge Replay Rebuild state from the cells.
Gauge Projection A derived view: disposable, rebuildable, never source truth.
Frontier HLC watermarks Per-journal accepted → durable → visible → applied progress inside one writer.
Gate Gate Caller-defined policy evaluated before commit.
Circuit host wiring Connects terminals across batteries without hiding ownership.
Capability Explicit authority to perform an operation.

Factory words explain the shape. Engineering names stay precise in the API, and factory language never renames a Rust contract unless the type model earns that name. Deeper factory identity lives in FACTORY.md.

Reading Paths

Pick the door that matches your intent — the docs are one model, but nobody is required to read all of them:

Machine law lives in bpk-lib/traceability/ and bpk-lib/tools/integrity/. These root docs describe the current system; they are not a decision archive.

Command Authority

Use the root justfile.

just list
just inspect
just verify
just host-dev
just perf-gates
just loom
just seal
just ship dry

just is the command counter. xtask is the factory machinery. ast-grep inspects structural doctrine. Tests inspect behavior. Receipts preserve evidence. perf-gates and loom are manual release-confidence proofs, not part of just verify.

Raw cargo, npm, and pnpm are implementation details unless routed through an explicit escape hatch:

just cargo -- <args>
just pnpm -- <args>
just npm -- <args>

License

Licensed under either of:

  • Apache License, Version 2.0
  • MIT license

at your option.

About

Sync-first event sourcing for Rust: append-only segments, causal metadata, policy gates, and typed projections — no async runtime. It's free batteries bro, take 'em!

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors