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

cadvm

Introduction

cadvm (CAD Version Manager) is a local-first version control system for CAD filesSTEP/STP (B-Rep) and STL/OBJ (triangle mesh). It brings Git-like workflows — snapshots, branches, diff, checkout, revert — to CAD data, and goes further with a geometric diff and a 3D viewer that show what actually changed in the geometry, not just in the bytes.

cadvm init
cadvm snapshot -m "Cube with a Ø5 hole"
# …edit the part…
cadvm snapshot -m "Ø5 → Ø8"
cadvm geom-diff HEAD~1 HEAD     # volumes added / removed / common
cadvm view HEAD~1 HEAD          # interactive 3D diff in your browser
cadvm ui                        # full-screen terminal dashboard

Why cadvm?

CAD files are awful to version with Git: a tiny geometric edit re-exports the whole STEP file with renumbered entities and a new timestamp, so a textual diff is pure noise. cadvm instead:

  • stores versions deduplicated (content-addressed, gzip-compressed chunks);
  • summarizes each file with light metadata (STEP schema/entities, or mesh triangle/vertex counts);
  • computes a real geometric diff (added / removed / common) — via Open CASCADE for STEP/STP, and a pure-Rust triangle diff for STL/OBJ;
  • renders that diff in a self-contained 3D HTML viewer (no server, no cloud).

🧊 See it live: the Live demo embeds a real diff you can rotate and zoom right in your browser.

Architecture

cadvm is built in three layers:

LayerWhatTech
VCS corecommits, manifests, refs, branches, status, diff, checkout, storage; STL/OBJ mesh diff100% Rust
Geometry helperSTEP/STP added/removed/common volumes + face diff, tessellationC++ / Open CASCADE (subprocess)
Viewerinteractive 3D diffself-contained WebGL HTML

The Rust core never links Open CASCADE directly — it calls the cadvm-geom helper as a subprocess, keeping the heavy CAD dependency isolated. Open CASCADE is needed only for the STEP/STP geometric diff; the VCS, the TUI and the STL/OBJ mesh diff all work without it.

Crates

  • cadvm-store — content-addressed storage (BLAKE3, blobs, chunks).
  • cadvm-core — repository logic.
  • cadvm-cli — the cadvm binary (CLI + TUI).
  • cpp/cadvm-geom — the C++/OCCT geometry helper.

The API reference (rustdoc) documents these crates.

Continue with Installation.

cadvm for AI agents

If your stack generates or edits CAD with AI (text-to-CAD, parametric copilots, design agents), cadvm is the version / diff / verification layer underneath it. The AI produces geometry; cadvm pins every iteration, tells your agent what actually changed, and lets it accept or roll back — automatically.

A human reviews a diff by eye. An agent needs structured data — that is what cadvm geom-diff --json provides.

See it run: the Example: AI agent loop page replays this whole loop (accept a good edit, catch & revert a regression) with its real output — no LLM, no Open CASCADE.

The agent loop

cadvm init                       # once, in the working directory

# …the AI writes/edits a CAD file (part.step, part.stl, …)…
cadvm snapshot -m "iteration 7"  # pin this AI iteration

# Machine-readable geometric diff vs the previous iteration:
cadvm geom-diff HEAD~1 HEAD --json
{
  "rev_a": "9c1f…", "rev_b": "a3b2…",
  "files": [{
    "path": "part.step",
    "kind": "brep",
    "diff": {
      "status": "ok",
      "added":   { "volume": 173.79, "faces": 252 },
      "removed": { "volume": 109.91, "faces": 179 },
      "common":  { "volume": 6266.98, "faces": 157 },
      "faces_topo": { "common": 6, "added": 3, "removed": 1 }
    }
  }]
}

Your agent parses that and decides — or it lets cadvm judge directly with verify, which asserts expectations and returns pass/fail (exit code 0/1):

# "the edit should add material and remove almost none"
cadvm verify HEAD~1 HEAD --expect 'added_volume>50' --expect 'removed_volume<1' --json
# → {"report":{"pass":true,"checks":[...]}}   exit 0 = pass, 1 = fail

So the agent can:

  • verify / gate — accept an iteration only if cadvm verify passes;
  • revertcadvm revert HEAD to undo a bad generation, then retry.

Available metrics: added_volume, removed_volume, common_volume, volume_delta, faces_added/removed/common (STEP); added_tris, removed_tris, unchanged_tris, bbox_dx/dy/dz (STL/OBJ).

Mesh files (STL/OBJ) emit the same shape with unchanged / added / removed triangle layers — and need no Open CASCADE (pure-Rust diff).

Evals & CI gates (no repository)

For evals and CI you usually have two files — the model’s output and a reference — and just want a geometric pass/fail. --files compares them directly, no repo, no snapshots:

# Did the candidate match the reference closely enough?
cadvm verify --files candidate.stl reference.stl \
  --expect 'added_tris<5' --expect 'removed_tris<5'
echo $?    # 0 = pass (gate the model output), 1 = fail

cadvm geom-diff --files candidate.step reference.step --json   # raw signal
cadvm view     --files candidate.stl  reference.stl            # 3D diff for a human

This makes cadvm verify a drop-in geometric assertion for any eval harness, RL reward, or CI job — exit code in, JSON out.

As a GitHub Action

A ready-made action gates generated CAD in a pull request — it downloads cadvm and runs verify --files, failing the job if the geometry drifts:

- uses: AdeMBCH/cadvm/.github/actions/cadvm-verify@main
  with:
    file-a: reference.stl
    file-b: candidate.stl
    expect: |
      added_tris<10
      removed_tris<10

Full sample workflow: examples/ci-gate.yml. (STL/OBJ work out of the box; STEP/STP need Open CASCADE on the runner.)

Why it fits AI workflows

  • Structured feedback--json is a reward/verification signal for agents, eval harnesses and RL loops, not just a human-readable report.
  • Local-first & offline — no cloud, no account; runs in CI or inside a sandbox next to the model.
  • Deterministic & cheap — content-addressed storage dedupes the many near-identical iterations an agent produces.
  • Agent-friendly surface — a plain CLI with JSON output, easy to wrap as a tool (e.g. an MCP server) the model calls.
  • Visual check for humanscadvm view renders the same diff in 3D when a person needs to look.

Also useful for

  • Evals / benchmarks for CAD-generating models — score “did the model produce the intended geometric change?”.
  • Regression gates in CI for generated or parametric CAD.

Use it via MCP (no glue code)

cadvm ships an MCP server so an agent calls it as native tools — no subprocess wiring or output parsing. Register it with your MCP client:

{
  "mcpServers": {
    "cadvm": { "command": "cadvm", "args": ["mcp"] }
  }
}

(With Claude Code: claude mcp add cadvm -- cadvm mcp.) It speaks JSON-RPC 2.0 over stdio — local, offline, no server to host.

The model then sees these tools:

ToolDoes
cadvm_statusnew / modified / deleted vs HEAD
cadvm_snapshotpin an iteration (commit)
cadvm_loghistory
cadvm_diffmetadata diff
cadvm_geom_diffgeometric diff (added/removed/common)
cadvm_verifyassert expectations → pass/fail
cadvm_revertundo the last iteration
cadvm_compare_filesgeometric diff of two files (no repo — for evals)
cadvm_verify_filesassert expectations on two files (no repo) → pass/fail

Each tool takes an optional repo argument (the working directory); otherwise it uses the server’s current directory. So the loop above becomes a sequence of tool calls the model makes on its own.

Example: AI agent loop

A runnable, self-contained demo of cadvm as the version / diff / verify layer under an AI-CAD agent — accept a good edit, catch and revert a regression. No LLM and no Open CASCADE: the part is an STL, so the geometric diff is pure Rust.

Script: examples/agent-loop.sh.

examples/agent-loop.sh    # after building or installing cadvm

What it runs

cadvm init
cadvm snapshot -m "baseline bracket"

# Agent adds a mounting boss → GATE: it must add material
cp block_v2.stl bracket.stl
cadvm snapshot -m "agent: add mounting boss"
cadvm verify HEAD~1 HEAD --expect 'added_tris>0'   # exit 0 = accept

# Agent regresses and drops the boss → GATE: nothing significant removed
cp block_v1.stl bracket.stl
cadvm snapshot -m "agent: regenerate (drops the boss)"
cadvm verify HEAD~1 HEAD --expect 'removed_tris<20' \
  || cadvm revert HEAD                              # exit 1 = revert

The accept/revert decision is driven by cadvm verify’s exit code — exactly the hook an agent or a CI gate uses.

What it prints

● baseline bracket committed

▶ Agent iteration: add a mounting boss
  verifying  added_tris > 0 …
  ✓ added_tris > 0   (actual 158)
  PASS (1 checks)
  ✓ accepted

▶ Agent iteration: (buggy) regenerates and loses the boss
  verifying  removed_tris < 20 …
  ✗ removed_tris < 20   (actual 158)
  FAIL (1/1 checks failed)
  ✗ regression caught — reverting to the good version

▶ The JSON an agent parses (verify --json):
{
  "file": "bracket.stl",
  "report": {
    "pass": true,
    "metrics": { "added_tris": 158, "removed_tris": 64, "unchanged_tris": 114,
                 "bbox_dx": 40, "bbox_dy": 30, "bbox_dz": 28 },
    "checks": [ { "metric": "added_tris", "op": "Gt", "expected": 0,
                  "actual": 158, "pass": true } ]
  }
}

▶ History (the bad iteration was reverted, the boss survives):
commit …  Revert "agent: regenerate (drops the boss)"
commit …  agent: regenerate (drops the boss)
commit …  agent: add mounting boss
commit …  baseline bracket

In an agent (via MCP)

The same loop runs as native tool calls — cadvm_snapshot, cadvm_verify, cadvm_revert — when cadvm is wired in as an MCP server.

Live demo

Below is a real cadvm view output. Between the two versions of this block a hole was moved from the left to the right, and a boss was added on top. It is rendered live in your browser; nothing is installed or sent anywhere, the whole 3D scene is embedded in the page.

The unchanged body is drawn translucent grey so you can see the changes — including the ones inside the part:

  • green — added: the new hole (visible through the body) and the boss on top;
  • red — removed: the old hole on the left;
  • grey — unchanged faces (the block body keeps the same surfaces).

Drag to rotate, scroll to zoom, and toggle any layer in the panel.

↗ Open the demo full-screen

This page is exactly what cadvm view HEAD~1 HEAD produces — a single, self-contained HTML file. See 3D viewer for how to generate one for your own parts, and Geometric diff for the numbers behind it.

Installation

cadvm installs in two layers. The first is all most users need.

  1. the cadvm binary (version control + TUI) — pure Rust, works everywhere;
  2. the cadvm-geom helper (geometric diff + viewer) — requires Open CASCADE, a prerequisite you install yourself (cadvm does not bundle it).

You only need part 2 for cadvm geom-diff and cadvm view; everything else works without Open CASCADE.

1. The cadvm binary

One-line install (Linux / macOS)

curl -fsSL https://raw.githubusercontent.com/AdeMBCH/cadvm/main/scripts/install-release.sh | sh

This detects your OS/arch, downloads the matching binary from the latest release, and installs it into ~/.local/bin (override the location with CADVM_INSTALL_DIR).

Prebuilt binary (manual download)

Each release ships binaries for Linux, macOS (Apple Silicon) and Windows. Download the one for your platform and put it on your PATH:

# Linux
curl -L -o cadvm https://github.com/AdeMBCH/cadvm/releases/latest/download/cadvm-x86_64-unknown-linux-gnu
chmod +x cadvm && sudo mv cadvm /usr/local/bin/

(macOS: cadvm-aarch64-apple-darwin; Windows: cadvm-x86_64-pc-windows-msvc.exe.)

From source

Requires a recent stable Rust toolchain (tested on 1.96).

# from a clone of the repository
cargo install --path crates/cadvm-cli

This puts cadvm in ~/.cargo/bin. Make sure that directory is on your PATH:

export PATH="$HOME/.cargo/bin:$PATH"
cadvm --help

To update later, re-run the same command with --force.

2. Geometry features (Open CASCADE prerequisite)

Install Open CASCADE

Ubuntu / Debian:

sudo apt-get install -y \
  libocct-foundation-dev libocct-modeling-data-dev \
  libocct-modeling-algorithms-dev libocct-data-exchange-dev \
  cmake g++

macOS (Homebrew):

brew install opencascade cmake

Windows: install OCCT (e.g. via vcpkg opencascade) and CMake, then build from a Developer prompt.

Build the helper

cpp/build.sh        # produces cpp/cadvm-geom/build/cadvm-geom

Point cadvm at it

export CADVM_GEOM_BIN="$PWD/cpp/cadvm-geom/build/cadvm-geom"

Add that line to your ~/.bashrc (or shell profile) to make it permanent. If cadvm-geom is on your PATH, the env var is optional.

One-command install / uninstall

From a clone, on Linux/macOS:

./scripts/install.sh      # builds & installs cadvm; also builds the geometry
                          # helper when Open CASCADE is detected
./scripts/uninstall.sh    # removes the binary and the geometry build

uninstall.sh removes the cadvm binary (via cargo uninstall cadvm-cli) and the locally built helper. Your repositories’ .cadvm/ data is left untouched; if you exported CADVM_GEOM_BIN in your shell profile, remove that line.

Shell completions

cadvm can generate completion scripts for bash, zsh, fish, elvish and PowerShell:

# bash
mkdir -p ~/.local/share/bash-completion/completions
cadvm completions bash > ~/.local/share/bash-completion/completions/cadvm

# zsh (ensure the dir is in your $fpath, then recompinit)
cadvm completions zsh > ~/.zfunc/_cadvm

# fish
cadvm completions fish > ~/.config/fish/completions/cadvm.fish

Reopen your shell, then cadvm <Tab> completes commands and options.

Verifying

cadvm --help                 # lists all commands, including ui
cadvm completions bash | head
cadvm-geom diff a.step b.step  # if you built the helper

Next: Getting started.

Getting started

This walks through a full session on a real part.

Create a repository

mkdir my-part && cd my-part
cadvm init

This creates a .cadvm/ directory. cadvm tracks .step, .stp, .stl and .obj files recursively from here (other files are ignored — see Storage model).

cadvm config user.name  "Your Name"
cadvm config user.email "you@example.com"

Commits made afterwards record this author. You can also override it per-command with the CADVM_AUTHOR_NAME / CADVM_AUTHOR_EMAIL environment variables.

First snapshot

Drop a STEP file in the directory, then:

cadvm status          # piece.step shows under "New"
cadvm snapshot -m "Initial version"

A snapshot captures the whole working tree (there is no staging step).

Make a change and snapshot again

Re-export the part from your CAD tool over the same file, then:

cadvm status          # piece.step shows under "Modified"
cadvm snapshot -m "Enlarged the main bore"

Inspect history

cadvm log             # commits, newest first, with author + date
cadvm show HEAD       # one commit in detail + per-file STEP metadata
cadvm diff HEAD~1 HEAD # metadata diff (size, lines, entities, schema)

See the geometry change

With the geometry helper built:

cadvm geom-diff HEAD~1 HEAD   # added / removed / common volumes + face counts
cadvm view HEAD~1 HEAD --open # 3D diff in your browser

Or do it all interactively

cadvm ui

A full-screen dashboard to browse commits and launch diffs/viewer — see Interactive dashboard.

Next: Typical workflow.

Typical workflow

Branching to explore a variant

cadvm branch second-hole      # create a branch at HEAD
cadvm switch second-hole      # move onto it (refuses a dirty tree without --force)

# …edit the part, then…
cadvm snapshot -m "Added a second hole"

cadvm switch main             # back to the trunk
cadvm branch                  # list branches; * marks the current one
cadvm branch -d second-hole   # delete a branch (not the current one)

switch restores the files of the target branch and never silently discards local changes: it refuses when the working tree is dirty unless you pass --force.

Undoing the last commit

cadvm revert HEAD

This creates a new commit that restores the state of HEAD’s parent (it does not rewrite history). Reverting HEAD is supported.

Restoring files without moving the branch

cadvm checkout HEAD~2                 # restore the whole tree to that revision
cadvm checkout HEAD~2 -- piece.step   # restore a single file
cadvm checkout HEAD~2 --force         # discard local modifications

checkout is restore-like: it changes files on disk but does not move HEAD or the current branch. It refuses to overwrite locally modified files without --force, and never deletes untracked files.

Revisions you can name

Anywhere a revision is expected:

  • HEAD, HEAD~1, HEAD~2, … (and HEAD^)
  • a branch name
  • a full 64-char hash (with or without the blake3: prefix)
  • an unambiguous short-hash prefix

Reclaiming space

cadvm gc            # report unreferenced objects (safe, no deletion)
cadvm gc --prune    # actually delete them

gc --prune removes any objects not reachable from a branch (see Storage model).

Next: Interactive dashboard.

Interactive dashboard (TUI)

cadvm ui

cadvm ui opens a full-screen terminal dashboard (built with ratatui) — a source-control panel for your CAD history.

Layout

 ◆ cadvm  /path/to/repo                                  ⎇ main  ● clean
╭ commits ───────────────────────────╮╭ details ──────────────────────╮
│▌● a81e2421  HEAD  Bloc v2  · Mat …  ││ commit a81e2421…              │
│ ● c9cb67eb  ⎇ main  Bloc v1 · Mat … ││ author: Mat <…>               │
│                                     ││ date: 2026-06-12 …            │
│                                     ││ Files (1)                     │
│                                     ││   ▪ piece.step                │
│                                     ││       70134 B · 1794 lines …  │
╰─────────────────────────────────────╯╰───────────────────────────────╯
 ↑↓ move  m anchor  d diff  g geom  v view  b branch  s status  ? help  q quit
  • Left — the commit list: graph marker (, green on HEAD), short hash, HEAD badge, branch chips (⎇ name), message, author and relative time.
  • Right — details of the selected commit: hash, author, date, parents, message, and each file with its STEP metadata.

Keys

KeyAction
/k, /jmove the selection
mset/clear the anchor (the diff base)
dmetadata diff
ggeometric diff (volumes + faces)
vbuild & open the 3D viewer
bswitch branch
sworking-tree status
rreload
?help · q/Esc quit or close a modal

How diffs choose their two sides

d, g and v compare anchor → selected. If no anchor is set, they compare the selected commit’s parent → selected (i.e. “what this commit changed”).

Set an anchor with m on one commit, move to another, then press d/g/v to compare across an arbitrary range.

Notes

  • g and v need the cadvm-geom helper (CADVM_GEOM_BIN); if it is missing you get a red toast, not a crash.
  • The TUI calls the engine directly — it is the same binary as the CLI.

Geometric diff

The metadata diff (cadvm diff) never inspects geometry. cadvm diffs geometry two ways, depending on the format:

  • STEP/STP (B-Rep) — via cpp/cadvm-geom, a standalone C++/Open CASCADE executable run as a subprocess (no FFI). Needs Open CASCADE; see Installation.
  • STL/OBJ (mesh) — a pure-Rust triangle diff built into cadvm; no Open CASCADE required (see Mesh diff below).

B-Rep diff (STEP/STP)

What it computes

Given two STEP files A and B, it computes the boolean decomposition of their solids:

  • common — material in both (A ∩ B);
  • added — material in B not in A (B − A);
  • removed — material in A not in B (A − B).

For each it reports a volume and face count, plus per-input metrics (volume, surface area, solid/shell/face counts, bounding box).

It also reports a topological face-to-face diff: faces of A and B are matched by their underlying surface — the plane equation, the cylinder’s axis and radius, the cone/sphere/torus parameters — which is invariant to how the face is trimmed. A wall that merely gains a hole keeps the same plane and counts as unchanged; only genuinely new or removed surfaces (e.g. the hole’s cylinder) are reported as added / removed. (Freeform B-spline faces fall back to an area + centroid signature.)

Using it

cadvm geom-diff HEAD~1 HEAD
Geometric diff 50d54d61..62cda376

  piece.step
    volume:  6498.700 -> 6584.340
    area:    2709.870 -> 2902.720
    bodies:  76 shells -> 190 shells
    faces:   76 -> 190
    bbox:    20.00×20.00×20.00 -> 20.00×20.00×20.00
    common:  vol 6266.980 (157 faces)
    added:   vol 173.788 (252 faces)
    removed: vol 109.907 (179 faces)
    faces (topo): 2 common, 188 added, 74 removed

By default it diffs every modified STEP file; restrict it with -- <file>.

Solids vs shells. Many STEP exports are sewn shells rather than OCCT solids, so bodies may report shells. Volumes are still integrated correctly over the faces.

The JSON contract

cadvm-geom diff a.step b.step prints a JSON object to stdout (status, file_a/b, a/b metrics, common/added/removed pieces, faces_topo). A handled geometry failure prints {"status":"error", ...} and still exits 0, so the caller always receives structured output.

Mesh diff (STL/OBJ)

Triangle meshes have no B-Rep faces or solids, so the boolean pipeline does not apply. Instead cadvm classifies each triangle by its distance to the other mesh — entirely in Rust, no Open CASCADE:

  • a triangle of the new version whose centroid lies on the old surface (point-to-triangle distance below a tolerance) is unchanged;
  • one with nothing nearby is added;
  • an old-version triangle with nothing nearby in the new mesh is removed.

Using point-to-triangle distance (not vertex-to-vertex) means a shared face matches even when the two meshes triangulate it differently.

cadvm geom-diff HEAD~1 HEAD          # works on .stl/.obj with no helper
  part.stl
    unchanged: 114 triangles
    added:     158 triangles
    removed:   64 triangles
    bbox:      40.00×30.00×28.00
    (distance-based mesh diff)

Mesh diffing is inherently fuzzier than the B-Rep diff (it depends on tessellation and a distance tolerance), but it needs no Open CASCADE and feeds the same 3D viewer.

To visualize either diff in 3D, see 3D viewer.

3D viewer

cadvm view HEAD~1 HEAD --open

cadvm view turns a geometric diff into a single self-contained HTML file with a hand-written WebGL renderer — no CDN, no server, fully offline. Open it in any modern browser.

What you see

Each face of the part is colored by how it changed:

  • grey — unchanged (the face exists in both versions);
  • green — added (a face of the new version with no match in the old one);
  • red — removed (a face of the old version, gone in the new one).

So you see exactly which faces changed on the real part. Each layer can be toggled in the side panel.

Controls: drag to rotate · scroll to zoom · toggle layers with the checkboxes.

Options

cadvm view HEAD~1 HEAD                 # if exactly one file changed
cadvm view HEAD~1 HEAD -- piece.step   # pick the file when several changed
cadvm view HEAD~1 HEAD -o diff.html    # choose the output path
cadvm view HEAD~1 HEAD --open          # also open in the default browser

You can also launch it straight from the TUI with the v key.

STEP/STP vs STL/OBJ

  • STEP/STP (B-Rep): the cadvm-geom helper classifies each face by its underlying surface and tessellates them — needs Open CASCADE.
  • STL/OBJ (mesh): cadvm diffs the triangles directly in pure Rust (no Open CASCADE). Each triangle of the new mesh is unchanged if it lies on the old surface (point-to-triangle distance) and added otherwise; old triangles with nothing nearby in the new mesh are removed. So view works on meshes even without the helper installed.

Both paths emit the same unchanged/added/removed layers, so the viewer is identical.

Under the hood

The classifier emits flat-shaded, per-color triangle layers as JSON; cadvm embeds that into the HTML template (cadvm-cli/src/viewer.rs) and the WebGL code renders it with per-layer colors and transparency.

Command reference

Run cadvm <command> --help for full flags. All commands operate on the repository discovered by walking up from the current directory.

CommandDescription
cadvm initCreate a .cadvm/ repository in the current directory.
cadvm snapshot -m "msg"Record a snapshot (commit) of all tracked CAD files (STEP/STP/STL/OBJ).
cadvm statusShow new / modified / deleted files vs. HEAD.
cadvm logShow the commit history of HEAD.
cadvm show [<rev>]Show one commit’s details and per-file metadata.
cadvm diff [<a> <b>] [--json]Metadata diff (default HEAD~1..HEAD); --json for scripts/agents.
cadvm checkout <rev> [-- <file>…]Restore the working tree (or named files) to a revision.
cadvm branchList branches.
cadvm branch <name>Create a branch at HEAD.
cadvm branch -d <name>Delete a branch.
cadvm switch <name>Switch branches, restoring their files.
cadvm revert <rev>Create a commit that restores HEAD’s parent state.
cadvm gc [--dry-run | --prune]Report / delete unreferenced objects.
cadvm geom-diff <a> <b> [--json] [--files]Geometric diff of modified files (STEP via OCCT; STL/OBJ pure Rust). --files compares two files on disk, no repo.
cadvm verify <a> <b> --expect '<m><op><v>'… [--files]Assert geometric expectations; exit 0/1 (AI gating & CI). --files for two files on disk.
cadvm view <a> <b>Generate a standalone 3D HTML viewer of the diff.
cadvm uiInteractive full-screen terminal dashboard.
cadvm mcpRun an MCP server over stdio — exposes cadvm as tools for AI agents.
cadvm config [<key>] [<value>]Get / set / list config (e.g. user.name).
cadvm completions <shell>Print a shell completion script.

Common flags

  • --force — on checkout / switch / revert: proceed even when it would overwrite locally modified files or a dirty tree. cadvm never overwrites your work without it.
  • -- <files> — on checkout / geom-diff / view: restrict to specific files.

Safety rules

  • switch and revert refuse a dirty working tree without --force.
  • checkout refuses to overwrite a locally modified file without --force.
  • Untracked files are never deleted.
  • gc only deletes with --prune; gc alone is a dry run.

See Typical workflow for examples and the revision syntax.

Storage model

A repository lives entirely in .cadvm/:

.cadvm/
├── objects/
│   ├── chunks/       # fixed 256 KiB content chunks (file storage)
│   ├── blobs/        # whole-file blobs (optional; cleaned by gc)
│   ├── manifests/    # serialized snapshots
│   └── commits/      # serialized commits
├── refs/heads/<branch>   # each file holds the branch's tip commit id
├── HEAD                  # "ref: refs/heads/main" or a detached commit id
├── config.json           # user.name / user.email and other settings
├── index.json            # size+mtime hash cache (speeds up status)
└── tmp/                  # scratch space for atomic writes

Content addressing

Every object is identified by the BLAKE3 hash of its content (blake3:<hex>), sharded by the first two hex byte-pairs:

.cadvm/objects/chunks/ab/cd/<full-hex>

Writes are atomic (temp file + rename), so a crash can never leave a corrupt object. Writing identical content twice is automatically deduplicated.

Deduplication

  1. Content identityraw_hash is the BLAKE3 hash of the whole file. Identical files share an identity, so status/diff comparisons are exact.
  2. Fixed-size chunking — files are split into 256 KiB chunks, each stored content-addressed, so identical chunks are shared across files and versions.

Chunk-only storage

File content is stored as chunks; the whole file is not duplicated as a standalone blob, so there is no on-disk redundancy. checkout reconstructs each file by concatenating its chunks, and cadvm gc --prune removes any unreferenced objects.

What is tracked

cadvm tracks .step, .stp, .stl and .obj files, recursively from the repository root. Skipped: the .cadvm/ directory, hidden directories, and anything matching .cadvmignore.

A .cadvmignore at the root uses a small pattern syntax:

# comments and blank lines are ignored
*.bak            # glob on the file name (* and ? supported)
build/           # a directory and everything beneath it
/secret/old.step # leading "/" anchors to the repo root

Performance

Working files are hashed in a streaming fashion (fixed-size blocks), so even very large STEP files are processed with constant memory. A hash cache in index.json records each file’s size, modification time and content hash; on the next status an unchanged file (same size + mtime) reuses its cached hash instead of being re-read and re-hashed. The cache is a transparent optimization — a miss simply recomputes — and a snapshot warms it so the following status is a pure cache hit.

File metadata

cadvm does not parse geometry for the VCS — it surfaces cheap, useful figures that feed show, log and the metadata diff:

  • STEP/STP: line count, HEADER;/DATA; sections, FILE_SCHEMA, the entity count, and the top 20 entity types.
  • STL/OBJ: triangle and vertex counts, and an axis-aligned bounding box.

Platform support

ComponentLinuxmacOSWindows
VCS + TUI (init, snapshot, ui, …)
Geometry (geom-diff, view)

VCS core & TUI

Pure Rust and cross-platform by design. Platform-specific bits (the SIGPIPE reset, opening the browser) are gated per-OS, so the binary builds and runs on Linux, macOS and Windows. The TUI works in any modern terminal (Windows Terminal, Terminal.app, common Linux terminals).

Geometry helper

cadvm-geom works wherever Open CASCADE and a C++17 toolchain are available — all three OSes qualify — but the installation of OCCT differs per platform (see Installation). It is only needed for the STEP/STP geometric diff; the VCS, TUI, and the STL/OBJ mesh diff all work without it.

Tested status

The Rust binary (version control + TUI) is built and tested on Linux, macOS and Windows in CI on every push, and prebuilt binaries for all three are attached to each release.

The geometry helper has been built and exercised on Ubuntu 24.04 (OCCT 7.6). On macOS/Windows you may need to adjust the OCCT toolkit names or CMake discovery in cpp/cadvm-geom/CMakeLists.txt.

Limits & roadmap

Current limits

  • The geometric diff matches faces by their underlying analytic surface (plane, cylinder, cone, sphere, torus); freeform B-spline faces use a coarser area + centroid fallback.
  • cadvm cannot merge two concurrent edits of the same file.
  • The STL/OBJ mesh diff depends on tessellation and a distance tolerance, so it is fuzzier than the B-Rep diff.
  • geom-diff / view need the cadvm-geom helper (OCCT) only for STEP/STP; STL/OBJ diff in pure Rust. The rest of cadvm works without OCCT.

Done

  • VCS core — snapshots, log, status, diff, branches, switch, revert, checkout, gc, config/author, deduplicated, gzip-compressed chunk storage.
  • Geometric diffcadvm-geom (C++/OCCT): boolean volumes + metrics + surface-based face-to-face diff.
  • 3D viewercadvm view: self-contained WebGL HTML, per-face green/red/grey changes.
  • STL/OBJ support — versioning + mesh metadata, and a pure-Rust distance-based mesh diff feeding the same 3D viewer (no Open CASCADE).
  • AI/agent surface--json diffs, a verify command (assert expected geometric deltas, exit 0/1), repo-less --files comparison for evals/CI, a GitHub Action (cadvm-verify) to gate generated CAD, and an MCP server (cadvm mcp) exposing cadvm as native tools for agents.
  • Interactive TUIcadvm ui, and shell completions.
  • Cross-platform — CI on Linux/macOS/Windows and prebuilt release binaries.
  • Docs — user guide and API reference published online.

Next

  • Sharper mesh diff (point sampling beyond centroids, configurable tolerance).
  • glTF/PLY mesh formats.
  • A staging index and richer merge tooling.

Developing

Repository layout

cadvm/
├── crates/
│   ├── cadvm-cli/    # the `cadvm` binary (CLI + TUI + viewer template)
│   ├── cadvm-core/   # repository logic, geom bridge
│   └── cadvm-store/  # content-addressed storage
├── cpp/cadvm-geom/   # C++/OCCT geometry helper
├── docs/             # this mdBook (user guide)
└── tests/fixtures/   # sample STEP files

Everyday commands

cargo fmt
cargo test                       # geometry tests skip if CADVM_GEOM_BIN unset
cargo clippy --all-targets --all-features

To run the geometry-backed tests, point at a built helper:

export CADVM_GEOM_BIN="$PWD/cpp/cadvm-geom/build/cadvm-geom"
cargo test

API documentation (cargo doc)

The crates are documented with /// / //! doc comments. Generate the API reference (for contributors) with:

cargo doc --no-deps --workspace --open

Add --document-private-items to include internal items. The build is warning -clean under RUSTDOCFLAGS="-D warnings".

This is the developer/API doc. The user guide is this mdBook (below) — keep the two distinct: rustdoc explains the code, the book explains the tool.

📚 The API reference is published online alongside this guide: https://adembch.github.io/cadvm/api/cadvm_core/index.html (rebuilt on every push by the Docs workflow).

User guide (this book)

cargo install mdbook          # once
mdbook serve docs --open      # live-reloading preview
mdbook build docs             # static site in docs/book/

The geometry helper

cpp/build.sh                  # needs Open CASCADE + cmake + a C++17 compiler

The Rust core talks to it only as a subprocess (see cadvm-core::geom); there is no FFI. Keep OCCT confined to cpp/cadvm-geom.

Publishing to the MCP Registry

cadvm ships a server.json describing it for the official MCP Registry — the catalog MCP clients use to discover and install servers. This page is the maintainer checklist to (re)publish it.

The registry hosts metadata only; the runnable artifact must live in a public package registry. cadvm uses the cargo path (crates.io).

Prerequisites

  • A crates.io account + API token (cargo login).
  • A GitHub account (namespace io.github.AdeMBCH/*).
  • The publisher CLI: brew install mcp-publisher.

1. Publish the crates to crates.io

cadvm is a workspace; publish bottom-up so dependencies resolve:

cargo publish -p cadvm-store
cargo publish -p cadvm-core     # after cadvm-store is live
cargo publish -p cadvm          # the binary crate (the MCP server)

Ownership verification: the cadvm crate’s README already carries the visible marker mcp-name: io.github.AdeMBCH/cadvm, which crates.io serves and the registry checks. Keep it intact.

2. Keep versions in sync

The release tag, the workspace version, the crate on crates.io, and the two version fields in server.json must all match (e.g. 0.1.1).

3. Publish to the registry

mcp-publisher login github      # browser OAuth → grants io.github.AdeMBCH/*
mcp-publisher publish           # reads ./server.json

Verify it is listed:

curl -s "https://registry.modelcontextprotocol.io/v0/servers?search=cadvm" | jq .

Optional: publish from CI (OIDC, no browser)

In a release workflow, authenticate with GitHub OIDC instead of a browser:

permissions:
  id-token: write
  contents: read
steps:
  - run: |
      curl -fsSL https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz | tar -xz
      ./mcp-publisher login github-oidc
      ./mcp-publisher publish

(Run this after the crates are published to crates.io for that version.)

Notes

  • Once a version is published it cannot be re-published; bump the version.
  • The registry currently restricts cargo packages to https://crates.io.
  • A no-toolchain MCPB path (prebuilt binary from GitHub Releases) is an alternative for end users without Rust — a possible future addition.

License

cadvm is released under the Prosperity Public License 3.0.0 — a source-available license.

In plain terms:

  • Free for noncommercial use — personal projects, research, education, nonprofits, evaluation.
  • Reuse & modify — you may build on cadvm as a base and share your changes for noncommercial purposes.
  • Commercial use gets a 30-day trial; beyond that it requires a commercial license from the maintainer.
  • 🚫 No reselling cadvm (or a derivative) as a commercial product without a commercial license.

This is source-available, not OSI “open source,” because it restricts commercial use. The full, authoritative text is in LICENSE.

This page is a plain-language summary, not legal advice. For commercial licensing, contact the maintainer.