Go to file
Levi Neuwirth cd40e2c892 Initialize an absent root through the production entry point
Scope 6.4 deliverable 1, startup state 1. StoreEngine::open now creates a
root rather than refusing to, and every harness that measured a store
production did not build is re-pointed at it. States 3 and 4 still refuse.

The four startup states are distinguished by one read-only classification
that creates nothing. A FORMAT entry means state 2 on the strength of the
name alone, because a FORMAT that does not decode is still FORMAT and
treating an unreadable marker as "no marker, therefore empty" would
authorize building a fresh tree over a populated root. A root holding only
LOCK is empty, since lock_root creates that file as a side effect of asking
whether the root is busy. The signer check moved above every root access,
because once an absent root is initialized rather than refused, a signerless
configuration would otherwise create a tree, write FORMAT and fsync the
parent before failing -- a configuration error must not leave a root behind.

Three defects found in review, all fixed here rather than deferred.

A deeply absent path was created with create_dir_all and only its immediate
parent fenced, so open could report success over ancestors a power loss
could take. Ancestors are now created one at a time -- so what this call
created is exactly what it fences, and a racing creator surfaces as
AlreadyExists rather than being absorbed -- and fenced deepest-first, since
a directory entry lives in the parent that names it and the reverse order
can leave a fenced parent naming an unfenced child.

An interrupted initialization was unrecoverable: any tree residue classified
the root as non-empty-without-FORMAT and it was refused forever, with a
message about legacy layouts that had nothing to do with what happened. A
root being built now carries an INITIALIZING marker, installed by rename as
the first durable act and removed as the last, so a durable partial tree
always has a durable marker beside it and every crash window is resumable.
Recognizing it requires both a byte-exact marker this crate alone writes and
every entry in the root drawn from a closed set of names this store
invented, so a foreign layout cannot be mistaken for abandoned
initialization and overwritten -- the direction that matters, since refusing
a resumable root costs an operator time and overwriting a real one costs
their data. Sibling staging with atomic installation was the alternative and
is structurally blocked: LOCK lives inside the root, so the root must exist
before any mutation can be serialized, and renaming a tree onto a directory
containing LOCK fails ENOTEMPTY.

The classifier then ignored INITIALIZING.tmp by name regardless of type or
contents, and initialization opened that name with create plus truncate. An
operator's file there was destroyed silently, and a symlink there truncated
a file outside the root to 27 bytes and then removed the link -- destroying
data the store never owned and erasing the evidence, while open returned Ok
and reported a working store. The justification for ignoring the name was
that only this path could have written it, which is circular: that is the
claim the classifier runs in order to establish. Every entry is now judged
by lstat type before anything opens it, the temporary marker is validated as
an exact regular marker or refused, and installation is create-new rather
than create-truncate. Contract review 2026-07-28-D records the two no-follow
open primitives this added to the frozen sys.rs, and the four further
symlink hazards in segment.rs that are recorded rather than fixed -- the
first of which lets two processes believe they hold one root lock.

A fifo at that name made the pre-fix open block forever: one mkfifo in a
configured root was an unbounded startup hang, not only a data hazard.

The engine and the drive seam are now asserted to recover one crash image
identically, closing a gap that was true by construction and untested.

Charter item 8 applied to the harness: the in-crate test helper no longer
calls segment::initialize_root, so every writer test builds its root through
open; the ROOT_SEEDED_BY_NON_PRODUCTION_PATH disclosure is retired; and the
fixture's root_seeded_by becomes a stable token matched by exact equality,
with the history moved to an adjacent reason field -- a substring match
passes on a value that has drifted to mean something else.

The d0 contract test asserting open returns NotImplemented for any valid
configuration is obsoleted by this deliverable and replaced with the
stronger property: a signerless configuration is refused and leaves no root
behind. It moves off a fixed /tmp path, which under the old check ordering
would have created a real store root on every gate run on every machine.

scripts/check-phase1.sh GATE_EXIT=0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 17:26:05 -04:00
.github/workflows integrate CI and docs 2026-05-01 11:29:18 -04:00
bench Freeze Wave A: Phase 1 storage spine 2026-07-26 19:47:03 -04:00
crates Initialize an absent root through the production entry point 2026-07-29 17:26:05 -04:00
deploy levcs 0.1.0 - initial core 2026-05-01 11:14:36 -04:00
doc Initialize an absent root through the production entry point 2026-07-29 17:26:05 -04:00
scripts Freeze Wave A: Phase 1 storage spine 2026-07-26 19:47: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.