1. Introduction to OpenViking
1.1 Use Cases
| Problem | How OpenViking Handles It |
|---|
| Fragmented context | Unified management with a filesystem paradigm: memory, resources, and skills all map to a viking:// virtual directory, so context is managed the way files are |
| High token consumption | L0/L1/L2 tiering: the L0 summary (~100 tokens) is used for retrieval, the L1 overview (~2k tokens) for decisions, and the L2 full text is loaded on demand — significant cost savings |
| Poor retrieval quality | Recursive directory retrieval: vector search first locates a high-scoring directory, then refines recursively inside it, combining semantics with hierarchical structure to improve accuracy |
| Opaque retrieval | Each retrieval path maps to an explicit URI, and retrieval traces can be visualized, making debugging and optimization easier |
| Memory is hard to iterate on | Automatic session management: when a conversation ends it is compressed and archived automatically, and an LLM extracts long-term memory into user/memories and agent/memories, getting more accurate the more it is used |
1.2 Core Concepts
Resource (knowledge and documents added by the user), Memory (the user’s/Agent’s memory), and Skill (callable capabilities), all organized with Viking URIs
VikingFS provides the URI abstraction; underneath, AGFS stores the content (L0/L1/L2 files) and the Vector Index stores vectors and metadata. Separating the two makes scaling easier
Supports find() (single-query vector retrieval) and search() (intent analysis → tiered retrieval → Rerank); results are returned by URI and can be distinguished as memories/resources/skills
2. Deploying OpenViking
- Prepare the data directory
1
2
| mkdir -p openviking/data
chmod -R 777 openviking
|
- Generate the ROOT_API_KEY
- Prepare the configuration file
1
2
3
| export OPENAI_API_KEY=sk-xxx
export OPENAI_API_BASE=https://llmapi.xxx.com/v1
export ROOT_API_KEY="your-token"
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| cat > openviking/ov.conf <<EOF
{
"server": {
"host": "0.0.0.0",
"port": 1933,
"root_api_key": "$ROOT_API_KEY",
"cors_origins": ["*"]
},
"storage": {
"workspace": "/app/data",
"vectordb": {
"name": "context",
"backend": "local"
},
"agfs": {
"backend": "local",
"port": 1833,
"log_level": "warn"
}
},
"embedding": {
"max_concurrent": 10,
"dense": {
"provider": "openai",
"api_key": "$OPENAI_API_KEY",
"api_base": "$OPENAI_API_BASE",
"model": "text-embedding-v4",
"dimension": 1024,
"input": "text",
"batch_size": 32
}
},
"vlm": {
"provider": "openai",
"api_key": "$OPENAI_API_KEY",
"api_base": "$OPENAI_API_BASE",
"model": "default",
"temperature": 0.0,
"max_retries": 2,
"max_concurrent": 100
},
"log": {
"level": "INFO",
"output": "stdout"
}
}
EOF
|
The embedding configuration is used for vectorization, and the vlm configuration is used for summarization, L0/L1, memory extraction, and image and content understanding. No rerank model is configured here. In addition, the backend for vectordb and agfs is set to local, meaning the local filesystem is used for storage.
- Run the OpenViking container
1
2
3
4
5
6
7
8
9
| nerdctl run -d \
--name openviking \
--restart always \
--security-opt apparmor=unconfined \
--security-opt seccomp=unconfined \
-p 1933:1933 \
-v $(pwd)/openviking/data:/app/data \
-v $(pwd)/openviking/ov.conf:/app/ov.conf \
ghcr.io/volcengine/openviking:v0.2.9
|
- Remove the OpenViking container
1
| nerdctl rm -f openviking
|
- Verify that the OpenViking service is healthy
1
| curl http://127.0.0.1:1933/health
|
A return of {"status":"ok","healthy":true,"version":"v0.2.9"} means the service is ready.
3. OpenViking User Management
- Set environment variables
1
2
| export OPENVIKING_BASE_URL="http://10.0.0.10:1933"
export ROOT_API_KEY="your-token"
|
1
2
| curl -fsS -H "X-API-Key: $ROOT_API_KEY" \
"${OPENVIKING_BASE_URL%/}/api/v1/admin/accounts"
|
1
| {"status":"ok","result":[{"account_id":"default","created_at":"2026-03-23T09:58:40.472608+00:00","user_count":0}],"error":null,"telemetry":null}
|
- Create an account and admin user
1
2
3
4
| curl -fsS -X POST "${OPENVIKING_BASE_URL%/}/api/v1/admin/accounts" \
-H "Content-Type: application/json" \
-H "X-API-Key: $ROOT_API_KEY" \
-d '{"account_id":"my-team","admin_user_id":"my-admin"}'
|
1
| {"status":"ok","result":{"account_id":"my-team","admin_user_id":"my-admin","user_key":"49331c3c1cafb6e5670f316d47aabfaae6c628b6ae72a0d05f35b7c93da763a2"},"error":null,"telemetry":null}
|
You need to provide the admin user key under the account
1
2
3
4
5
| export ADMIN_USER_KEY="49331c3c1cafb6e5670f316d47aabfaae6c628b6ae72a0d05f35b7c93da763a2"
curl -fsS -X POST "${OPENVIKING_BASE_URL%/}/api/v1/admin/accounts/my-team/users" \
-H "Content-Type: application/json" \
-H "X-API-Key: $ADMIN_USER_KEY" \
-d '{"user_id":"my-user","role":"user"}'
|
1
| {"status":"ok","result":{"account_id":"my-team","user_id":"my-user","user_key":"ad133ab241f779f19e2efe0c69655bd97dd67ac3a26a58d36a4a30c346e4b53f"},"error":null,"telemetry":null}
|
- List the users under an account
1
2
| curl -fsS -H "X-API-Key: $ADMIN_USER_KEY" \
"${OPENVIKING_BASE_URL%/}/api/v1/admin/accounts/my-team/users"
|
1
| {"status":"ok","result":[{"user_id":"my-admin","role":"admin"},{"user_id":"my-user","role":"user"}],"error":null,"telemetry":null}
|
1
2
3
4
| curl -fsS -X PUT "${OPENVIKING_BASE_URL%/}/api/v1/admin/accounts/my-team/users/my-user/role" \
-H "Content-Type: application/json" \
-H "X-API-Key: $ROOT_API_KEY" \
-d '{"role":"admin"}'
|
1
| {"status":"ok","result":{"account_id":"my-team","user_id":"my-user","role":"admin"},"error":null,"telemetry":null}
|
- Regenerate the user key (the old key is invalidated immediately; use the Root or Admin User Key)
1
2
3
4
| curl -fsS -X POST "${OPENVIKING_BASE_URL%/}/api/v1/admin/accounts/my-team/users/my-user/key" \
-H "Content-Type: application/json" \
-H "X-API-Key: $ADMIN_USER_KEY" \
-d '{}'
|
1
| {"status":"ok","result":{"user_key":"97b790bed56e40d288d3c60ee09abb41d821fb039fb2a53f92fe4fbd629ea8cd"},"error":null,"telemetry":null}
|
1
2
| curl -fsS -X DELETE "${OPENVIKING_BASE_URL%/}/api/v1/admin/accounts/my-team/users/my-user" \
-H "X-API-Key: $ADMIN_USER_KEY"
|
1
2
| curl -fsS -X DELETE "${OPENVIKING_BASE_URL%/}/api/v1/admin/accounts/my-team" \
-H "X-API-Key: $ROOT_API_KEY"
|
4. Using OpenViking Features
- Set environment variables
1
2
3
| export OPENVIKING_BASE_URL="http://10.0.0.10:1933"
export USER_ID="my-user"
export USER_KEY="97b790bed56e40d288d3c60ee09abb41d821fb039fb2a53f92fe4fbd629ea8cd"
|
1
2
3
4
| curl -fsS -X POST "$OPENVIKING_BASE_URL/api/v1/fs/mkdir" \
-H "Content-Type: application/json" \
-H "X-API-Key: $USER_KEY" \
-d '{"uri":"viking://resources/lifecycle-demo/"}'
|
1
| {"status":"ok","result":{"uri":"viking://resources/lifecycle-demo/"},"error":null,"telemetry":null}
|
- Upload to a temporary directory
1
2
3
4
5
| export FILE_PATH="/tmp/ov-lifecycle.md"
echo 'Hello OpenViking lifecycle' > $FILE_PATH
curl -fsS -X POST "$OPENVIKING_BASE_URL/api/v1/resources/temp_upload" \
-H "X-API-Key: $USER_KEY" \
-F "file=@$FILE_PATH"
|
1
| {"status":"ok","result":{"temp_path":"/app/data/temp/upload/upload_3c5f30d789b9439e91f16da9b22555cc.md"}}
|
- Ingest into the directory created in the previous step
1
2
3
4
5
6
7
8
9
10
| export TEMP_PATH="/app/data/temp/upload/upload_3c5f30d789b9439e91f16da9b22555cc.md"
curl -fsS -X POST "$OPENVIKING_BASE_URL/api/v1/resources" \
-H "Content-Type: application/json" \
-H "X-API-Key: $USER_KEY" \
-d "{
\"temp_path\": \"$TEMP_PATH\",
\"parent\": \"viking://resources/lifecycle-demo/\",
\"wait\": false,
\"reason\": \"curl lifecycle\"
}"
|
1
| {"status":"ok","result":{"status":"success","errors":[],"source_path":"/app/data/temp/upload/upload_3c5f30d789b9439e91f16da9b22555cc.md","meta":{},"root_uri":"viking://resources/lifecycle-demo/upload_3c5f30d789b9439e91f16da9b22555cc","temp_uri":"viking://resources/lifecycle-demo/upload_3c5f30d789b9439e91f16da9b22555cc"}}
|
1
2
3
4
5
| curl -fsS -G \
-H "X-API-Key: $USER_KEY" \
--data-urlencode "uri=viking://resources/lifecycle-demo/" \
--data-urlencode "recursive=true" \
"$OPENVIKING_BASE_URL/api/v1/fs/ls"
|
1
| {"status":"ok","result":[{"uri":"viking://resources/lifecycle-demo/upload_3c5f30d789b9439e91f16da9b22555cc","size":88,"isDir":true,"modTime":"09:02:48","rel_path":"upload_3c5f30d789b9439e91f16da9b22555cc","abstract":""},{"uri":"viking://resources/lifecycle-demo/upload_3c5f30d789b9439e91f16da9b22555cc/upload_3c5f30d789b9439e91f16da9b22555cc.md","size":27,"isDir":false,"modTime":"09:02:48","rel_path":"upload_3c5f30d789b9439e91f16da9b22555cc/upload_3c5f30d789b9439e91f16da9b22555cc.md","abstract":""}],"error":null,"telemetry":null}
|
1
2
3
4
5
| export FILE_URI="viking://resources/lifecycle-demo/upload_3c5f30d789b9439e91f16da9b22555cc/upload_3c5f30d789b9439e91f16da9b22555cc.md"
curl -fsS -G \
-H "X-API-Key: $USER_KEY" \
--data-urlencode "uri=$FILE_URI" \
"$OPENVIKING_BASE_URL/api/v1/content/read"
|
1
| {"status":"ok","result":"Hello OpenViking lifecycle\n","error":null,"telemetry":null}
|
1
2
3
4
5
| export FILE_URI="viking://resources/lifecycle-demo/upload_3c5f30d789b9439e91f16da9b22555cc/upload_3c5f30d789b9439e91f16da9b22555cc.md"
curl -fsS -G \
-H "X-API-Key: $USER_KEY" \
--data-urlencode "uri=$FILE_URI" \
"$OPENVIKING_BASE_URL/api/v1/fs/stat"
|
1
| {"status":"ok","result":{"isDir":false,"modTime":"2026-03-24T09:02:48.710880298Z","mode":420,"name":"upload_3c5f30d789b9439e91f16da9b22555cc.md","size":27},"error":null,"telemetry":null}
|
A rerank model can be configured.
1
2
3
4
5
6
7
8
| curl -fsS -X POST "$OPENVIKING_BASE_URL/api/v1/search/find" \
-H "Content-Type: application/json" \
-H "X-API-Key: $USER_KEY" \
-d '{
"query": "Hello",
"target_uri": "viking://resources/lifecycle-demo/",
"limit": 5
}'
|
The response will be relatively slow, since a model is called.
1
| {"status":"ok","result":{"memories":[],"resources":[{"context_type":"resource","uri":"viking://resources/lifecycle-demo/upload_a67d1858a30f4c4d91df60e7a489448c/upload_a67d1858a30f4c4d91df60e7a489448c_1.md","level":2,"score":0.09988392326968897,"category":"","match_reason":"","relations":[],"abstract":"","overview":null}],"skills":[],"total":2}}
|
1
2
3
4
| curl -fsS -X POST "$OPENVIKING_BASE_URL/api/v1/search/grep" \
-H "Content-Type: application/json" \
-H "X-API-Key: $USER_KEY" \
-d '{"uri":"viking://resources/lifecycle-demo/","pattern":"Hello"}'
|
1
| {"status":"ok","result":{"matches":[{"line":24,"uri":"viking://resources/lifecycle-demo/upload_3c5f30d789b9439e91f16da9b22555cc/.overview.md","content":""},{"line":1,"uri":"viking://resources/lifecycle-demo/upload_3c5f30d789b9439e91f16da9b22555cc/upload_3c5f30d789b9439e91f16da9b22555cc.md","content":"Hello OpenViking lifecycle"},{"line":1,"uri":"viking://resources/lifecycle-demo/upload_d318b2c6f0494e88af77f9d1afbc18de/upload_d318b2c6f0494e88af77f9d1afbc18de.md","content":"Hello OpenViking lifecycle"}],"count":3},"error":null,"telemetry":null}
|
- search: semantic and intent retrieval, optionally with a session.
1
2
3
4
| curl -fsS -X POST "$OPENVIKING_BASE_URL/api/v1/sessions" \
-H "Content-Type: application/json" \
-H "X-API-Key: $USER_KEY" \
-d '{}'
|
1
| {"status":"ok","result":{"session_id":"d46e0160-1ebc-483d-971f-32d60bb1de9a","user":{"account_id":"my-team","user_id":"my-user","agent_id":"default"}},"error":null,"telemetry":null}
|
1
2
3
4
5
6
7
8
9
| export SESSION_ID="d46e0160-1ebc-483d-971f-32d60bb1de9a"
curl -fsS -X POST "$OPENVIKING_BASE_URL/api/v1/search/search" \
-H "Content-Type: application/json" \
-H "X-API-Key: $USER_KEY" \
-d '{
"query": "Hello",
"session_id": "$SESSION_ID",
"limit": 5
}'
|
A model is called here as well.
1
2
3
4
| curl -fsS -X DELETE \
-H "X-API-Key: $USER_KEY" \
--get "$OPENVIKING_BASE_URL/api/v1/fs" \
--data-urlencode "uri=$FILE_URI"
|
1
| {"status":"ok","result":{"uri":"viking://resources/lifecycle-demo/upload_3c5f30d789b9439e91f16da9b22555cc/upload_3c5f30d789b9439e91f16da9b22555cc.md"},"error":null,"telemetry":null}
|
1
2
3
| curl -fsS -X DELETE \
-H "X-API-Key: $USER_KEY" \
"$OPENVIKING_BASE_URL/api/v1/fs?uri=viking://resources/lifecycle-demo/&recursive=true"
|
1
| {"status":"ok","result":{"uri":"viking://resources/lifecycle-demo/"},"error":null,"telemetry":null}
|
1
2
3
4
| curl -fsS -G \
-H "X-API-Key: $USER_KEY" \
--data-urlencode "uri=viking://resources/" \
"$OPENVIKING_BASE_URL/api/v1/fs/ls"
|
1
| {"status":"ok","result":[],"error":null,"telemetry":null}
|
5. Integrating OpenClaw with OpenViking
OpenClaw: 2026.3.11 is recommended; higher versions may have compatibility issues
- Enter the OpenClaw container
1
| nerdctl exec -it openclaw bash
|
- Disable the default memory backend
1
2
3
4
| openclaw config set agents.defaults.memorySearch.enabled false --json
openclaw config set memory.qmd.includeDefaultMemory false --json
openclaw config set hooks.internal.entries.session-memory.enabled false --json
openclaw config set agents.defaults.compaction.memoryFlush.enabled false --json
|
1
| npm install -g openclaw-openviking-setup-helper
|
Choose remote, and fill in the OpenViking service address and USER_KEY.
1
| openclaw config set plugins.slots.memory openviking
|
1
| openclaw gateway restart
|
- View the plugin configuration
1
| openclaw config get plugins.entries.openviking.config
|
1
2
3
4
5
6
7
8
| 🦞 OpenClaw 2026.3.11 (unknown) — I've survived more breaking changes than your last three relationships.
{
"mode": "remote",
"baseUrl": "http://10.0.0.10:1933",
"apiKey": "__OPENCLAW_REDACTED__",
"agentId": "openclaw-viking"
}
|
1
| node openclaw.mjs status |grep Memory
|
1
| │ Memory │ enabled (plugin openviking) │
|
- Verify that the integration succeeded
Try a conversation with OpenClaw; you will see logs of OpenViking creating the relevant memory.
1
| nerdctl logs openviking -f
|
1
| 2026-03-24 10:20:59,392 - openviking.session.memory_extractor - INFO - uri viking://user/my-user/memories/events/mem_372fc569-f140-4a31-a266-6a795bf49af0.md abstract: 正在学习参考 LVM 日常运维文章作为速查手册,将其作为运维工作的速查手册使用。文章内容涵盖物理卷(PV)、卷组(VG)、逻辑卷(LV)的管理命令,以及 RAID 逻辑卷配置、扩容缩容操作等实用内容。
|
6. References