Concepts
What Oak is
Oak is a purpose-built VCS designed for agent-driven development. It's the thing your coding agent (Claude Code, Codex, Cursor, etc.) can use to store code and assets.
Oak doesn't run agents. Bring your own. What Oak gives you is the storage and collaboration layer underneath: branch-per-session workflows and lazy mounts for large repos.
Oak ships today for macOS on Apple Silicon and Linux x86_64.
Organizations
An organization is the owner of repositories. Every account gets a personal organization (named after your username); you can also create shared organizations for teams and companies.
The organization is also the deduplication boundary for storage. Blobs and chunks are shared across every repo within the same organization, so a binary asset committed to two repos in the same org is stored once. Storage and bandwidth quotas are accounted at the organization level.
Repository URLs are <organization>/<repo> โ for example oakspace/oak.
Branches and descriptions
The unit of work in Oak is a branch, not a commit. oak init and oak clone auto-create a personal feature branch off main โ you never work directly on main locally. This matches how agents work: one branch per session, merged when the session is done.
Commits on feature branches have no commit message. Instead, the branch description is the source of truth for what a change introduces. Create a branch with oak switch -c <name> and set its description with oak desc "...".
Merging a feature branch into main produces a single squash commit whose message is the branch description. The squash commit retains a pointer to the original branch tip, so the pre-squash history stays reachable for tooling. Direct pushes to main are refused.
Working with agents
Oak doesn't run agents โ bring your own (Claude Code, Codex, Cursor, etc.). The model below is what keeps an agent-driven workflow tidy:
- One branch per session.
oak cloneandoak initalready drop you onto a personal feature branch offmain. Start each agent run on its own branch so a session you don't like can be abandoned without touchingmain. Branches are cheap by design. - Have the agent set the branch description. Commits carry no message, so
oak desc "..."is the source of truth for what the change does โ and it becomes the squash-merge message onmain. Ask the agent to write it for a reviewer, not as a list of touched files. - Give the agent the docs. Use the Copy page button at the top of this page to paste the concepts and CLI reference straight into your agent's context, so it knows the commit workflow before it starts.
- Agent commits, you merge. Let the agent commit on the feature branch and stop there. Review the diff and run the merge to
mainyourself โ merging is a human decision. - Mount large repos instead of cloning them. For monorepos,
oak mountgets the agent editing in seconds rather than waiting on a full pull.
Oak makes no AI calls on your behalf and never trains on your code. Whatever agent you bring is its own integration with its own privacy posture.
Lazy mounts
oak mount lets a working tree live on top of a remote repository without a full local clone. Files materialize lazily through a FUSE filesystem as you touch them, so you can work against a multi-gigabyte monorepo with seconds-to-first-edit instead of waiting for a full pull.
Edits go into a virtual branch backed by a local mount cache. The overlay (modified, deleted, and renamed paths) is the active commit โ the logical commit you're amending as you edit. oak commit finalizes that active commit onto the virtual branch; oak push sends it upstream.
Inside a mounted directory, top-level commands (oak status, oak commit, oak log, oak diff) automatically route to the mount-aware code path, so mounted and locally-cloned repos feel identical at the command level.
Mount requires the CLI built with --features mount plus a FUSE backend installed โ macFUSE on macOS, libfuse on Linux. See Setting up mount permissions below for the one-time setup.
Setting up mount permissions
Lazy mounts rely on a FUSE filesystem, which needs a one-time permission grant per machine. The CLI must also be built with --features mount.
macOS (macFUSE)
- Install macFUSE:
brew install macfuse. - macFUSE installs a system extension that macOS blocks until you approve it. Open System Settings โ Privacy & Security, scroll to the Security section, and click Allow next to the message about system software from the macFUSE developer being blocked.
- Reboot once when prompted โ the kernel extension only loads after a restart. After rebooting,
oak mount oak/oakworks.
Linux (libfuse)
- Install FUSE:
sudo apt install fuse3on Debian/Ubuntu (use your distro's equivalent elsewhere). - If your agent or editor runs as a different user โ or in a sandbox โ and needs to see into the mount, enable
user_allow_otherby uncommenting that line in/etc/fuse.conf. - On distros that gate FUSE behind a group, make sure your user is a member (commonly the
fusegroup), then re-log so the membership takes effect.
Verify the setup with oak mount oak/oak followed by oak mount list. If the mount fails to attach, the error almost always points back at one of the steps above.
Git import and export
oak clone <gitrepo> is the import path from git into Oak. When the argument looks like a git remote URL, Oak shells out to git clone, initializes an Oak repository in the destination directory, then replays the cloned git history into Oak on main. By default the destination directory is derived from the git repo name; pass a destination path as the second argument to choose it yourself.
The CLI's oak export <dest> command is the local counterpart: replay your Oak history into a fresh git repo on disk. This is the documented escape hatch โ your data is never trapped in Oak's format.
oak clone <gitrepo> replays git history into Oak, while oak export replays Oak history back into git. Push code with oak push.
Data portability
The trust story is the export path above: any Oak repo can be replayed into a fully-functional git repo at any time with oak export.
If you want to leave, you can โ with full history intact.
Overview
The oak CLI is the primary way to drive Oak from your terminal โ create, commit, branch, pull, mount, and export repositories.
Pass --help to any command to see its flags. A global --verbose flag (before the subcommand) prints phase timings.
Installation
Install the latest release with one command:
curl -fsSL https://oakvcs.com/install | sh
The binary installs to ~/.local/bin/oak. To upgrade in place:
oak upgrade
Getting Started
Start a new local repository, commit, and push to the server:
# Create and enter a new repo
oak init my-game && cd my-game
# Add files, then snapshot the working directory
oak status
oak commit
# Log in and push โ the remote repo is created on first push
oak login
oak push
Or clone an existing repo from the server:
oak clone owner/repo
cd repo
Core VCS
oak init [PATH]
Initialize a new local repository, auto-creating a personal feature branch parented onto main.
PATH
directory to initialize (default: current directory)
oak status
Show the status of the working directory โ modified, added, and deleted files.
oak diff
Show changes between the working directory and HEAD.
oak commit [--no-verify]
Create a new commit on the current branch, snapshotting the entire working directory. Commits carry no message โ the branch description (set with oak desc) becomes the squash-merge commit message when the branch lands on main.
--no-verify
skip pre-commit and post-commit hooks
oak log [-n LIMIT] [--verbose]
Show commit history.
-n, --limit LIMIT
maximum number of commits to display
--verbose
include changed files in each entry
oak hash
Print the current HEAD commit hash.
oak switch [NAME] [-c] [-d]
Switch to a branch, create one, or detach HEAD at a commit. With no NAME, prompts you to pick a branch interactively.
NAME
branch name or commit hash (omit for interactive picker)
-c, --create
create a new branch with this name and switch to it
-d, --detach
treat NAME as a commit hash and detach HEAD there
oak checkout REFERENCE
Detach HEAD at a specific commit, given by full hash or a unique prefix (โฅ 4 hex chars).
oak desc DESC
Set the current branch's description โ the source of truth for what the branch introduces, and the message used when it squash-merges to main.
oak close [NAME]
Close a branch (defaults to the current branch).
oak restore [PATHS...] [-s COMMIT] [-f]
Restore files to their HEAD state, or to a source commit. Restores everything when no path is given.
-s, --source COMMIT
restore from this commit instead of HEAD
-f, --force
skip confirmation prompt
oak reset [PATH] [-f]
Reset the working directory (or a specific path) to HEAD, discarding uncommitted changes.
-f, --force
skip confirmation prompt
oak merge [--continue | --abort]
Merge the current branch into its parent. Use --continue to resume after resolving conflicts, or --abort to cancel.
Remote operations
oak login [-r URL]
Log in to an Oak server. Credentials are stored in the OS keychain.
-r, --remote URL
server URL (default: https://oakvcs.com)
oak clone [OWNER/NAME] [DEST]
Clone a repository from the server (alias: get). With no argument, opens an interactive picker. A git remote URL is accepted instead and imported into Oak.
OWNER/NAME
repo spec; a bare name resolves to your personal org. Omit to pick interactively
DEST
destination directory (default: the repo name, git-style)
-r, --remote URL
server URL
oak push [-r URL] [-f]
Push commits on the current branch to the remote. The remote repository is created automatically on the first push.
-r, --remote URL
server URL
-f, --force
overwrite remote history even when diverged
oak pull [-r URL] [-f] [--continue | --abort]
Bring the local clone up to date: fetch new commits on the current branch, then merge in changes from the parent branch. --continue / --abort drive parent-merge conflict resolution.
-r, --remote URL
server URL
-f, --force
discard local commits not on remote and sync with remote HEAD
oak fetch [-r URL]
Refresh the local copy of main from the remote without touching the working tree or running a merge.
-r, --remote URL
server URL
Mount
oak mount SPEC [BRANCH]
Shorthand: set up an agent space at ./
SPEC
repository spec in owner/repo form
BRANCH
branch to mount (default: repo head, preferring main then master)
-r, --remote URL
server URL
oak mount start SPEC DEST [-b BRANCH]
Mount a repository at an explicit destination path. Runs in the foreground until interrupted.
-b, --branch BRANCH
branch to mount (default: repo head)
oak mount list
List active mounts.
oak mount status [DEST] [--quiet]
Show status for one mount, or summarize all mounts under the current directory when DEST is omitted.
--quiet
print nothing when every mount under cwd is clean (useful from hooks)
oak mount push DEST
Push the virtual branch (and its dependencies) to the remote.
oak mount forget DEST
Forget a mount's local state (only safe when nothing is mounted there).
oak mount end DEST [-f]
Unmount, forget local state, and remove the mount directory in one step.
-f, --force
discard uncommitted overlay changes instead of refusing
Tools
oak archive [-o PATH]
Create a zip archive of the current directory.
-o, --output PATH
output file path (default: oak export DEST [-b BRANCH] [--git-branch NAME] [-f]
Replay this Oak repository's history into a fresh git repo at DEST โ the documented escape hatch. Preserves original author and timestamp.
-b, --branch BRANCH
Oak branch to export (default: current branch)
--git-branch NAME
name for the resulting git branch (default: same as the Oak branch)
-f, --force
write into DEST even if it already contains files
oak open
Open the current repository in the web browser.
oak upgrade [-f] [-r URL]
Upgrade the Oak CLI to the latest release.
-f, --force
skip confirmation
-r, --remote URL
server URL
Overview
Oak exposes a JSON HTTP API for scripting repositories, branches, organizations, and webhooks โ the same API the oak CLI is built on. Use it to wire Oak into CI, internal tooling, or your own agents.
All endpoints are rooted at:
https://oakvcs.com/api
Requests and responses are JSON (Content-Type: application/json). Paths use the {owner}/{name} form, where owner is a username or organization slug.
Authentication
Authenticate with a personal API key sent as a bearer token:
Authorization: Bearer oak_โฆ
Create and revoke keys from Settings โ API keys, or over the API itself. A newly created key's full secret is returned once โ store it somewhere safe; afterwards only its prefix is shown.
# List your repositories
curl https://oakvcs.com/api/repos \
-H "Authorization: Bearer $OAK_API_KEY"
Unauthenticated requests can read public resources; anything that mutates state, or touches a private repo or organization, requires a key with access.
Conventions & errors
Successful responses return 200/201 with a JSON body. Errors return the appropriate status code (400, 401, 403, 404, 409) and a body of the form:
{ "error": "human-readable message" }
API keys
Manage the keys used to authenticate. POST /api-keys takes {"name": "ci"} and returns the full key once.
/api-keys
List your API keys (prefixes only).
/api-keys
Create a key. Returns the full secret once.
/api-keys/{id}
Revoke a key.
Repositories
POST /repos takes {"name", "description?", "is_public?", "organization_slug?"}. Omit organization_slug to create a personal repo.
/repos?sort=name|updated|created
List repositories you can access.
/repos
Create a repository.
/{owner}/{name}
Fetch a repository (name, head, visibility, โฆ).
/{owner}/{name}/visibility
Set public/private.
/{owner}/{name}/transfer
Transfer the repo to another organization.
/{owner}/{name}/size
Storage used by the repository.
/{owner}/{name}
Delete a repository.
Branches
POST โฆ/branches takes {"name", "description?", "parent_branch?"}. Merging a branch parented on main squashes it onto main, carrying the branch description as the commit message.
/{owner}/{name}/branches
List branches.
/{owner}/{name}/branches
Create a branch.
/{owner}/{name}/branches/{branch}
Fetch one branch.
/{owner}/{name}/branches/{branch}/merge
Squash-merge the branch onto its parent.
/{owner}/{name}/branches/{branch}/sync
Update the branch from its parent.
/{owner}/{name}/branches/{branch}/rename
Rename a branch.
Commits & content
Inspect history and read file content at any commit. Trees and raw files are addressed by commit hash.
/{owner}/{name}/commits/info
Look up commit metadata by hash.
/{owner}/{name}/commits/{hash}/revert
Revert a commit.
/{owner}/{name}/tree/{commit}
List the root tree at a commit.
/{owner}/{name}/tree/{commit}/{path}
List a subtree at a commit.
/{owner}/{name}/raw/{commit}/{path}
Download a file's bytes at a commit.
Organizations
Create and manage shared organizations, their members, and storage. Member roles are owner, admin, and member.
/orgs
List your organizations.
/orgs
Create an organization.
/orgs/{slug}
Fetch an organization.
/orgs/{slug}
Update name, emoji, or description.
/orgs/{slug}/members
List members.
/orgs/{slug}/members
Add a member.
/orgs/{slug}/members/{username}
Change a member's role.
/orgs/{slug}/storage
Organization storage usage.
Webhooks
Register per-repository webhooks fired on branch.created, push, and merge. Discord URLs are detected and posted as channel messages; other URLs receive a JSON payload signed with X-Oak-Signature-256 when a secret is set.
/{owner}/{name}/webhooks
List a repo's webhooks.
/{owner}/{name}/webhooks
Create a webhook.
/{owner}/{name}/webhooks/{id}
Delete a webhook.
This page is for people who want to know how Oak actually works under the hood โ object model, chunking algorithm, and how it compares to the VCSes that influenced it. Oak is written in Rust; the CLI ships as a single static binary.
Manifests and commits, like Mercurial
Oak's object model is closer to Mercurial's design than Git's. There are no tree objects โ each commit points at a flat manifest that records the full directory snapshot as a list of (path, blob_hash, mode) tuples.
Everything is content-addressed via BLAKE3. A blob's identity is its hash; storing the same file twice costs one chunk write. Diffs are computed by diffing two manifests โ walk the path lists, compare hashes, done.
Branch descriptions (not commit messages) are the narrative layer. Local commits don't carry messages; only the squash commit that lands on main gets one, derived from the branch description. This keeps the history clean without requiring rebase discipline.
FastCDC for content-defined chunking
Large files are split into variable-length chunks using FastCDC, a content-defined chunking algorithm. Cut points are determined by the content of the file itself โ not fixed offsets โ so inserting a line near the top of a 100 MB file only invalidates a handful of chunks around the insertion point, not everything after it.
Git stores each version of a large file as a separate blob and relies on pack-file delta compression after the fact. Oak's chunking happens at write time, so deduplication is immediate and works across files that share content โ two binary assets with a common header share those chunks in storage.
Oak's defaults produce chunks roughly in the 256 KB โ 4 MB range (FastCDC's normalization keeps the distribution tight around the target). Each chunk is hashed independently with BLAKE3, and the manifest records the ordered chunk list per blob. Push and pull transfer only the chunks the remote is missing.
How Oak differs from Fossil
Fossil SCM is the closest prior art to Oak in philosophy: both use a single local database file instead of a loose-object store, and both treat the repository as a first-class artifact you can back up with cp. Oak local repos use the same SQLite-backed approach โ the manifests, blobs, and commits live in a single local database file.
Where they diverge:
- Large-file handling. Fossil stores file content inline in SQLite, which caps out on large binaries. Oak uses content-defined chunking (FastCDC) and spills large chunks to external storage keyed by BLAKE3 hash โ large binary assets are a first-class citizen, not a workaround.
- Object model. Fossil's artifact format is a custom text encoding. Oak uses flat manifests (path โ blob hash), closer to Mercurial's design. Simpler to reason about; no need to parse a specialized artifact grammar to understand repo state.
- Branching. Fossil uses a timeline model where branching is a tag on a commit. Oak has explicit named branches with a parent-branch relationship โ branches are first-class, not timeline annotations.
- Scope. Fossil bundles a wiki, bug tracker, and forum into the repository. Oak is VCS-only โ use the tools you already have for issue tracking and documentation.
- Agent workflows. Oak is designed for branch-per-agent patterns:
oak clone, commit on a personal branch,oak push, open a PR. Fossil's model predates this use case.
Fossil's biggest win is its self-contained philosophy โ Oak borrows that. The rest is a different set of trade-offs aimed at a different problem.
Built with HTMX, running in Oregon
The web UI is server-rendered HTML powered by HTMX. Most interactions are partial-page swaps: a button click sends a request, the server returns a fragment, HTMX swaps it into the DOM. No client-side framework, no build step, no JavaScript bundle. The server is a single Rust binary (Axum) deployed on a Linux host in Oregon.
Questions about Oak's internals, or interested in working on it? Email [email protected] or reach out on Discord.