A clean-sheet reimplementation of Apache Subversion in the Aether systems language. The goal isn't a mechanical C→Aether translation — we read the avn C tree as a reference for behaviour contracts and wrote our own on-disk format, wire protocol, and algorithms.
The reference avn C code still lives at libsvn_*/ in this repo,
read-only. The port lives at the repo root in per-area dirs (client/, delta/, repo_storage/, working_copy/, etc). The old Apache-avn README is
preserved at README.apache-svn-upstream.
- 57 commits, each phase reviewable in isolation.
- 47 test suites, ~507 assertions, all green.
- ~14,400 lines (Aether + C shims + shell drivers).
- Daily-driver workflow end-to-end:
checkout → edit → status → diff → add → rm → commit → update → branch → merge.
Full phase map and feature matrix: PORT_STATUS.md.
| Binary | Role |
|---|---|
avn |
Client CLI: checkout, status, add, rm, cp, mv, commit, update, revert, diff, log, cat, ls, info, propset/get/del/list, merge, branch create/list, switch, blame, verify |
avnserver |
Read/write HTTP server. JSON for metadata, base64 for bytes over JSON, raw bytes for cat. |
avnadmin |
Repository admin: create, dump, load (our own portable dump format; not reference-avn compatible by design). |
avnsync |
Filesystem-sync client — Dropbox-shape pairing of a local directory with a server branch. init / status / pull / push / sync / watch. Server-side 3-way auto-merge (MB-I) handles concurrent non-overlap edits cleanly. See avnsync/README.md. |
avnproxy |
Class-1 horizontal-scaling front door: one port fans reads to --read-only replicas and writes (+ GET /info) to the write primary, all sharing one repo via SQLite WAL. See avnproxy/README.md. |
avn-seed |
Test-only helper that populates a repo with a known tree across three commits. |
Build and test driven by aetherBuild's aeb. Run aeb --init once to
populate .aeb/lib/. After that, one command builds every binary
(regenerating _generated.c files as needed via aetherc) and runs the
full 32-test suite.
# Build every binary AND run the 32-test suite
aeb
# Build a single binary
aeb avn # just avn — output at target/avn/bin/avn
# Outputs land under target/<dir>/bin/<name>
target/avnadmin/bin/avnadmin create /srv/repo
target/svnserver/bin/avn-seed /srv/repo # three-commit known tree
target/svnserver/bin/avnserver demo /srv/repo 8080 &
target/avn/bin/avn checkout http://localhost:8080/demo wc
cd wc
echo "hi" >> README
../target/avn/bin/avn commit --author alice --log "tweak"Some .c files at the repo root in per-area dirs (client/, delta/, repo_storage/, working_copy/, etc) are produced by aetherc --emit=lib from
paired .ae sources (grep for _generated.c). They're not checked
into git — aeb's aether.program(b) { regen(...) } setter
regenerates them on demand. Never hand-edit a _generated.c; the next
regen blows your changes away. Edit the .ae source instead.
The port shares avn's commands, data model, and user-facing semantics, but diverges freely on the plumbing where doing so pays off.
- Content-addressable rep store at
$repo/reps/aa/bb/<sha>.repwithZ(zlib) orR(raw) header byte. SHA-1 by default, SHA-256 selectable at repo-create time; format file records the choice. - Per-repo pluggable hash:
avnadmin create PATH --algos sha256. Server advertises viaGET /info; clients adapt at checkout time and persist the algo inwc.db. Golden list enforced at create time — you can't open a repo whose hash your build doesn't support. - Multi-algo secondary hashes (Phase 7.5): add SHA-256 to an
existing SHA-1 repo so you can migrate gradually.
avn verify --secondariescross-checks both. - Merkle verification with per-user redaction (Phase 7.1): a
restricted user's recomputed root hash matches what the server
reports, even though the tree they see has denied subtrees
replaced by opaque
{kind: "hidden", sha: ...}entries. - rep-cache.db is SQLite; also hosts
path_rev(Phase 8.1) for O(touched-revs) queries, andrep_cache_secfor secondary hashes. - Branches (Phase 8.1+): each branch has its own tree root
(git-like), its own head file at
$repo/branches/<name>/head, and its own revs fanout at$repo/branches/<name>/revs/aa/bb/NNNNNN. Rev numbers are per-repo, not per-branch. Design rationale:branching_connnundrum.md.
-
REST + JSON + base64 over plain HTTP. No WebDAV, no DeltaV, no
ra_svn/ra_serf/ra_localmultiplexing. Endpoints fit on a screen:GET /repos/{r}/info GET /repos/{r}/rev/{N}/info GET /repos/{r}/rev/{N}/cat/<path> GET /repos/{r}/rev/{N}/list/<path> GET /repos/{r}/rev/{N}/paths GET /repos/{r}/rev/{N}/props/<path> GET /repos/{r}/path/<p> # HEAD content PUT /repos/{r}/path/<p> # single-file commit DELETE /repos/{r}/path/<p> # single-delete commit POST /repos/{r}/commit # bulk commit POST /repos/{r}/copy # server-side cp POST /repos/{r}/branches/<NAME>/create # create branch -
URL grammar:
http://host/repo/BRANCH[;PATH]—;separates branch from path. Legacy formhttp://host/repo[/PATH]still works and impliesmain. -
Optimistic concurrency via
Svn-Based-On: <sha>header, not revision-based locking. -
ACL-aware responses: denied subtrees are indistinguishable from "doesn't exist" at the HTTP layer.
- Our own portable format (Phase 12). Round-trips through
avnadmin dump/avnadmin load. Not byte-compatible with reference avn's dumpfile format — if you need to migrate into this port, you're on your own for now.
- ACL lines (
+alice,-eve) stored at per-path granularity, anchored to each rev. Nosvn:mergeinfo-style property hackery. - avn cp refuses unless RW-everywhere (Phase 7.7): you can't server-copy a subtree you don't have full RW access to, closing the "branch off a locked area to escape its ACLs" gap.
- Branches are first-class (Phase 8.1+). Each branch has an
include-glob spec; paths outside the spec are rejected at commit
time. Cross-branch
avn cpis refused. Super-user bypasses.
- Single
wc.db(SQLite) per WC. Pre-1.7-style entries files are not supported — clean-sheet means clean-sheet. - Pristine store is content-addressable under
.svn/pristine/, matching the repo's hash algo.
- Command names and semantics.
avn statusmeans what you'd expect, including?/M/A/D/R/ conflict markers. - Three-way merge with svn-style conflict markers in working files.
svn:ignoreread bystatus.avn log -vper-rev path list.svn:mergeinforange arithmetic (adjacent ranges collapse; reverse ranges cancel forward ranges of the same rev).
- Real authentication beyond the
X-Svnae-Superuser:token stub - TLS (deferred; plain HTTP today)
- Hooks (pre/post-commit scripts)
- Locks (
avn lock/unlock) svn:externalslibmagicreplacement (extension-based mime stub when needed)
util/ foundations — error, path, checksum, compress, io,
sqlite, utf8proc bindings. Each is a C shim + .ae
wrapper + a test.
delta/ svndiff1 encode/decode, xdelta match-finder.
repo_storage/ content-addressable blob store, revisions, txn,
server-side copy, commit finalisation.
repos/ query API (log, cat, list, info) on top of repo_storage.
client/ REST client (libcurl + cjson).
svnserver/ HTTP server wrapping repos/, per-route handlers,
commit/copy POST endpoints.
working_copy/ working copy: db, pristine, checkout, status,
mutate (add/rm), commit, update, revert, diff,
cp/mv, props, merge.
avn/ the avn CLI.
avnadmin/ avnadmin CLI (create/dump/load).
avnsync/ filesystem-sync client (Dropbox-shape pairing of a
local dir with a server branch). MIT-licensed
subtree — see avnsync/LICENSE and avnsync/README.md.
avnsync_gui/ system-tray companion for avnsync, built on
aether-ui (GTK4 / SNI). MIT-licensed. Built by its
own build.sh, not aeb — see avnsync_gui/README.md.
avnproxy/ Class-1 scaling front door — read/write-splitting
reverse proxy over std.http.proxy. See
avnproxy/README.md.
ffi/ third-party FFI bindings (openssl).
Each module has one or two C shim.c files (the real implementation)
and an Aether wrapper/driver. Tests are either in-language .ae or
.sh drivers that exercise the whole stack through built binaries.
- PORT_STATUS.md — phase map, feature matrix, commit-by-commit progress.
- branching_connnundrum.md — branching design (Option B, include-only specs, Piper-scale target).
- AETHER_ISSUES.md — running list of Aether bugs/rough edges found during the port, triaged against the current Aether main.
Aether sources: Apache License 2.0. Header on each file:
Copyright 2026 Paul Hammant (portions).
Portions copyright Apache Subversion project contributors (2001-2026).
The libsvn_*/ C tree retains its original Apache-2.0 headers verbatim.
Exception: the avnsync/ subtree is MIT-licensed, copyright 2026
Paul Hammant — see avnsync/LICENSE. MIT is GPL-
and Apache-2.0-compatible, so AvnSync can keep importing the
Apache-2.0 client / working_copy / util modules without friction.