Go to file
Levi Neuwirth 0d2fd6d986 Keep a surviving journal on the identity the manifest implies
The sealing fallback was reaching a journal that seals nothing. A recovery
keeping an empty tail gave it the fallback's generation, but no TailRange
recorded the move — nothing was sealed — so the next open derived the identity
the manifest still implied while the session in between had appended frames
under the moved one and sealed a run over them. The guard searched published
runs for the derived generation, found none, and let the store open with the
run unresolvable. Review confirmed it: empty tail plus an orphan at 1, reopen
taking 2, frames and a run at 2, an orphan at 2, then an open at 3 with
`object_source(0, 2) == None`.

`RecoveryGenerations` carries a third number. `tail` is what a surviving
journal keeps and is always the tail's own identity: segment-name occupancy is
a fact about `segments/`, and a journal that seals nothing does not go there.
`logical` remains the sealing identity and still falls back under the guard.

That restores the invariant the split rests on — the active tail's identity is
always derivable from the manifest — so the guard and the frames are always
naming the same generation. A tail may keep a generation an orphan occupies;
the collision is only real when frames are sealed under that name, and it is
refused then, with the run in hand.

The regression follows the reported sequence and reads the run's generation out
of the run rather than assuming it, since which identity the frames ended up
with is the thing under test. Mutation-checked in both places it can fail: the
run drifts to generation 2, and with the mid-assertion relaxed the open
succeeds with None pinned there, reproducing the report exactly. The shared
helper now reports the run's own generation, so all three refusal paths fail
legibly rather than against a hard-coded number.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKzM69CHmBuDcA3qN1jFdh
2026-07-30 17:12:16 -04:00
.github/workflows integrate CI and docs 2026-05-01 11:29:18 -04:00
bench Condition benchmark claims on the run that produced them 2026-07-29 17:38:03 -04:00
crates Keep a surviving journal on the identity the manifest implies 2026-07-30 17:12:16 -04:00
deploy levcs 0.1.0 - initial core 2026-05-01 11:14:36 -04:00
doc Keep a surviving journal on the identity the manifest implies 2026-07-30 17:12:16 -04:00
scripts Condition benchmark claims on the run that produced them 2026-07-29 17:38:03 -04:00
.gitignore levcs 0.1.0 - initial core 2026-05-01 11:14:36 -04:00
Cargo.lock Implement D0-B storage publication interfaces 2026-07-27 22:31:32 -04:00
Cargo.toml Implement D0-B storage publication interfaces 2026-07-27 22:31:32 -04:00
LICENSE integrate CI and docs 2026-05-01 11:29:18 -04:00
README.md integrate CI and docs 2026-05-01 11:29:18 -04:00

README.md

LeVCS

A distributed version control system with first-class federation, signed authority chains, and a cascading merge engine. Content-addressed by BLAKE3, signed with Ed25519, and built to fix what git can't.

Status: v0.1.0 — protocol substrate complete, workflow surface deferred. The object model, federation API, merge cascade, and instance server all work end-to-end. There is no PR review surface, issue tracker, or web UI yet — those are the next layer up. See doc/technical-report.md for a full framing of where this project is and why.


What's different from git

  • Identity is in the protocol. A repo's membership is a versioned, signed authority object with explicit roles (Reader/Contributor/Maintainer/Owner). Force-push enforcement and push authorization are protocol-level, not server policy.
  • Federation is first-class. Every repo has a global repo_id (BLAKE3 of its genesis authority); instances mirror each other in three storage modes (full / release-only / metadata-only).
  • Merge is a cascade, not a line-level diff. Per-file dispatch to a handler ranked by aggressiveness: textual fallback, format-aware (JSON / YAML / TOML / XML / Markdown / prose), tree-sitter for source code (Rust, Python, JS/TS, Go, C/C++, Java, Ruby, Bash), and wasm-sandboxed plugins for the long tail.
  • BLAKE3, not SHA-1. Tree-hashed, ~5 GiB/s on a laptop, 32-byte IDs everywhere.
  • Releases are signed objects, not mutable name pointers.

For a deeper comparison and context, see the technical report.


Building

LeVCS is a Rust workspace. You'll need a recent stable toolchain (workspace MSRV is 1.75) and a C compiler for the tree-sitter grammars.

cargo build --release

Two binaries land in target/release/:

  • levcs — the user-facing CLI.
  • levcs-instance — the federation HTTP server.

Install them somewhere on PATH:

sudo install -m 0755 \
    target/release/levcs target/release/levcs-instance \
    /usr/local/bin/

Quick start (single user, local only)

# Generate an identity key (stored in $XDG_CONFIG_HOME/levcs/keys.toml).
levcs key generate --label me

# Create a repository wherever you have files to track.
mkdir /tmp/demo && cd /tmp/demo
echo "hello" > a.txt

levcs init --key me
levcs track --all
levcs commit -m "first commit"
levcs log

That's a fully working LeVCS repo. Branch and merge:

levcs branch feature/x
echo "more" >> a.txt
levcs commit -m "wip"
levcs branch main
levcs merge feature/x

If a merge produces conflicts, drop into the resolution TUI:

levcs merge --resolve

Cut a release:

levcs release v0.1.0 --notes "first release"

Hosting an instance

To dogfood the federation surface, run levcs-instance on a VPS behind nginx or Caddy. The full walkthrough is in deploy/README.md: build, systemd unit, reverse proxy templates, firewall, and the laptop-side bootstrap.

The compressed version:

sudo cp deploy/levcs-instance.service /etc/systemd/system/
sudo cp deploy/instance.toml.example /etc/levcs/instance.toml
sudo $EDITOR /etc/levcs/instance.toml
sudo systemctl enable --now levcs-instance
# ... then drop deploy/Caddyfile.example into /etc/caddy/Caddyfile

From your laptop, point the local repo at the instance and push:

levcs instance --set https://levcs.example.com/levcs/v1
levcs push refs/branches/main

The first push to a fresh instance auto-inits the repo with your genesis authority. Subsequent pushes are role-checked against the authority chain.


Repository layout

crates/
  levcs-core/        Object model (Blob/Tree/Commit/Release/Authority),
                     hash, store, refs, repository abstractions.
  levcs-identity/    Authority objects, Ed25519 keys, signing/verify.
  levcs-merge/       Cascade engine, format and tree-sitter handlers,
                     plugin runtime, merge records.
  levcs-protocol/    Pack codec, wire types, request signing, P2P.
  levcs-client/      Thin HTTP client over the federation API.
  levcs-instance/    Axum HTTP server (the federation peer).
  levcs-cli/         The `levcs` user-facing CLI.
  levcs-tui/         Conflict-resolution terminal UI.

deploy/              Production deployment artifacts (systemd, Caddy, nginx).
scripts/             Reproducible benchmark and ops scripts.
doc/                 Technical report and architecture docs.
.github/workflows/   CI configuration.

Testing

cargo test --workspace

Runs the full suite — unit tests, integration tests, federation end-to-end tests including the three-instance "dogfood" scenario, the merge conformance corpus, and property-based fuzz tests. ~194 tests at v0.1.0; full run is well under a minute on a modern laptop.

Useful subsets:

# A single crate's tests
cargo test -p levcs-merge

# A specific integration test
cargo test -p levcs-instance --test dogfood

# Property tests only
cargo test -p levcs-merge --test proptest_textual

Benchmarks

Microbenchmarks live in each crate's benches/ directory and use criterion. A reproducible runner with metadata capture and optional flamegraph generation is at scripts/bench.sh:

scripts/bench.sh --quick               # smoke test (~ a minute total)
scripts/bench.sh                       # full criterion run (~ a few minutes)
scripts/bench.sh --flamegraph          # generate per-bench SVG flamegraphs
scripts/bench.sh --bench pack_codec    # one bench only

Output goes to bench-results/<host>-<UTC-timestamp>/ with a parsed summary.txt, criterion's HTML reports, and a metadata.txt capturing rustc version, kernel, CPU, and git rev for run-to-run comparison.

Headline numbers on a Ryzen 7 laptop:

  • Pack decode at 10 × 1 MiB entries: ~2.3 ms (4.3 GiB/s).
  • Blob serialize + BLAKE3 at 1 MiB: ~190 µs (5.1 GiB/s).
  • Textual three-way merge of a 100 KiB document: ~4.6 ms.

Pack encoding is the throughput floor at ~380 MiB/s — bottlenecked by zstd level 3 on incompressible data.


Documentation

  • doc/technical-report.md — Distribution document. What LeVCS is, how to use it, and how it differs from / improves upon git. Targets technical evaluators and the workflow-spec reader.
  • deploy/README.md — Comprehensive VPS deployment walkthrough.
  • spec/ — The protocol specification and trust-root revision. Currently kept private; ask the maintainer for a copy.

Contributing

This is a young project. The most useful contributions right now are:

  1. Trying it. Run levcs init on a real project, push to a local instance, and report friction.
  2. Workflow design. The next major piece of work is the workflow spec — PR/review surface, issues, CI conventions. Discussion welcome.
  3. Plugin handlers. The wasm plugin protocol exists; concrete handlers (e.g. protobuf, SQL migrations) are needed to validate it.
  4. Tightening CI. The fmt and clippy GitHub jobs are informational; flipping them to gating would close a small but real quality gap.

Please open an issue or reach out before starting non-trivial work so we can coordinate.


License

Released under the Apache License 2.0 — see LICENSE for the full text.


Citation

If LeVCS supports academic work, please cite the v0.1.0 release. A formal citation entry will land with the workflow spec; in the meantime a repository-URL reference is fine.