Veyra

Version control that understands your code. Veyra is a Git-compatible version control system written in Rust. It reads and writes standard Git repositories, so you can use it alongside git in either order, push to GitHub or GitLab as usual, and stop using it at any time.

Beta. Veyra is feature-complete for everyday use and tested against real Git, but so far it has only been used day to day by its author, on Windows. The test suite passes on Linux, macOS and Windows in CI (see Compatibility). Because it never changes Git's on-disk format, you can always continue with plain git if something goes wrong.

What it adds to Git

  • veyra undo for everything, backed by a journal of every operation (including git commands you ran yourself), and automatic checkpoints of your working tree before any command touches your files.
  • Secret scanning on commit, with redacted findings and fingerprint allowlists.
  • Intents: records of why code exists, with guards that warn when a change would remove the mechanism they describe.
  • Code intelligence for Rust, TypeScript/JavaScript, Python, Go, Java, C, C++, C#, Ruby, PHP, Kotlin, Swift, Scala, Bash, Lua and Elixir: semantic diffs, structural merges of edits to different functions, impact analysis with evidence, and risk analyzers for migrations and infrastructure that you can extend with your own rules and plugins.
  • Optional AI, off by default: commit messages, splitting a change into commits (by file or by hunk), assisted merges and reviews, every one a proposal you approve.
  • veyra serve, a local, read-only viewer for history, signatures, intents, the undo journal and checkpoints.

Quick start

cargo install --path crates/veyra-cli    # or a release binary, see the README

veyra init my-project && cd my-project
echo "# Hello" > README.md
veyra add .
veyra commit -m "feat: initial commit"

veyra checkpoint -m "before refactor"   # snapshot without committing
veyra op log                            # everything that happened
veyra undo                              # reverse the last operation
veyra doctor --full                     # health report, with every object verified

Command names are Git's (veyra add, veyra commit -m, veyra switch -c, …).

Where to go next

Veyra Command Reference

This page maps Git commands to their Veyra equivalents and marks what is implemented today.

Rule: Git's spelling is canonical. Moving a script to Veyra should be close to s/git/veyra/. New, clearer names exist only as aliases (see DESIGN.md Β§2, A5).

Legend: βœ… implemented and tested Β· 🟑 partial (see note) Β· πŸ—“ planned (phase in ROADMAP.md)

Veyra and Git share the same repository. Anything marked πŸ—“ can be done with git today, and Veyra records it as an external operation, so veyra undo still works afterwards.


Repository setup

GitVeyraStatusNotes
git init [dir]veyra init [dir] [-b <branch>] [--object-format sha256]βœ…Respects init.defaultBranch. SHA-256 repositories work end to end.
git clone <url> [dir]veyra clone <url> [dir]βœ…HTTPS (rustls), SSH, file:// and local paths through gitoxide
git clone --depth N / git fetch --depth N / --unshallowveyra clone --depth N / veyra fetch --depth N / --unshallowβœ…Shallow history in Git's .git/shallow
git clone --filter=…veyra clone --filter=blob:none (also blob:limit=<n>[kmg], tree:<depth>)βœ…A partial clone configured exactly like Git's (promisor remote, .promisor packs), so both tools fetch missing objects on demand. Veyra fetches them in batches before checkouts and merges.
git backfillveyra backfillβœ…Downloads every object a partial clone left on the remote
git statusveyra status [--short] [--porcelain] [--json]βœ…--short and --porcelain (v1) output match git status --porcelain. Paths are shown relative to the repository root.
git configveyra config [--global] <key> [<value>], --unset, --listβœ…Same files as Git
git help / --versionveyra help, veyra --versionβœ…
git clone --sparseveyra clone <url> --scope <name> / --sparse <dir>β€¦βœ…Only the scope's directories are checked out (cone-mode sparse). Scopes are declared in .veyra/scopes.toml. Add --filter=blob:none to download only the scope's files.
git sparse-checkout set/add/list/disableveyra sparse set [dirs] [--scope s] [--no-cone] / add / list / disableβœ…Same .git/info/sparse-checkout and skip-worktree bits as Git (git sparse-checkout list agrees). --no-cone takes Git's gitignore-style patterns (last match wins). Refuses to hide files with local changes.

Staging and committing

GitVeyraStatusNotes
git add <paths> / .veyra add <paths> (alias stage)βœ…Globs (*.rs) work even in PowerShell. Stages deletions, like Git 2.x.
git add -A / -uveyra add -A / -uβœ…
git add -fveyra add --forceβœ…Needed for ignored files
git add -pveyra add -p [paths]βœ…y/n/q/a/d/s (split) per hunk, deletions and binary files as a whole. No hunk editing (e) yet.
git rm [--cached] [-r] [-f]veyra rm (alias remove)βœ…Refuses files with changes unless -f
git mvveyra mv (alias move)βœ…
git restore <paths>veyra restore <paths> / veyra discard <paths>βœ…Difference: the old contents are checkpointed first, so veyra undo recovers them
git restore --stagedveyra restore --staged / veyra unstageβœ…--source, --worktree supported
git commit -m … [-a] [--amend] [--allow-empty]veyra commit β€¦βœ…-F file, --no-edit, or the editor (GIT_EDITOR / core.editor)
git commit --trailerveyra commit --trailer "K: v"βœ…
β€”veyra commit --assisted-by "<tool>"βœ…Adds Assisted-by:. Provenance is declared, never guessed.
β€”veyra commit --intent <id>βœ…Adds Intent:
β€”secret scan + intent check on commitβœ…--allow-secrets, --no-verify
git commit -S / commit.gpgSignveyra commit -S / --no-gpg-signβœ…OpenPGP, X.509 or SSH per gpg.format / user.signingKey (via gitoxide). Merges, cherry-picks and rebases are signed too when commit.gpgSign is set.
Git hookspre-commit, prepare-commit-msg, commit-msg, post-commit, pre-push, post-checkout, post-merge, pre-rebase, post-rewriteβœ…core.hooksPath respected. Hooks run through Git for Windows' sh on Windows. Never run in repositories you don't own. --no-verify skips them.
β€”veyra commit --smart / --split [--dry-run]βœ…Optional AI (see veyra ai). --smart proposes a message you accept, edit or reject; --split proposes a sequence of commits for the staged change, by file or by hunk (hunks of one file can go to different commits), validated to cover every file and hunk exactly once before you see it. Each commit is a normal, undoable commit with an Assisted-by: trailer, and hooks and secret scanning still run.

History and inspection

GitVeyraStatusNotes
git log [revs] [-n N] [--oneline] [--all]veyra log β€¦βœ…Decorations. --json. --trailer Intent=<id>. --all hides refs/veyra/*.
git log --graphveyra log --graphβœ…Topological order, lane rendering like Git's
git show [rev]veyra show [rev] [--stat]βœ…
git diff / --staged / <rev> / <a> <b> / a..bveyra diff β€¦βœ…--stat, --name-only, --name-status, -U<n>, -- <paths>. Hunks match git diff.
git blame [-L a,b]veyra blame <file> [-L a,b] [--rev R] [--json]βœ…Line attribution matches git blame
git reflogveyra op logβœ…Records every operation, including staging and uncommitted files
git shortlog [-s] [-n] [-e] [range]veyra shortlogβœ…Output matches git shortlog; --json
git describe [--tags] [--long] [--always] [--dirty]veyra describeβœ…Same names as git describe
git grep [-i] [-w] [-F] [-n] [-l] [-c] [--cached] [rev] [-- paths]veyra grepβœ…Rust regex syntax; exits 1 when nothing matches, like Git
git bisect start/good/bad/skip/run/log/resetveyra bisect β€¦βœ…Picks the commit that splits the candidates most evenly, merges included. run: exit 0 good, 125 skip, 1–127 bad. Every step is journaled. The session is Git's (BISECT_* files, refs/bisect/*), so either tool can continue or reset it.
git format-patch [-o dir] [--stdout] <range>veyra format-patchβœ…mbox files that git am applies
git apply [--cached|--index] [--check] [-R]veyra applyβœ…Git and plain unified diffs. Hunks apply at their position or where their context matches exactly, never fuzzily; nothing is written unless every file applies. Binary patches are refused. Journaled.
git am <mbox>…veyra amβœ…Reads git format-patch output (author, date, subject, body); one journaled commit per patch
git notes add/append/show/list/remove [--ref R]veyra notes β€¦βœ…Git's notes layout (refs/notes/commits by default); git notes show reads Veyra's notes
β€”veyra diff --semantic / --functions [--json]βœ…Functions, methods, classes and types that were added, removed, modified, had their signature changed, were only reformatted, or renamed (renames need a token-identical, non-trivial body). Rust, TypeScript/TSX, JavaScript, Python, Go, Java, C, C++, C#, Ruby, PHP, Kotlin, Swift, Scala, Bash, Lua and Elixir (Tree-sitter). Other files are listed as unsupported.

Branches

GitVeyraStatusNotes
git branch / -a / -vveyra branch / branch listβœ…--json
git branch <name> [start]veyra branch <name> / branch create <name>βœ…A branch literally named list, create, … needs the create form
git branch -d / -Dveyra branch -d/-D / branch delete [--force]βœ…veyra undo restores deleted branches
git branch -mveyra branch -m / branch renameβœ…Keeps the reflog and branch.<name>.* upstream config
β€”veyra branch stale [--days N]βœ…Age + merged status
β€”veyra branch cleanup [--dry-run]βœ…Deletes local branches fully merged into the default branch. veyra undo restores them.
β€”veyra branch compare <branch> [base]βœ…Ahead/behind, merge base, changed files and a suggestion. --json.
β€”veyra branch graphβœ…The branch tree by commit ancestry (not names), with ahead/behind counts
β€”veyra branch explain [name]βœ…Base, ahead/behind, areas touched, predicted conflicts (in-memory merge, writes nothing), suggested action
git switch <b> / -c / --detach / -veyra switch β€¦βœ…Refuses to overwrite local changes. --force checkpoints first. Creates tracking branches from origin/<b>.
git checkout <b> / -b / -- <paths>veyra checkout β€¦βœ…Prefer switch and restore

Merging and rewriting

GitVeyraStatusNotes
git merge <rev> [--ff-only] [--no-ff] [-m]veyra merge β€¦βœ…Fast-forward, clean 3-way merge, or conflicts written exactly like Git (markers + index stages + MERGE_HEAD)
git merge -s ours|subtree, -X ours|theirs|subtree=<dir>, git merge A B Cveyra merge -s … / -X … / veyra merge A B Cβœ…-s ours keeps our tree; -X ours/theirs decides conflicting hunks; subtree merges find (or are told) the directory; an octopus merge with any conflict is refused before anything changes
git merge --abort / --continueveyra merge --abort / --continueβœ…Abort replays the operation log
git reset [--soft|--mixed|--hard] [rev]veyra reset β€¦βœ…Difference: --hard checkpoints first
git reset <paths>veyra reset <paths> / unstageβœ…
git cherry-pick [-x] [-n] [-m N]veyra cherry-pickβœ…Keeps the original author. Conflicts leave CHERRY_PICK_HEAD like Git. Several commits use Git's .git/sequencer, so --continue / --skip / --abort work from either tool.
git revert [-n] [-m N]veyra revertβœ…The safe way to reverse pushed commits. --continue / --skip / --abort, sequences shared with Git.
git rebase <upstream> [--onto X]veyra rebaseβœ…Defaults to the branch's upstream. --continue / --skip / --abort. State lives in Git's .git/rebase-merge, so a rebase started by either tool can be continued or aborted by the other. veyra undo restores the pre-rebase branch.
git rebase -iveyra rebase -i <upstream>βœ…pick, reword, edit, squash, fixup, drop, exec, break (and their one-letter forms), plus reordering. Uses GIT_SEQUENCE_EDITOR / sequence.editor, then the commit editor.
git rerere / rerere forget / rerere clearveyra merge resolutions [status|list|forget <path>|clear|enable [--auto-stage]|disable] (alias veyra rerere …)βœ…Git's own .git/rr-cache format and conflict ids: resolutions recorded by Veyra are replayed by Git and vice versa. Enabled by rerere.enabled (or an existing rr-cache). Replays apply to merges, cherry-picks, reverts, rebases and stash applications, slightly shifted conflicts included; rerere.autoUpdate stages them. Every replay is reported.
β€”structural merge (automatic in merge, pull, cherry-pick, revert, rebase, stash apply/pop)βœ…When Git's line merge conflicts but the two sides changed different top-level symbols, Veyra merges the file symbol by symbol. It refuses on syntax errors, same-symbol edits, delete-vs-modify and reorders on both sides, and re-parses the result. Every resolution is printed, and recorded as a Veyra-Structural-Merge: trailer. veyra.structuralMerge = true Β· stage (resolve, then stop for review) Β· false.
β€”veyra merge <branch> --assist / veyra ai resolve [paths]βœ…AI-proposed resolutions for the conflicts that remain after structural merge and rerere. Proposals that still contain markers or don't parse are discarded; the rest are shown as a diff with a confidence level. Accepting stages the file (journaled, so veyra undo restores the conflict). --yes accepts only high-confidence proposals; uncertain ones always need a person.

Undo, recovery and checkpoints (Veyra-only)

CommandStatusWhat it does
veyra undo [--dry-run] [--force] [--op <id>]βœ…Undoes the latest operation: any Veyra command, or a change made with plain git. Touches only what that operation changed, and refuses if those things changed again (unless --force, which is itself undoable).
veyra undo commitβœ…The same, but checks that the last operation really is a commit (β‰ˆ git reset --soft HEAD~1)
veyra redo [--force]βœ…Re-applies the last undone operation
veyra op log [-n N] [--all] [--json]βœ…The operation journal, newest first, with ← undo / ← redo markers
veyra op show <id>βœ…Refs, HEAD, index and file changes of one operation
veyra op restore <id> [--before]βœ…Makes the whole repository match that moment
veyra op prune --older-than 30dβœ…Retention (the newest 20 are always kept)
veyra checkpoint [-m msg]βœ…Snapshots tracked and untracked, non-ignored files. No commit, index untouched, never pushed.
veyra checkpoint list / diff <id> / restore <id> [paths]βœ…restore takes a fresh checkpoint first
veyra checkpoint enable / disableβœ…Toggles veyra.autoCheckpoint (snapshot on every command)
veyra checkpoint watch --interval 10mβœ…Foreground timer, no daemon
veyra checkpoint prune --older-than 14dβœ…
veyra stash [push] [-m] [-u] [--keep-index] / list / show [-p] / apply [--index] / pop / drop / clear (aliases shelve, unshelve)βœ…Same refs/stash structure and reflog as Git: git stash and veyra stash see each other's entries. A conflicted pop keeps the entry.
veyra clean -f [-d] [-n] [paths]βœ…Like git clean, but the files are checkpointed first, so veyra undo brings them back. Ignored files are never removed (no -x yet).

Worktrees and submodules

GitVeyraStatusNotes
git worktree add/list/remove/pruneveyra worktree add <path> [branch] [-b new] [--detach] / list / remove [--force] / pruneβœ…Via gitoxide. Each linked worktree has its own operation log and keep ref, so veyra undo there only touches that worktree.
git submodule status/init/update/add/foreachveyra submodule [status] / init / update [--init] [--recursive] / add <url> [path] / foreach <cmd>βœ…Submodules are cloned into their path (Git's embedded layout). Relative URLs resolve against origin.

Remotes

GitVeyraStatusNotes
git remote [-v], add, removeveyra remote β€¦βœ…
git fetch [remote]veyra fetch [--depth N] [--unshallow]βœ…In a partial clone, fetches keep the filter
git pull [--ff-only]veyra pullβœ…Fetch + fast-forward or merge
git push [remote] [refspec…]veyra pushβœ…Implemented on gix's transport (HTTPS, SSH, local). Supports -u, --tags, --delete, +refspec, --force, --force-with-lease, --dry-run, --no-verify. Credential helpers are used. Changed blobs are sent as deltas against their previous version (a thin pack, like git push --thin).
git ls-remote [--heads] [--tags] [remote|url]veyra remote refs (alias veyra ls-remote)βœ…Same output as git ls-remote; --json
β€”veyra sync [--push]βœ…Fetch, then fast-forward, or rebase onto the upstream if both sides moved. Refuses with uncommitted changes. Pushes only with --push.

Tags and releases

GitVeyraStatusNotes
git tag / <name> [rev] / -a -m / -s / -d / -fveyra tag … / tag create|delete|listβœ…-s (or tag.gpgSign) signs. Annotated tags have a tagger.
git verify-commit / git verify-tagveyra verify commit [rev] / veyra verify tag <name>βœ…Uses Git's verification config (gpg.ssh.allowedSignersFile, gpg.program, …)
β€”veyra release notes [range]βœ…Markdown notes since the last version tag, grouped by Conventional Commits type, breaking changes first; --json
β€”veyra release prepare <version> [--dry-run] [-s] [--force]βœ…Preflight (tag is new, clean tree, changes since the last release; warnings for non-semver versions, not on the default branch, unpushed, unsigned, no test attestation), then an annotated tag with the release notes. Journaled.
β€”veyra release verify <tag>βœ…Tag signature, signature status of every commit in the release, attestations; exits non-zero on a bad signature

Diagnostics, security and intent (Veyra-only)

CommandStatusWhat it does
veyra doctor [--full] [--json]βœ…PASS/WARN/FAIL with evidence for identity, integrity, in-progress ops, storage, large files, secrets, branches, journal and compatibility. --full also rehashes and parses every object, verifies pack checksums and checks connectivity. Exits non-zero on FAIL.
veyra security scan [--staged | --rev R] [--json]βœ…Built-in and custom rules, redacted output, fingerprints. Exits non-zero on high-confidence findings.
veyra security allow <fingerprint>βœ…Allowlists a reviewed false positive in .veyra/secrets.toml
veyra intent create --title … [--guard-path G --guard-contains T]βœ…Writes .veyra/intents/<id>.toml
veyra intent list / show / verify [--staged|--rev] / close / logβœ…verify exits non-zero on violations
veyra explain <git command…>βœ…What it does, risk, recovery, Veyra equivalent
git fsck [--full]veyra doctor [--full]βœ…
git count-objects -vveyra storage stats [--json]βœ…
git gc / git maintenance runveyra maintenance run [--prune-expire 2w]βœ…
git pruneveyra maintenance prune [--expire 2w] [-n]βœ…
β€”veyra serve [--port 7420] [--no-open]βœ…
β€”veyra impact [--staged | --commit R | --branch [BASE]] [--tests] [--history N] [--json]βœ…
β€”veyra verify --history [--rev R] [-n N]βœ…
β€”veyra attest run [--sign] -- <cmd> / attest review [--reject] [--sign] / attest listβœ…
β€”veyra verify --provenance [--rev R]βœ…
β€”veyra ai [status] / ai configure [--provider anthropic|command] [--model M] [--effort E] [--command CMD] [--global] / ai disableβœ…
β€”veyra ai explain [REV|--staged] / ai review [scope] / ai summarize [RANGE] / ai risks [scope]βœ…
β€”veyra intent suggest <paths> [--dry-run]βœ…
β€”veyra analyze [--migration] [--infra] [scope] [--fail-on high|medium|low] [--json]βœ…

To see impact, analyze, diff --semantic and commit signatures on every pull request, use the Veyra review GitHub Action: CI.md.

Plumbing

Plumbing commands keep their Git names at the top level (hidden from the main help).

GitVeyraStatus
git rev-parse <spec>veyra rev-parse [--short]βœ…
git cat-file -t/-s/-pveyra cat-fileβœ…
git hash-object [-w]veyra hash-objectβœ… (applies the clean filter inside a worktree, like Git)
git ls-files [-s]veyra ls-filesβœ…
git show-refveyra show-refβœ…
git write-treeveyra write-treeβœ…
git commit-tree <tree> [-p P]… -m Mveyra commit-treeβœ… (signs when commit.gpgSign is set)
git update-ref [-d] <ref> <new> [<old>]veyra update-refβœ… (journaled, so undoable)
git for-each-ref [--format] [patterns]veyra for-each-refβœ… (%(refname[:short]), %(objectname[:short]), %(objecttype), %(subject))
git merge-base [--is-ancestor] A Bveyra merge-baseβœ…
git pack-objects --stdoutveyra pack-objects --stdoutβœ… (ids on stdin)
git archive [--format tar|tgz|zip] [--prefix] [-o] [rev] [paths]veyra archiveβœ… (tar with the commit id in the pax header, so git get-tar-commit-id works)
git bundle create/verify/list-heads/unbundleveyra bundle β€¦βœ… (v2 bundles that git clone and git bundle verify accept; ranges become prerequisites)

Full Git compatibility will be claimed only when a published conformance matrix, backed by automated tests, supports it.


Configuration reference (veyra.* keys in Git config)

KeyDefaultMeaning
veyra.autoCheckpointfalseAlso checkpoint on read-only commands (status, diff, log)
veyra.checkpointMaxFileSize20mLarger files are skipped by snapshots (and reported)
veyra.checkpointExcludeβ€”Multi-valued globs never snapshotted (beyond .gitignore)
veyra.secretsPolicyblockblock Β· warn Β· off for high-confidence findings on commit
veyra.intentPolicywarnwarn Β· block Β· off for guard violations on commit
veyra.ai.provideroffanthropic Β· command Β· off
veyra.ai.modelclaude-opus-5-5Claude API model
veyra.ai.effortmediumlow Β· medium Β· high Β· xhigh Β· max
veyra.ai.commandβ€”Local model command for provider = command
veyra.ai.fallbackstrueClaude API server-side refusal fallbacks (fallbacks: "default")
veyra.ai.maxTokens16000Output limit per request
veyra.structuralMergetruetrue Β· stage Β· false: resolve conflicts in different symbols automatically, resolve and stop for review, or never

Committed, shared policy:

FilePurpose
.veyra/intents/*.tomlIntent records (see DESIGN.md Β§5.2)
.veyra/secrets.tomlallow = [fingerprints], exclude_paths = [globs], [[rule]] custom patterns

Environment

VariableEffect
VEYRA_AGENT=1Agent mode: destructive commands (reset --hard, branch -D, switch --force, discard/restore, undo --force, op restore, checkpoint restore) require --yes
NO_COLORDisables colour
GIT_EDITOR, VISUAL, EDITORCommit message editor (after core.editor), run like Git: sh -c '<editor> "$@"'
GIT_SEQUENCE_EDITORTodo-list editor for rebase -i (before sequence.editor)

Exit codes

0 success Β· 1 error, failed check (doctor FAIL, security scan high-confidence finding, intent verify violation) or merge conflicts Β· 2 usage error

Compatibility

Veyra reads and writes ordinary Git repositories. A repository that Veyra has touched is a repository Git can use, and the other way round. This page lists what that covers, what is tested, and what is not supported yet.

How compatibility is tested

  • Git is the oracle. The end-to-end suite (crates/veyra-cli/tests/cli.rs) runs every workflow with the veyra binary and then checks the result with real Git: git fsck --strict, git status --porcelain, git log, git rerere, git sparse-checkout list, signature verification, and Git reading Veyra's pushes, submodules and worktrees.
  • Mixed use. Tests interleave Git and Veyra commands in the same repository, including Git and Veyra running at the same time (veyra_and_git_running_at_the_same_time_stay_consistent).
  • Property tests and fuzzing cover the parsers and encoders that see untrusted input (mod properties in each crate; fuzz/).

Platforms

PlatformStatus
Windows 10/11 (x86_64, MSVC)Tested: CI on every push, and the author's daily use
Linux (x86_64, glibc)Tested: CI on every push (Ubuntu)
macOS (Apple silicon)Tested: CI on every push
Linux (aarch64), macOS (Intel)Tested when a release is built: the release workflow runs the suite before building

The CI workflow (.github/workflows/ci.yml) runs the whole suite, including the end-to-end tests that use the runner's Git as the oracle. So far only Windows has seen daily use, so reports from Linux and macOS users are very welcome.

Minimum supported Rust version for building from source: 1.88.

Git versions

GitStatus
2.52 (Windows)Tested
Any Git β‰₯ 2.30Expected to work: Veyra writes only long-standing formats (index v2–v4, loose objects, packs v2 with .idx v2, packed and loose refs, reflogs)

Git itself is not required to use Veyra. It's only needed to run the test suite, and for Git hooks that call git.

Repository features

FeatureStatusNotes
SHA-1 repositoriesβœ…
SHA-256 repositories (extensions.objectFormat)βœ…veyra init --object-format sha256
Loose objects, packs, multi-pack directoriesβœ…Read via gitoxide; Veyra writes packs for large adds and maintenance
commit-graph, multi-pack-indexReadNot written; git maintenance can still maintain them
Packed refs, loose refs, symbolic refs, reflogsβœ…
Reftable (extensions.refStorage=reftable)❌Not supported by gitoxide yet. Veyra refuses such repositories with the fix: git refs migrate --ref-format=files (Git 2.46+)
Index v2/v3/v4, split index offβœ…Split index and untracked cache are not written
.gitattributes filters (text, eol, ident, external drivers)βœ…Through gitoxide's filter pipeline
Git LFSβœ…The filter process stores pointers on add, the pre-push hook uploads content on push, and checkouts smudge it back (tested with git-lfs 3.7)
Sparse checkout (cone mode)βœ…Same files and skip-worktree bits as Git
Non-cone sparse patternsβœ…veyra sparse set --no-cone; gitignore-style patterns, last match wins, shared with Git
Linked worktreesβœ…
Submodulesβœ…Cloned in the embedded layout; git submodule absorbgitdirs converts
Shallow clonesβœ…clone --depth, fetch --depth, fetch --unshallow; .git/shallow is Git's
Partial clone (--filter)βœ…blob:none, blob:limit=<n>, tree:<depth>, configured as Git does (promisor remote, .promisor packs); missing objects are fetched on demand, and with --scope only the scope's files are downloaded. veyra backfill fetches the rest
Commit/tag signing: OpenPGP, X.509, SSHβœ…
Hooks (core.hooksPath, husky, lint-staged)βœ…pre-commit, prepare-commit-msg, commit-msg, post-commit, post-checkout, post-merge, pre-rebase, post-rewrite (after --amend), pre-push
rerere (.git/rr-cache)βœ…Shared with Git
Notes (refs/notes/*)βœ…Used for attestations
Merge strategies (-s ours, -s subtree, octopus) and options (-X ours, -X theirs, -X subtree=<dir>)βœ…An octopus merge with a conflict is refused before anything changes, as in Git
Operations in progress: merge, rebase (.git/rebase-merge), cherry-pick/revert sequences (.git/sequencer), bisectβœ…Started by either tool, continued or aborted by the other
Object verificationβœ…veyra doctor --full rehashes every object, checks pack checksums and connectivity (like git fsck --full)
Transports: HTTPS, SSH, local paths, file://βœ…Fetch and clone via gitoxide; push via Veyra's receive-pack client
Git protocol v2 for fetchβœ…Push uses v0/v1, like Git

State Veyra adds

Veyra's own state never changes how Git sees the repository:

PathGit sees it as
.git/veyra/An unknown directory (ignored)
refs/veyra/keep, refs/veyra/keep-<id>Ordinary refs that keep journal objects alive; not pushed by default
.veyra/ (committed)Ordinary files

Deleting .git/veyra/ and refs/veyra/* turns the repository back into one that Veyra has never seen: only the undo history is lost.

Veyra in pull requests

Veyra stores everything in plain Git, so GitHub already shows a commit's trailers (Assisted-by, Intent, Veyra-Structural-Merge) and signatures. What it can't show is what Veyra computes: the impact of a change, analyzer findings, changed symbols. The Veyra review action runs those analyses on every pull request and puts the result where reviewers look: the job summary and one PR comment, updated on each push instead of adding a new one.

The review contains:

  • Risk (low / medium / high), its score and the reasons for it (veyra impact).
  • Analyzer findings: severity, file:line, the offending line, and advice (veyra analyze).
  • Tests to run, and why each one is suggested.
  • May be affected: files that import a changed file or mention a changed symbol, by confidence, with the evidence for each.
  • Changed symbols: functions, methods and types added, removed, modified, renamed or with a changed signature, with lines (veyra diff --semantic).
  • Commits: signature status of each of the PR's commits (good, unverified key, unsigned, bad), their trailers, and the files Veyra merged structurally (Veyra-Structural-Merge), which deserve a second look.
  • Usually changed together: files that change with these ones in history but not in this PR.

Everything is deterministic; no AI is involved.

Usage

# .github/workflows/veyra-review.yml
name: Veyra review
on:
  pull_request:

permissions:
  contents: read
  pull-requests: write   # for the PR comment

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0     # the merge base and co-change history
      - uses: dtolnay/rust-toolchain@stable   # install: source builds veyra with cargo
      - uses: RMV-Coder/veyra/.github/actions/veyra-review@main
        with:
          fail-on: high      # optional: fail the job on high-severity analyzer findings

The action pins Veyra to its own ref: @main builds Veyra's main, @v1.2.0 builds v1.2.0. The first run builds Veyra with cargo (a few minutes); later runs restore the binary from the Actions cache. Once release binaries are published, install: release downloads one instead (the archive's checksum is verified, and its build provenance when gh is available) and the Rust toolchain step can go.

Veyra's own repository runs it in .github/workflows/veyra-review.yml, with source-path: . so each PR is reviewed by the Veyra it builds.

Use pull_request, not pull_request_target

pull_request_target runs with a write token in the context of the base branch; checking out and analysing the PR's code there is a well-known way to leak that token. With pull_request, PRs from forks get a read-only token: the action can't comment, says so in a warning, and the review is still in the job summary.

Inputs

InputDefault
fail-on(empty)high, medium or low: fail the job like veyra analyze --fail-on. The review and comment are written first. Empty never fails.
commenttruePost the review as a PR comment, or update the one posted before.
installsourcesource: cargo install from the Veyra repository (needs a Rust toolchain). release: download a release binary. none: veyra is already on PATH.
versionthe action's refA tag, branch or commit (source), or a release tag (release; latest when empty).
source-path(empty)With install: source, build the Veyra workspace at this path instead.
basethe PR's base branchBranch to compare with.
headthe PR's head commitCommit to review. actions/checkout checks out GitHub's test merge commit for pull requests; the action switches to this commit (detached) when HEAD differs, so the merge commit isn't counted as an unsigned PR commit.
pr-numberthe PR's numberPull request to comment on.
github-tokengithub.tokenToken for the comment.
max-commits250Check signatures and trailers of at most this many of the PR's newest commits.

Outputs

risk, risk-score, findings, high-findings, tests, unsigned-commits, unverified-signatures, bad-signatures, and summary-file (the rendered Markdown), for later steps such as labelling high-risk PRs:

      - uses: RMV-Coder/veyra/.github/actions/veyra-review@main
        id: veyra
      - if: steps.veyra.outputs.risk == 'high'
        run: gh pr edit ${{ github.event.pull_request.number }} --add-label "high risk"
        env:
          GH_TOKEN: ${{ github.token }}

How it works

  1. Install Veyra (setup-veyra.sh): from source, a release, or what's on PATH.
  2. Find the range. Fetch the base branch, switch to the PR head, and deepen a shallow clone until the merge base is reachable (fetch-depth: 0 avoids that).
  3. Collect (collect.sh), all with --json, against the fork point: veyra impact --branch <fork>, veyra analyze --branch <fork>, veyra diff --semantic <fork> HEAD, and for each commit in base..HEAD, veyra log -n 1 (for its trailers) and veyra verify commit. veyra verify --history would walk everything reachable from HEAD, including commits merged in from the base branch, so each PR commit is verified on its own. A failing analysis doesn't stop the others; its section shows the error.
  4. Render (render.py, Python 3 standard library): one Markdown review, written to $GITHUB_STEP_SUMMARY. Paths, commit messages and code excerpts come from the PR, so they are escaped: no HTML, no @mentions, no table-breaking pipes. Long sections are collapsed or dropped to stay under GitHub's 65,536-character comment limit.
  5. Comment. The review starts with a hidden marker, <!-- veyra-review -->. The action looks for a comment by a bot that starts with it and edits it; otherwise it posts one. (With a personal access token the comment is a user's, not a bot's, so each run would post a new one; use github.token or a GitHub App token.)
  6. Gate. With fail-on, run veyra analyze --branch <fork> --fail-on <severity> last.

Signatures on the runner

"Good" means the signature verifies and the runner trusts the key, using Git's own configuration (gpg.ssh.allowedSignersFile for SSH signatures, the GnuPG keyring for OpenPGP). A fresh runner trusts nobody, so a sound signature shows as unverified key until you add the keys before the action runs, e.g. a committed allowed_signers file:

      - run: git config gpg.ssh.allowedSignersFile "$PWD/.github/allowed_signers"

Bad means anything else the verifier reports: the signature doesn't match the commit (it was changed after signing, or the signature is malformed), or a problem such as an expired key.

Running it locally

The scripts work outside Actions, with bash, git, Python 3 and veyra on PATH:

bash .github/actions/veyra-review/collect.sh origin/main /tmp/veyra-review
python3 .github/actions/veyra-review/render.py /tmp/veyra-review > review.md

The renderer's tests use real Veyra output in tests/fixtures/; with a built veyra (in target/ or $VEYRA) they also run collect.sh against a scratch repository:

python3 -m unittest discover -s .github/actions/veyra-review/tests

Analyzer plugins

veyra analyze flags risky database migrations and infrastructure changes with built-in rules. A repository can add its own checks in the committed .veyra/analyzers.toml, in two ways:

  • Pattern rules ([[rule]]): a regular expression matched against added lines. They are data, not code, so they always run.
  • Plugins ([[plugin]]): any program that reads the change as JSON and answers with findings. Use them for checks a pattern can't express: parse a schema, call your linter, look something up.

Declaring a plugin

[[plugin]]
id = "orm-lint"                              # unique, no `/`
command = ["python", "tools/orm_lint.py"]    # program and arguments
paths = ["app/models/**/*.py"]               # optional: the changed files it sees (default: all)
timeout_seconds = 30                         # optional (default 60)

A relative program path such as tools/check is relative to the repository root, and the command runs from the repository root. On Windows, run scripts through their interpreter (["sh", "tools/check.sh"], ["python", "tools/check.py"]).

Trust

A plugin is a program, and .veyra/analyzers.toml is committed: anyone who can push to the repository can change it. So Veyra runs a plugin only after you have trusted it in your clone:

$ veyra analyze
warning: plugin `orm-lint` isn't trusted here (it would run `python tools/orm_lint.py`)
  hint: review it in .veyra/analyzers.toml, then `veyra analyze --trust-plugin orm-lint`

$ veyra analyze --trust-plugin orm-lint
Trusted plugin `orm-lint`: it runs `python tools/orm_lint.py` from the repository root.
  • Trust is recorded in .git/veyra/trusted-plugins.json, which is never committed or cloned.
  • Trust covers the plugin's exact definition. If a commit you pull changes its command, its paths or its timeout, it stops running until you review and trust it again.
  • Plugins never run in a repository you don't own (the same rule Veyra applies to Git hooks).
  • --revoke-plugin <id> withdraws trust; --no-plugins skips all plugins for one run.
  • In CI, the workflow file is under your control, so trusting there is explicit too: veyra analyze --trust-plugin orm-lint --fail-on high.

Trust covers the definition in .veyra/analyzers.toml, not the script it runs. If the script is in the repository, review changes to it as you would any code that runs on your machine.

Protocol

Veyra writes one JSON request to the plugin's standard input:

{
  "version": 1,
  "plugin": "orm-lint",
  "files": [
    { "path": "app/models/user.py", "old": "…previous content…", "new": "…new content…" }
  ]
}

old is null for an added file and new is null for a deleted one. Binary files are sent as null on both sides. If no changed file matches paths, the plugin isn't started at all.

The plugin answers on standard output, and exits with status 0:

{
  "findings": [
    {
      "severity": "high",
      "rule": "nullable-fk",
      "path": "app/models/user.py",
      "line": 12,
      "message": "A new foreign key is nullable",
      "advice": "Backfill first, then add NOT NULL in a second migration",
      "excerpt": "team_id = Column(ForeignKey('teams.id'))"
    }
  ]
}
  • severity is high, medium or low. line (1-based, 0 for the whole file), advice and excerpt are optional.
  • path must be one of the files the plugin was given.
  • Findings are reported as <plugin id>/<rule> (here orm-lint/nullable-fk), so disable = ["orm-lint/nullable-fk"] silences one rule and disable = ["orm-lint"] the whole plugin.

Failures

A plugin that exits with a non-zero status, prints something other than the expected JSON, or doesn't answer within its timeout produces a warning. With --fail-on, it also makes veyra analyze fail, so a broken plugin can't let a change through CI unchecked.

A minimal plugin

#!/usr/bin/env python3
import json, sys

request = json.load(sys.stdin)
findings = []
for f in request["files"]:
    for i, line in enumerate((f["new"] or "").splitlines(), start=1):
        if "DROP TABLE" in line.upper():
            findings.append({"severity": "high", "rule": "drop-table", "path": f["path"],
                             "line": i, "message": "Dropping a table loses data"})
json.dump({"findings": findings}, sys.stdout)

Veyra Vision

This document records the brainstorm that started Veyra: a conversation about how Git could be improved, followed by a proposal for a Rust-based, Git-compatible successor. It summarises that proposal and gives Veyra's verdict on each idea. The engineering decisions are in DESIGN.md, and the schedule is in ROADMAP.md.

Git is excellent at remembering what changed. The next generation of version control should also help developers understand why it changed, what it affects, and how to safely recover when something goes wrong.

The name

Veyra is short, easy to say and type, and doesn't tie the project to "git" or to version control alone. The CLI is veyra. As of 2026-10-20 the crate name was free on crates.io. Before any public launch, still check trademarks and GitHub organisation availability.

Tagline: Veyra β€” version control that understands your code.

The thirteen ideas, and what Veyra does with them

#Idea from the brainstormVerdictWhere it lives
1Semantic diff: understand renames, moved functions and behaviour-preserving changes, never claiming equivalence without evidenceKeep, built on Tree-sitter and difftastic-style structural diffingPhase 4
2Intelligent merge: syntax-aware conflict resolution with Conservative / Assisted / Experimental modes, explained and never silently committedKeep, with Conservative as the default. Phase 1 already does Git-equivalent merges with conflict markers.Phase 1 (merge), Phase 4 (structural)
3First-class undo and recovery: one undo, an operation journal, safeguards for pushed workKeep and build first. One vocabulary: undo, redo, op log/show/restore.βœ… Phase 1
4Automatic checkpoints without commits: local, never pushed, retention-managed, secrets excludedKeep and build first. Snapshots on every mutating command, plus opt-in snapshots on every command and a watch mode. No daemon.βœ… Phase 1
5AI-assisted VCS: commit --smart, commit --split, AI provenanceAdjusted. AI is optional and never decides. Provenance uses declared trailers (--assisted-by), not guesses.Provenance βœ… Phase 1. Smart/split in Phase 5.
6Better branch management: graph, stale, cleanup, compare, explainKeepbranch stale βœ…. Graph, cleanup and compare in Phase 2.
7Repository health (doctor) and pre-commit secret scanningAdjusted: per-check PASS/WARN/FAIL with evidence instead of a 0–100 scoreβœ… Phase 1
8Smarter large-repo support: clone --scope, adaptive fetchingAdjusted: scopes are declared in .veyra/scopes.toml and map to partial clone plus sparse cone, not inferred dependenciesPhase 3
9Change impact analysis: affected modules, recommended tests, risk, all advisoryKeep, deterministic first (import graphs, test mappings), AI optionalPhase 4
10Database and infrastructure change analysis (DROP COLUMN, deleted cloud resources)Keep, as provider plugins. It never replaces migration testing or terraform plan.Phase 5
11Verifiable history: signatures, attestations, provenanceKeep, on existing standards (SSH/GPG signing, in-toto/SLSA attestations)Phase 3
12A more intuitive CLI: undo commit, discard, shelve, explainAdjusted: Git spellings stay canonical, and new names are aliasesexplain, discard, unstage, undo commit βœ…. shelve in Phase 2.
13Intent-based version control: structured why, linked to commits, with violation warningsAdjusted: intents are reviewed files with deterministic guards, linked through Intent: trailersβœ… Phase 1

The brainstorm's priority ranking, and Veyra's

The brainstorm ranked Unified Undo first, then Checkpoints, Semantic Diff, Intelligent Merge, Impact Analysis, Health, CLI, AI, Branches, Large Repos, Verified History and Infra Analysis. Veyra keeps the first two at the top and moves Git compatibility above everything else. A safety net that corrupts repositories is worse than no safety net. See DESIGN.md Β§2 for the full list of adjustments.

Principles kept from the brainstorm

  1. Distributed and offline-first. Every clone is a full repository, and nothing needs a server.
  2. Content-addressed storage. It stays Git's object model.
  3. Deterministic core. No core operation needs AI, a network or an API key.
  4. Explicit developer control. Veyra suggests and explains. It never silently decides.
  5. Compatibility. Existing repositories, GitHub, GitLab and every Git tool keep working.
  6. AI as an optional layer. Core first, then deterministic intelligence (Tree-sitter, static analysis), then optional local or remote models.

The architecture sketch, as adopted

The brainstorm proposed a layered system: a developer interface, an intelligent VCS layer (semantic diff, intent, merge, impact, recovery, security), a Git compatibility layer, and a standard .git/ underneath. Veyra adopts this, with gitoxide as the compatibility layer and far fewer crates at the start. See DESIGN.md Β§3.

Two command styles?

The brainstorm suggested a second, "modern" command set (stage, save, publish). Veyra keeps only uncontroversial aliases (stage, remove, move, discard, unstage). As the brainstorm itself admitted, save and publish blur concepts (committing is not publishing), so they were dropped. See COMMANDS.md.

Veyra Design

Veyra is version control that understands your code, written in Rust and compatible with Git.

This document is the engineering design for Veyra. It started from a brainstorm, summarised in VISION.md, about what a next-generation Git would look like. This document keeps what held up, changes what didn't, and adds what the brainstorm missed. Every claim about "implemented" behaviour can be checked against COMMANDS.md and the test suite.


1. The non-negotiable rule

No intelligent feature may ever compromise repository integrity, deterministic core operations, or a developer's ability to recover their work.

The rest of this document follows from that rule. Five consequences show up throughout:

  1. Git's on-disk format is the source of truth. Veyra never stores code history anywhere Git can't read.
  2. Veyra and Git can be used on the same repository at any time, in either order. There are no lock-in states and no "please only use veyra here" warnings.
  3. Before any command discards work, Veyra snapshots that work. This covers switch, reset --hard, restore, undo, checkpoint restore and op restore.
  4. Every mutating command is journaled, so every one of them can be undone.
  5. AI is never required, and it never decides on its own. It proposes, a deterministic layer verifies, and a human approves.

2. Review of the original proposal: what changes before implementation

The brainstorm (VISION.md) is a good product vision. The changes below are the engineering corrections that make it buildable and safe. Each is labelled Keep, Adjust or Add.

2.1 Adjustments

#Original ideaAdjustmentWhy
A1Two repository modes: Git-compatible and a "native" .veyra/objects storeGit object format only. No native store. Veyra-only state lives in .git/veyra/ (local, never pushed). Shared, reviewable policy lives in a committed .veyra/ directory.Two storage formats means two of everything: two fsck implementations, two GC implementations, two sets of bugs. Git already has a SHA-256 transition. Interoperability is the product. If a native format is ever justified, a measured benchmark has to justify it first.
A2Implement the Git layers ourselves, or "evaluate gitoxide"Build on gitoxide (gix) for objects, packs, refs, index, filters, status, merge and fetch. Wrap it behind veyra-core so it can be replaced. Where gix has gaps (push), implement the missing pieces on gix's protocol crates and offer them upstream.gix is pure Rust, MIT/Apache-licensed, and has years of work on Git edge cases. Rewriting packfile deltas and the index format adds risk and no user value.
A316 crates from day one5 crates now (core, ops, security, intent, cli). Split further only when a boundary has proven itself.Premature crate boundaries freeze the wrong APIs and slow compilation.
A4Tokio for asyncNo async runtime in the core. VCS work is disk-bound, and gix is blocking. Use threads where they help.async/await adds complexity with no throughput gain for local operations.
A5Rename commands (rm β†’ remove, branch <n> β†’ branch create <n>, stash β†’ shelve) and a second "modern" CLI (save, stage, publish)Git spellings are canonical and always accepted. New names are aliases only. Interface B is dropped.Muscle memory and existing scripts are worth more than nicer verbs. The original itself noted that save is ambiguous. Moving a script to Veyra should be close to s/git/veyra/.
A6Several recovery vocabularies: undo --history, history operations, recover --list, recover <id>One concept: the operation log. The commands are veyra undo, veyra redo, veyra op log, veyra op show and veyra op restore. Checkpoints are operations too.Three names for one idea is exactly the confusion Veyra is meant to remove.
A7checkpoint enable with a timer daemonSnapshot on every mutating Veyra command (Jujutsu-style), optionally on read-only commands too (veyra.autoCheckpoint). Add a foreground checkpoint watch --interval. No background service by default.Daemons are a support burden, especially as Windows services and on laptops. Command-time snapshots cover the dangerous moments, and watch covers long editing sessions.
A8doctor shows an "Overall Health: 87/100" scorePer-check PASS / WARN / FAIL with evidence and a fix. No composite score.A single number suggests a precision the checks don't have.
A9Intent violations detected by analysis or AIIntents carry machine-checkable guards (path glob plus a pattern that must remain). They are stored as files in the repository and reviewed in pull requests like code.Deterministic, explainable and branch-aware. AI may suggest guards but never decides that something is a violation.
A10AI provenance recordsStandard commit trailers (Assisted-by:, Intent:), declared by the tool or the human, never inferred.Trailers already work on GitHub and GitLab and with git interpret-trailers. Guessing whether code "looks AI-written" is unreliable.
A11Build semantic diff and merge in-houseReuse first: difftastic-style structural diffing on Tree-sitter. Before borrowing from mergiraf, check its license (GPL-3.0 can't be linked into an MIT/Apache crate).These are hard research problems with good existing tools.
A12veyra sync does fetch, pull and pushSpecified precisely: fetch, then fast-forward or rebase the current branch if the worktree is clean, and push only with --push."Do the right thing" commands surprise people at the worst moments.
A13clone --scope frontend with automatic dependency detectionScopes are declared in .veyra/scopes.toml and map to Git's partial clone plus cone-mode sparse checkout.Dependency inference belongs in impact analysis. Clone has to be predictable.
A14Recovery in Phase 3Recovery is Phase 1. The operation journal wraps every mutating command from the first release.Adding a journal to dozens of commands later is much harder than building it in from the start. Recovery is also the feature that sets Veyra apart.
A15git cat-file β†’ veyra plumbing cat-filePlumbing keeps its Git name at the top level (veyra cat-file). It is hidden from the main help, and the help footer lists it.Scripts port unchanged, and the help screen stays short.
A16Config in .veyra/config.tomlLocal settings are Git config keys under veyra.* (veyra.autoCheckpoint, veyra.secretsPolicy, …).One configuration system. git config veyra.autoCheckpoint true works.

2.2 Additions

#AdditionSummary
B1Prior-art studyJujutsu (operation log, working-copy snapshots, the closest prior art), Sapling (smartlog, undo), git-branchless (undo, smartlog), difftastic, mergiraf, Gitless (a study of simplified concepts). Veyra differs by being Git-format-only, safety-first and intent-aware. It learns from these projects instead of re-inventing them.
B2Machine-readable output--json on the commands people script against, plus stable exit codes. Scripts, CI and AI agents are first-class users.
B3Agent safety modeVEYRA_AGENT=1: every mutation is checkpointed, and destructive commands require an explicit --yes. AI coding agents are now heavy VCS users, and their mistakes have to be cheap to undo.
B4Coexistence with GitAt the start of every command, Veyra compares the repository to the end of its operation log. Changes made by plain git are recorded as external operations, so veyra undo can undo a git commit too.
B5Differential conformance testingThe integration tests use real Git as an oracle: git fsck must stay clean, and git status/git log must agree with Veyra. A compatibility matrix states exactly what is supported.
B6Clean-room licensing policyGit is GPL-2.0, and Veyra is MIT OR Apache-2.0. Contributors may read Git's documentation (including Documentation/technical/*) and observe Git's behaviour, but must not translate Git's source code. See Β§8.
B7Windows as a first-class platformCRLF and core.autocrlf through Git's own filter rules, case-insensitive filesystems, reserved NTFS names, long paths and missing symlinks. The primary development machine runs Windows.
B8Untrusted-repository safetyNever run hooks, filters or editors configured by a repository the user doesn't own (gix's trust model and safe.directory). Reject path attacks such as .git aliases and .. (gix-validate). Secret scanning runs on commit by default.
B9Snapshot-before-discardAny command that would overwrite worktree content snapshots it first (see Β§1).
B10Remote-aware undoUndoing an operation whose commits were already pushed is refused unless --force, and Veyra suggests revert instead. Push operations are journaled. This is Phase 2, which is when push lands.

3. Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ veyra-cli        argument parsing, output (human / --json),      β”‚
β”‚                  orchestration, explain, doctor                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ veyra-ops     β”‚ veyra-intent  β”‚ veyra-security   β”‚ veyra-semanticβ”‚
β”‚ operation log β”‚ intent files  β”‚ secret scanner   β”‚ veyra-analyze β”‚
β”‚ undo / redo   β”‚ guards        β”‚ allowlist        β”‚ veyra-ai      β”‚
β”‚ checkpoints   β”‚ verification  β”‚                  β”‚               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ veyra-core       repository facade: status, add, commit, refs,   β”‚
β”‚                  worktree read/write, flat trees, diff, merge    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ gix (gitoxide)   objects Β· packs Β· refs Β· index Β· filters Β·      β”‚
β”‚                  status Β· merge Β· fetch Β· config                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ .git/            a standard Git repository                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

3.1 Crates

CrateResponsibilityDepends on
veyra-coreRepo facade over gix. FlatTree, a path-to-entry map used to compare HEAD, the index, worktree snapshots and arbitrary trees uniformly. Worktree materialisation with smudge filters. Index construction. Porcelain primitives.gix
veyra-opsThe operation journal, views, undo/redo, op restore, checkpoints and retention.core
veyra-securitySecret detection rules, fingerprints and allowlists. Pure, with no repository access.regex
veyra-intentIntent records (TOML), guard evaluation and ID generation. Pure, with no repository access.toml, globset, regex
veyra-semanticTree-sitter symbol extraction, semantic diff, structural merge, imports. Pure.tree-sitter + grammars
veyra-analyzeMigration and infrastructure risk rules, repository rules. Pure.regex, globset, toml
veyra-aiOptional AI: providers (Claude API over HTTPS, local command) and task prompts with JSON schemas. No repository access; the CLI decides what is sent.reqwest, serde_json
veyra-cliThe veyra binary.all of the above

The pure crates (security, intent) take bytes and paths instead of repositories. That makes them trivial to unit-test and to reuse in CI tools and editor plugins.

3.2 Where state lives

LocationContentsShared?
.git/objects, .git/refs, .git/indexNormal Git dataPushed as Git normally pushes
.git/config, [veyra] sectionLocal Veyra settings, e.g. veyra.autoCheckpoint. veyra config and git config read and write the same keys.No
.git/veyra/oplog.jsonlThe operation journal, one JSON record per lineNo
.git/veyra/lockJournal lockNo
refs/veyra/keepA commit that keeps snapshot trees, index trees and orphaned commits reachable so git gc won't prune themNot pushed by default
.veyra/intents/*.tomlIntent records, committed and reviewedYes
.veyra/secrets.tomlSecret-scan allowlist and custom rules, committedYes
.veyra/analyzers.tomlRepository rules for veyra analyze, committedYes
.veyra/scopes.tomlNamed directory sets for clone --scope / sparse set --scope, committedYes
refs/notes/veyra/attestationsin-toto attestations about commits (Git notes)Push explicitly
.git/rr-cache/, .git/MERGE_RRRecorded conflict resolutions β€” Git's own format, shared with git rerereNo
.git/worktrees/<id>/veyra/, refs/veyra/keep-<id>A linked worktree's own journal and keep refNo

Using Git config for local settings means one configuration system instead of two (there is no config.toml).

Note on git log --all: refs/veyra/keep is a real ref, so git log --all shows a single veyra: keep commit, plus any commits that only the journal still references, such as an undone commit (much like reflog entries). Use git log --all --exclude='refs/veyra/*' to hide them. veyra log --all excludes them automatically.


4. The operation log

The operation log is Veyra's central safety mechanism. It is inspired by Jujutsu's, but it is stored entirely as Git objects plus one append-only file.

4.1 Model

A view is a full description of repository state at one moment:

{
  "head": { "symbolic": "refs/heads/main" },          // or { "detached": "<oid>" }
  "refs": { "refs/heads/main": "<oid>", "refs/tags/v1": "<oid>", ... },  // everything except refs/veyra/*
  "index_tree": "<tree oid>",       // stage-0 index entries written as a tree (dedupes subtrees)
  "index_conflicts": false,         // true β†’ the raw index file is also stored, as index_blob
  "worktree": "<tree oid>"          // snapshot of tracked + untracked, non-ignored files
}

An operation is a pair of views plus metadata:

{
  "id": "7f3a9c2e01b4d8aa",
  "time": 1792483200,
  "command": "veyra commit -m 'feat: login'",
  "kind": "commit",                 // commit | switch | branch | reset | merge | checkpoint | undo | redo | external | ...
  "summary": "commit a8f42bc feat: login",
  "before": { ...view... },
  "after":  { ...view... },
  "target": null                    // for undo/redo: the id of the operation that was undone or redone
}

4.2 Recording

Every mutating command runs inside a transaction:

  1. Take the journal lock (.git/veyra/lock, created exclusively, like Git's .lock files). A lock older than 10 minutes is treated as left over from a crash.
  2. Capture the before view, including a worktree snapshot. This is the automatic checkpoint.
  3. Detect external changes. If the before view's HEAD, refs, index or merge state differ from the last operation's after view, append an external operation first. That keeps undo correct after plain git commands.
  4. Run the command.
  5. Capture the after view, append the record, update refs/veyra/keep, and release the lock. Commands that can't change files (commit, add, branch, tag, …) reuse the before snapshot instead of scanning the worktree again.

If the command fails, nothing is appended. The before snapshot objects are already in the object database, so they become ordinary garbage and are removed by GC.

External operations carry no worktree snapshots. Veyra can't tell what a git command did to files apart from what the user edited, so undoing an external operation restores refs, HEAD and the index. It rewrites files only when HEAD was switched to a different branch or commit (git checkout/git switch), and then only files the user hasn't touched since. A branch that merely moved (git commit, git reset) leaves files alone. That is the conservative choice, and it never discards work.

4.3 Worktree snapshots

A snapshot is the tree that git add -A would produce, written without touching the index:

  • It starts from the index's stage-0 entries.
  • Modified and untracked files (found by gix's stat-cached status) are hashed through the clean filter, so CRLF handling matches Git exactly.
  • Ignored files are excluded, and so are paths matching veyra.checkpointExclude.
  • Files larger than veyra.checkpointMaxFileSize (default 20 MiB) are skipped with a warning.
  • When nothing has changed, the snapshot is the index tree and costs only a status scan.

Restoring a snapshot writes files through the smudge filter, which is the same path Git's own checkout uses.

4.4 Undo and redo: path-wise three-way restore

Undoing operation X must not clobber work done after X. Veyra uses one generic primitive, applied to refs, index entries and worktree paths alike:

for each key k where X.before[k] β‰  X.after[k]:
    if current[k] == X.after[k]:  set current[k] := X.before[k]
    elif current[k] == X.before[k]: nothing to do
    else: CONFLICT(k)             # changed again since X: refuse unless --force

Redo is the same function with before and after swapped. Because the undo is itself recorded as an operation, and its before view includes a fresh snapshot, even undo --force can be undone.

Which operation does veyra undo target? Walk the log chronologically while keeping an undo stack. undo(X) pushes X and redo(X) pops it. Any other undoable operation clears the stack, the same as the redo stack in a text editor. The undo target is the latest undoable operation that is not currently undone. The redo target is the top of the stack. Repeated veyra undo therefore walks back through history instead of toggling.

veyra op restore <id> is the blunt instrument: it makes the whole repository (refs, HEAD, index and worktree) match that operation's after view. Current state is snapshotted first.

4.5 Retention and garbage collection

refs/veyra/keep points at one synthetic commit:

  • Tree: ops/<id[0..2]>/<id>/{before,after}-{worktree,index} entries, which keep every retained snapshot reachable. The two-level fan-out keeps per-operation rewrites small.
  • Parents: commits that an operation orphaned, meaning a ref moved from A to B where A is not an ancestor of B, or a ref was deleted. These are exactly the commits a user might want back.

veyra op prune --older-than 14d (and checkpoint prune) drops old records and rebuilds the keep commit. After that, git gc reclaims the space.


5. Other Phase-1 subsystems

5.1 Secret scanning (veyra-security)

  • Rules are regular expressions with an ID, a description and a confidence level: private-key headers, AWS, GitHub, GitLab, Slack, Stripe, Google, OpenAI and Anthropic key formats, JWTs, and generic password = "…" assignments.
  • veyra commit scans only the added lines of staged text blobs. High-confidence findings block the commit when veyra.secretsPolicy=block (the default). Lower-confidence findings only warn.
  • False positives are allowlisted by fingerprint (a hash of rule, path and matched text) in the committed .veyra/secrets.toml, so the allowlist itself never contains the secret.
  • veyra security scan scans the worktree, the index or a revision.
  • Secret scanning is a safety net, not a guarantee. It complements dedicated scanners and does not replace them.

5.2 Intents (veyra-intent)

An intent records why code exists, as a reviewed file:

# .veyra/intents/int-8a92c1.toml
id = "int-8a92c1"
title = "Prevent duplicate payment processing"
kind = "bugfix"
status = "active"
created = "2026-10-20"
objective = "Concurrent requests must not create duplicate payment records."
solution = "Idempotency keys on payment creation."
constraints = ["Maintain backward compatibility"]
related = ["PAY-142"]
verification = ["Concurrent payment tests"]

[[guard]]
paths = ["src/payment/**"]
must_contain = "idempotencyKey"
message = "Idempotency validation must remain in the payment service"
  • veyra commit --intent int-8a92c1 adds an Intent: int-8a92c1 trailer.
  • veyra intent verify evaluates every active intent's guards against the worktree, the index (--staged) or a revision. veyra commit runs it on the index and warns, or blocks when veyra.intentPolicy=block.
  • Guards are deterministic and explainable, and they live on branches like code. Superseding an intent is an ordinary reviewed change to its file.

5.3 veyra explain

veyra explain <git or veyra command…> uses a built-in knowledge base to describe what a command does, how dangerous it is, how to recover from it, and what the Veyra equivalent is. For example, veyra explain reset --hard. Explanations match on the longest known command and flag prefix.

5.4 veyra doctor

Each check reports PASS, WARN or FAIL with evidence and a suggested command. The checks cover: integrity (refs resolve, commits and trees exist along the history, the index checksum), storage (loose-object count, pack count and sizes), large blobs in HEAD, secrets in HEAD, stale and merged branches, Git operations in progress, journal health, and identity and line-ending configuration.


6. Git compatibility strategy

  • Read compatibility: anything gix can read (all current Git repository formats with SHA-1 objects; SHA-256 as gix matures).
  • Write compatibility: every object, ref and index Veyra writes must pass git fsck and leave git status consistent. The integration tests enforce this.
  • Behavioural compatibility: porcelain commands follow Git's semantics where Veyra keeps the Git name. Where Veyra deliberately differs (for example, switch snapshots before acting), COMMANDS.md documents the difference.
  • Honesty: Veyra does not claim "full Git compatibility" until a published conformance matrix backed by automated tests says so.

7. Non-goals (for now)

  • A native, non-Git object format (see A1).
  • A hosting server or forge.
  • Replacing dedicated security scanners, CI systems or code-review tools.
  • Mandatory AI features.

8. Clean-room and licensing policy

Veyra is dual-licensed MIT OR Apache-2.0, following the Rust ecosystem convention.

SourceMay we…Notes
Git documentation, including Documentation/technical/* format specsRead and implement from itSpecifications of file formats and protocols are what interoperability requires
Git's observable behaviourTest against it and match itDifferential testing is encouraged
Git's C source code (GPL-2.0)Not translate or port itA line-by-line translation would be a derivative work and would force GPL onto Veyra
gitoxide (MIT/Apache-2.0)Depend on it, read it, contribute to itCompatible license
mergiraf (GPL-3.0)Learn from published ideas onlyNot linkable into Veyra
difftastic (MIT), Jujutsu (Apache-2.0)Read; reuse code with attributionCompatible licenses

If a contributor has studied a specific Git C function closely, they should describe the behaviour in a test and let someone else implement it.

Stability promise (1.x)

While Veyra is in beta (see the README), this promise is the target, not yet a guarantee: if early users find a design problem, a fix may change something listed here, and the release notes will say so.

From 1.0, Veyra follows semantic versioning. Within 1.x, the following are stable. Anything not listed here (internal crate APIs, the exact wording of human-readable output, colours, hints) may change in any release.

Command line

  • Commands, subcommands, flags and their meaning don't change incompatibly. New flags and commands may be added.
  • Deprecated flags keep working for the rest of 1.x and print a warning that names the replacement.
  • Exit codes: 0 success; 1 failure, or a finding that the command reports as a failure (secrets, intent violations, analyze --fail-on, conflicts); 2 usage errors.
  • Agent mode (VEYRA_AGENT=1) keeps refusing destructive actions without --yes.

--json output

  • Fields are never removed, renamed or changed in type in 1.x. New fields may be added, so consumers must ignore unknown fields.
  • Enumerated string values ("high", "medium", …) may gain new values; consumers should treat unknown values conservatively.
  • Every --json output is a single JSON document on stdout. Diagnostics go to stderr.

Repository data

  • Git data: Veyra writes standard Git formats only; see COMPATIBILITY.md.
  • Operation journal (.git/veyra/oplog.jsonl): one JSON object per line. Records written by any 1.x release can be read (and undone) by every later 1.x release. Readers ignore unknown fields, so newer records stay readable by older 1.x versions, which simply don't use the new information. A torn last line from a crash is ignored and cleaned up on the next write.
  • Committed policy files (.veyra/intents/*.toml, secrets.toml, scopes.toml, analyzers.toml): files valid in 1.0 stay valid in 1.x with the same meaning.
  • Trailers Veyra writes (Assisted-by, Intent, Veyra-Structural-Merge) keep their names and meaning.
  • Attestations stay in-toto v1 Statements in refs/notes/veyra/attestations.

Configuration

veyra.* Git config keys and their defaults are stable. A default changes only if the old behaviour was unsafe, and the release notes say so.

Not covered

  • The Rust APIs of the veyra-* library crates. They are versioned with the binary but may change in minor releases until they are published as separately versioned crates.
  • AI output. Models and prompts improve; what stays stable is that AI features are off by default, everything they produce is a proposal, and nothing is changed without approval.

Veyra Roadmap

Each phase ends with an exit criterion that has to be met before the next phase starts. Git compatibility is the main engineering challenge (DESIGN.md Β§6), so every phase adds conformance tests that use real Git as the oracle.

Phase 1 β€” Foundation + safety net βœ… (v0.1, current)

The order was changed from the original brainstorm (A14): recovery comes first, because the journal has to wrap every mutating command from the start.

  • Cargo workspace: veyra-core, veyra-ops, veyra-security, veyra-intent, veyra-cli
  • Repository facade on gitoxide 0.89: objects, refs, index, status, clean/smudge filters
  • Porcelain: init, clone, status, add, rm, mv, restore/discard/unstage, commit (amend, trailers, editor), log, show, diff, branch, switch, checkout, merge (ff / clean 3-way / conflicts / abort / continue), reset, tag, remote, fetch, pull, config, plumbing basics
  • Operation log with undo / redo / op restore, external-change detection, gc protection
  • Automatic and manual checkpoints, watch mode, retention
  • Secret scanning on commit, security scan, fingerprint allowlists
  • Intents with deterministic guards, Intent: / Assisted-by: trailers
  • doctor, explain, branch stale, --json, agent mode
  • Differential tests against Git (fsck, status, log, diff, CRLF, gc survival, merge conflicts)

Exit criterion: met. 66 tests pass on Windows, and every repository Veyra writes passes git fsck --strict.

Known gaps carried forward: push, stash, rebase, cherry-pick, revert, hooks, commit signing, add -p, log --graph, blame, submodule writes, sparse checkout writes, SHA-256 repositories.

Phase 2 β€” Everyday Git parity βœ…

  • veyra push on gitoxide's transport and handshake: the receive-pack protocol, -u, --tags, --delete, +refspec, --force, --force-with-lease, --dry-run, credential helpers. Journaled, and veyra undo refuses to silently unpublish pushed commits (B10).
  • stash / shelve / unshelve, compatible with refs/stash and its reflog
  • clean, with checkpoint-first
  • cherry-pick, revert, rebase and rebase -i (pick/reword/edit/squash/fixup/drop and reordering), with Git-compatible conflict state
  • blame, log --graph
  • Git hooks (pre-commit, prepare-commit-msg, commit-msg, post-commit, pre-push, post-checkout, post-merge), with Git for Windows' sh discovery and a trust check
  • branch -m keeps the reflog and upstream configuration
  • Delta-compressed (thin) packs for push: a block-matching delta encoder, REF_DELTA against the previous version of each path
  • add -p (per-hunk y/n/q/a/d/s) and exec/break in rebase -i
  • branch cleanup [--dry-run], branch compare, sync [--push]
  • branch graph, branch explain (with an in-memory merge preview)
  • CI on Linux, macOS and Windows (first green run in 1.3, once the repository went public), and a published compatibility matrix

Exit criterion: a developer can use Veyra full-time on a GitHub project without falling back to git, and the conformance suite covers every Phase 1–2 command on all three platforms.

Phase 3 β€” Trust and scale βœ…

  • Commit and tag signing (OpenPGP, X.509, SSH) via gitoxide, honouring commit.gpgSign / tag.gpgSign; verify commit, verify tag, verify --history
  • Attestations: in-toto v1 Statements (test results, reviews) in Git notes, optionally signed; verify --provenance
  • clone --scope / --sparse and sparse set/add/list/disable (Git's cone mode, skip-worktree bits)
  • storage stats, maintenance run / pack-loose / prune (no git gc needed)
  • Worktrees (each with its own journal) and submodules (status/init/update/add/foreach)
  • SHA-256 repositories (init --object-format sha256)
  • Performance budgets: cargo test --release -p veyra-cli --test perf -- --ignored (10k files). New blobs are written as packs instead of thousands of loose files.
  • Partial-clone object filters (--filter=blob:none, blob:limit, tree:), driven by Veyra on gitoxide's transport, with on-demand fetching of missing objects (1.3)

Exit criterion: met. Signed history verifies in both Git and Veyra, and the 10k-file budgets pass.

Phase 4 β€” Code intelligence (deterministic) βœ…

  • diff --semantic / --functions with Tree-sitter (Rust, TypeScript/TSX, JavaScript, Python, Go, Java): added, removed, modified, signature changed, reformatted-only and renamed symbols. The grammars are C code compiled into the binary.
  • Structural merge in conservative mode: conflicts in different top-level symbols are merged per symbol, re-parsed and verified; reported on screen and as a Veyra-Structural-Merge trailer; veyra.structuralMerge = stage stops for review instead of committing
  • impact [--staged | --commit | --branch] [--tests]: import resolution per language, name references, test mapping, co-change history, risk with reasons, limitations reported
  • merge resolutions (rerere) in Git's rr-cache format
  • C, C++, C#, Ruby, PHP, Kotlin, Swift, Scala, Bash, Lua and Elixir grammars (1.3)

Exit criterion: met (for the original six languages; eleven more were added in 1.3). False-equivalence guards: "reformatted" requires an identical token sequence and tree shape (so re-indenting Python is a change), renames require a token-identical, non-trivial body, and structural merges refuse anything they can't verify.

Phase 5 β€” Optional AI and domain analyzers βœ…

  • veyra ai configure / disable / status: the Claude API (raw HTTPS; there is no official Rust SDK) or any local model through the command provider. Off by default; keys only from the environment; every payload is secret-scanned; --dry-run shows what would be sent.
  • commit --smart (message proposals) and commit --split (file-level proposals, validated before they are shown). The developer always approves.
  • Assisted merge (merge --assist, ai resolve): proposals are checked for markers and syntax, shown as a diff with a confidence, and uncertain ones always need a person
  • ai explain / review / summarize / risks (risks build on the deterministic impact report)
  • Migration and IaC analyzers (analyze --migration / --infra), extensible with repository rules in .veyra/analyzers.toml
  • AI-proposed intents (intent suggest): guards that don't hold are dropped; a human accepts
  • Hunk-level commit --split; out-of-process analyzer plugins with local trust (1.3)

Exit criterion: met. Every AI feature degrades to "AI features are off" with no side effects, proposals are validated deterministically, and nothing changes history without explicit approval (--yes in scripts; never for low-confidence merge resolutions).

Phase 6 β€” Production hardening β†’ 1.0 βœ…

  • Property tests for every parser and encoder that sees untrusted input (delta, journal, conflict normalization, Tree-sitter analysis, policy files, secret scanning) and cargo-fuzz targets for the same (fuzz/). Git's own formats are parsed by gitoxide, fuzzed upstream.
  • Crash consistency: a torn journal record is tolerated and cleaned up on the next write
  • Concurrency: Veyra commands queue on the journal lock; Veyra and Git can run at the same time β€” index writes are compare-and-swap under Git's index.lock, so a concurrent git add is never silently lost, and changes Git makes during a checkpoint are journaled separately
  • Release workflow (Linux, macOS, Windows binaries, SHA-256 checksums, build-provenance attestations), install scripts that verify both, Homebrew and winget manifests
  • COMPATIBILITY.md and the 1.x STABILITY.md promise for CLI flags, --json output, the journal format and committed policy files
  • The documentation as an mdBook site, published to GitHub Pages by .github/workflows/docs.yml (1.3)
  • Submit the Homebrew and winget manifests

Exit criterion: met for 1.0.

After 1.0

  • veyra serve: local read-only viewer (1.2)

  • Pull-request review action (1.2)

  • Rebase, bisect and cherry-pick/revert sequences shared with Git (1.3)

  • Partial and shallow clones, merge strategies, non-cone sparse patterns, doctor --full, Git LFS coverage, pre-rebase / post-rewrite hooks (1.3)

  • Eleven more languages, hunk-level commit --split, analyzer plugins, documentation site (1.3)

  • Reftable, once gitoxide reads it (until then Veyra refuses reftable repositories with the git refs migrate fix)

  • Dropping the beta label once Veyra has seen daily use on Linux and macOS