Snapshots (Multi-Version Management) Guide
This guide explains how to enable and use OpenViking's multi-version management (snapshots). On top of VikingFS, it provides Git-based commit/log/show/restore primitives, letting you save an account's resource tree as a series of immutable snapshots, walk history, compare versions, and restore the workspace to any past state.
Multi-version management is powered by gitoxide embedded in the Rust RAGFS layer, maintaining one logical Git repository per account_id. It is fully transparent to callers — you never run any git command yourself.
For the full API reference of each command's parameters and responses, see Snapshots API.
Prerequisites
- You already have a working
ov.conf. - Resource read/write is verified to work (snapshots build on filesystem resources).
- If you choose the S3 backend for Git objects, prepare the bucket, region, endpoint, and access credentials first.
Enabling Multi-Version Management
Multi-version management is enabled by default (git.enabled defaults to true). The Git object backend can be local (local filesystem) or s3 (S3-compatible object storage); when git.backend is not set explicitly, it inherits storage.agfs.backend (a memory storage backend maps to local). To turn multi-version management off, set git.enabled to false.
Local Backend (recommended for single-node deployments)
{
"storage": {
"workspace": "./data"
},
"git": {
"enabled": true,
"backend": "local",
"default_branch": "main",
"author_name": "viking-bot",
"author_email": "bot@viking.local",
"local": {
"base_dir": ""
}
}
}Configuration reference:
| Field | Default | Description |
|---|---|---|
git.enabled | true | Whether multi-version management is on. Set to false to disable snapshot commands |
git.backend | inherits storage.agfs.backend | Git object backend: local or s3. When not set explicitly, inherits storage.agfs.backend (memory maps to local) |
git.default_branch | main | Default branch name when none is specified |
git.author_name | viking-bot | Default author name when callers omit author_name |
git.author_email | bot@viking.local | Default author email |
git.local.base_dir | "" | Directory holding Git objects/refs. When empty, defaults to {storage.workspace}/.ovgit |
Usually leave
git.local.base_dirempty so snapshot data lands in.ovgitunder the workspace, making it easy to back up and migrate alongside resource data.
S3 Backend (recommended for distributed / cloud deployments)
Stores Git objects and refs in S3-compatible object storage (e.g. Volcengine TOS, MinIO, AWS S3). When backend is s3, the git.s3 section is required, and bucket and region must not be empty.
Tip: the
git.s3fieldsbucket,region,endpoint,access_key, andsecret_keyinherit the matchingstorage.agfs.s3values when not set explicitly. So whenstorage.agfsis already configured as an s3 backend, you usually don't need to repeatgit.s3— as long asgit.backendis not set explicitly, multi-version management reuses the bucket and credentials fromstorage.agfs.
{
"storage": {
"workspace": "./data"
},
"git": {
"enabled": true,
"backend": "s3",
"default_branch": "main",
"author_name": "viking-bot",
"author_email": "bot@viking.local",
"s3": {
"bucket": "your-tos-bucket",
"region": "cn-beijing",
"endpoint": "https://tos-s3-cn-beijing.volces.com",
"access_key": "<your-volcengine-ak>",
"secret_key": "<your-volcengine-sk>",
"prefix": ".ovgit",
"use_path_style": false,
"cas_mode": "native"
}
}
}Configuration reference:
| Field | Default | Description |
|---|---|---|
git.s3.bucket | inherits storage.agfs.s3.bucket | Bucket holding Git objects/refs. Required (may be inherited from storage.agfs.s3) |
git.s3.region | inherits storage.agfs.s3.region, else us-east-1 | Region the bucket is in. Required |
git.s3.prefix | .ovgit | Key prefix; all data is stored under {prefix}/{account}/... |
git.s3.endpoint | inherits storage.agfs.s3.endpoint, else "" | Custom S3 endpoint (MinIO/TOS, etc.); leave empty for standard AWS S3 |
git.s3.access_key / git.s3.secret_key | inherit the matching storage.agfs.s3 fields, else null | Credentials read directly; empty falls back to the SDK default credentials chain |
git.s3.use_path_style | true | true uses path-style addressing (MinIO, etc.); false uses virtual-host style (TOS, etc.) |
git.s3.cas_mode | native | Ref CAS mode. native uses S3 conditional writes (If-Match) |
After editing the config, restart the OpenViking service (or re-initialize the SDK client) for it to take effect.
The repository ships ready-to-use examples: ov.conf.git-local.example and ov.conf.git-s3-tos.example.
Directory Layout Change: the .ovgit Directory
When the local backend is enabled and base_dir is left empty, OpenViking adds a .ovgit directory under the workspace to hold Git objects and refs:
data/ # storage.workspace
├── viking/ # user-visible resource tree (viking:// maps here)
│ └── ...
└── .ovgit/ # multi-version management data (new)
└── {account_id}/ # one logical Git repository per account
├── objects/ # Git objects (commit/tree/blob), standard fanout aa/bb...
├── refs/
│ └── heads/
│ └── main # branch ref, content is a 40-hex OID
└── HEAD # current branch pointer, content "ref: refs/heads/main"Key points:
.ovgitis an internal data directory. It is not exposed throughviking://— users cannot see or modify it through the filesystem APIs (ls/read, etc.).- Its layout matches a standard Git object store (content-addressed
objects/, looserefs/), but it is managed automatically by OpenViking. You should not rungitcommands against it. - When backing up or migrating the workspace, copy
.ovgitalong with it to preserve the full version history. - With the
s3backend, no local.ovgitdirectory is created; data lives under the bucket's{prefix}/{account}/...keys instead.
Usage
Once enabled, all three surfaces expose snapshot commands. The examples below show a minimal "commit → modify → restore" flow.
Python SDK
Snapshot methods live under the client.snapshot.* namespace.
import openviking as ov
client = ov.OpenViking()
client.initialize()
root = "viking://resources/my_project"
# 1. Write initial content and commit v1
client.write(f"{root}/guide.md", "# Guide\n\nv1 content\n", mode="create", wait=True)
v1 = client.snapshot.commit(message="v1 initial import")
print("v1:", v1["commit_oid"])
# 2. Modify and commit v2
client.write(f"{root}/guide.md", "# Guide\n\nv2 content\n", mode="replace", wait=True)
v2 = client.snapshot.commit(message="v2 update")
# 3. Walk history
for c in client.snapshot.log(limit=10):
print(c["oid"][:8], c["message"])
# 4. Inspect a commit's metadata
print(client.snapshot.show(v1["commit_oid"])["message"])
# 5. Restore the workspace to v1 (creates a new "forward" commit on top of v2)
client.snapshot.restore(project_dir=root, source_commit=v1["commit_oid"], message="restore to v1")
client.close()CLI
The CLI subcommands live under ov snapshot:
# Commit the current workspace state
ov snapshot commit -m "v1 initial import" -o json
# Walk history (newest first)
ov snapshot log --limit 10 -o json
# View commit metadata
ov snapshot show <commit_oid> -o json
# Read a file's content from a commit (defaults to stdout; use --out-file to write a local file)
ov snapshot show <commit_oid> --path viking://resources/my_project/guide.md --out-file ./guide.md
# Restore a directory to a past snapshot (positional args are <source_commit> then <project_dir>)
ov snapshot restore <commit_oid> viking://resources/my_project -m "restore to v1" -o json
# Preview which files would change first
ov snapshot restore <commit_oid> viking://resources/my_project --dry-run -o jsonHTTP API
# Commit
curl -X POST "http://localhost:1933/api/v1/snapshot/commit" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{"message": "v1 initial import"}'
# Walk history
curl -X GET "http://localhost:1933/api/v1/snapshot/log?branch=main&limit=10" \
-H "X-API-Key: your-key"
# View commit metadata
curl -X GET "http://localhost:1933/api/v1/snapshot/show?target_ref=<commit_oid>" \
-H "X-API-Key: your-key"
# Restore
curl -X POST "http://localhost:1933/api/v1/snapshot/restore" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{"project_dir": "viking://resources/my_project", "source_commit": "<commit_oid>", "message": "restore to v1"}'Key Semantics: Forward-Commit Restore
restore uses forward-commit semantics: it reads the content at source_commit, writes the diff back into the workspace, and creates a new commit on top of the current HEAD. Therefore:
- The new commit's parent is the HEAD that existed before the restore — not
source_commit. - HEAD always advances monotonically, and history is never rewritten or lost — going back to an older version is itself a new commit.
restoreonly affects files withinproject_dir(the whole account tree when omitted); files outside that scope are left untouched.
Excluding Files with .ovgitignore
The .ovgitignore file at the account root is an account-level exclusion file, analogous to a root .gitignore: files matching its rules are excluded from commit snapshots. It composes with the built-in system pruning (_system, tasks, vector-index derived files, etc.).
Key points:
- The rules file itself is never ignored by
.ovgitignorerules — even if a rule matches.ovgitignoreit is still included in snapshots, so rule changes are auditable and restorable. - Rules affect only
commit;restore,show, andlogtreat commit contents as authoritative and do not apply the current.ovgitignoreas a filter. So restoring a historical snapshot still restores files that match the current rules. - If a file was tracked in an earlier commit and a later rule matches it, the next
commitremoves it from the new snapshot (the workspace file itself is untouched). .ovgitignorenever enters vector indexing/retrieval.
Rule syntax
.ovgitignore is UTF-8 text supporting a common glob subset:
- Blank lines are ignored.
- A line whose first non-space character is
#is a comment. - Leading/trailing whitespace is trimmed.
!negation is unsupported (its presence makescommitfail with an error).- Git-style backslash escaping is unsupported.
- The file is capped at 64 KiB.
Matching uses account-relative Git tree paths (/-separated), e.g. resources/proj/a.log. For example, *.log matches .log files at any depth, build/ matches a directory named build and its contents, and /cache/** matches only cache/ at the account root.
Python SDK
# Write the rules
client.snapshot.set_gitignore(content="*.log\n")
# Read (returns an empty string when absent)
print(client.snapshot.get_gitignore())
# Delete (missing is success, idempotent)
client.snapshot.delete_gitignore()On subsequent commits, files matching the rules are excluded, and the response's ignored field reports how many candidate paths were skipped:
v = client.snapshot.commit(message="with ignore")
print(v["result"], v.get("ignored")) # created, 1CLI
# Set (pass content inline with --content, or read from a file with --file)
ov snapshot ignore-set --content "*.log" -o json
ov snapshot ignore-set --file ./my-rules -o json
# Get (-o json returns {"result": "<content>"}; without -o json it prints the content to stdout)
ov snapshot ignore-get -o json
# Delete (idempotent)
ov snapshot ignore-delete -o jsonHTTP API
# Get
curl -X GET "http://localhost:1933/api/v1/snapshot/ignore" \
-H "X-API-Key: your-key"
# Set
curl -X PUT "http://localhost:1933/api/v1/snapshot/ignore" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{"content": "*.log\n"}'
# Delete
curl -X DELETE "http://localhost:1933/api/v1/snapshot/ignore" \
-H "X-API-Key: your-key"Notes
- After editing the
gitconfig, restart the service / re-initialize the client for it to take effect. - With the
s3backend,git.s3.bucketandgit.s3.regionare required; missing them causes initialization to fail. - If a restore has vector side effects (files written/deleted), the response carries a
task_idyou can poll viaGET /api/v1/tasks/{task_id}to track the background vector rebuild (see the Observability guide and API Overview). - If
.ovgitignoreis too large (over 64 KiB) or contains unsupported syntax (!negation, backslash escaping),commitfails with aninvalid operationerror;set_gitignorevalidates the size up front. - Do not operate on the
.ovgitdirectory with an externalgittool; it is maintained by OpenViking.
Related Documentation
- Snapshots API: full reference of command parameters and responses
- Configuration: full
ov.confreference - Multi-Write Storage Guide: multi-backend replication of resource data
