* feat(compile): add memory consolidation mode via `--skill memory` Add an in-process memory consolidation mode to `ov compile`. When `--skill` is the sentinel value `memory`, CompileService runs the existing memory framework (ConsolidationExtractContextProvider -> ExtractLoop -> MemoryUpdater) directly inside OpenViking core to dedup/merge/split/compact an existing memory-type directory in place, conforming to that type's schema. The default skill path (VikingBot Wiki compile) is unchanged. Highlights: - New ConsolidationExtractContextProvider: agentic exploration with ls/search/read tools seeded by a recursive listing; single schema inferred from --to; space (self/peer) taken from the canonical --to URI so listing never depends on an empty ctx.user_id. - MemoryCompileRunner: session.commit-lite task shape (task_tracker + one in-process asyncio.Task, no QueueFS re-delivery), bound to a root span so a trace_id is recorded; result reports adds/updates/deletes (file URIs only, memory_diff.json semantics) classified via read_file_contents. - MemoryLsTool: add recursive listing (relative paths, 500-node cap with truncation) and stop hiding subdirectories so subfoldered dirs are not misreported as empty. Only the compile provider exposes ls, so session.commit is unaffected. - CLI: --from optional (required for normal mode, rejected for memory mode); help gains a memory example. - Fix a syntax regression in crates/ragfs/src/lock/provider.rs test module that blocked `make build` (unrelated to compile; introduced by #4908). - Docs: document the memory mode in ov-compile-design.md. - Tests: unit tests for provider/runner/request validation; integration script test_compile_memory_xiaomei.py with merge/split/dedup/preferences cases. Co-authored-by: TRAE CLI <traecli@bytedance.com> * docs(compile): document memory consolidation mode Add a dedicated user-facing page (zh + en) for `ov compile --skill memory` covering when to use it, usage, parameters, behavior, and the adds/updates/ deletes result. Link it from the context-compilation overview. The VitePress sidebar picks the new page up automatically from the directory listing. Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(compile): honor language and cancellation Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: migrate files when URI fields change Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: materialize URI moves at apply time Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: inherit source links on explicit merge Co-authored-by: TRAE CLI <traecli@bytedance.com> * test(memory): cover rename conflict and streaming migration Co-authored-by: TRAE CLI <traecli@bytedance.com> * test(compile): assert URI migration diff semantics Co-authored-by: TRAE CLI <traecli@bytedance.com> * docs(compile): clarify cross-type link migration Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: fail rename on target read errors Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: preserve omitted URI identity fields Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: rebase URI moves on latest source Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: protect occupied empty rename targets Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: reject case-only URI moves Co-authored-by: TRAE CLI <traecli@bytedance.com> * docs(compile): document URI migration semantics Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: validate explicit replacement targets Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: reject case-only replacement moves Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: rerender managed links after URI moves Co-authored-by: TRAE CLI <traecli@bytedance.com> * test(compile): add repeatable Chinese URI rename case Co-authored-by: TRAE CLI <traecli@bytedance.com> * test(compile): cover bidirectional URI renames Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: remove empty directories after URI moves Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory(prompts): lowercase filename identity segments Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: consolidate memories-root in one extract loop Co-authored-by: TRAE CLI <traecli@bytedance.com> * docs(compile): document memory-root consolidation and failure semantics Co-authored-by: TRAE CLI <traecli@bytedance.com> * test(compile): add memory_root and deterministic rename cases Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory(compile): honor Account templates and preserve merged duplicate links - MemoryCompileRunner._consolidate now resolves the Account-level memory template snapshot via resolve_account_memory_registry(), matching the session.commit extract path, so consolidation no longer overwrites operator-customized content templates with deployment defaults. - _inherit_deleted_link_relations now tracks the deleted source URI for each inherited link. Implicit rename targets only exclude contributions copied from their own migration source, so links unique to a duplicate merged into the same target (delete_replacements) are folded in and the neighbor backlinks match. - Regression tests cover the Account-template snapshot and a same-batch rename+merge where only the duplicate holds a link to a third file. Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: preserve rename sources through session commit queue - ExtractLoop._updated_uri_for_existing_operation only considers an identity-field change a rename when the merged value actually differs from the current one, and returns the source URI unchanged when the regenerated candidate matches it. Legacy paths whose new template differs only in case (e.g. preferences user "Alice") no longer trip the case-only migration guard on plain content updates. - clone_operation_for_uri no longer drops old_memory_file_content when the target URI differs from the source. The upstream _materialize_uri_migrations still detects the mismatch and generates a write-new + delete-old migration, but the queue clone keeps the source content so the migration can inherit prior body and links instead of turning a rename into an empty new record. - python_protocol contract preamble drops the misleading "Unknown business fields are ignored" clause; unknown fields raise at parse time, so the note was inaccurate. - Regression tests cover legacy case-only preferences updates going through ExtractLoop and split_request_by_merge_group preserving the rename source for the add + delete pair. Co-authored-by: TRAE CLI <traecli@bytedance.com> * vikingbot(cli): pin openviking log stream to stderr before eager imports vikingbot chat --eval / -e commits to a stdout JSON contract. Downstream consumers like benchmark/locomo/vikingbot/run_eval.py parse stdout with json.loads, so any log line on stdout breaks the parse and drops token_usage and iteration to defaults (0). That is what caused Total prompt tokens=0 and Avg iteration=0 in the LoCoMo summary. Move the openviking / openviking_cli log redirection into a module-level _preimport_redirect_openviking_logs_to_stderr() that runs before any vikingbot.agent.* or openviking.* imports. Those imports call get_logger() at module load time, which loads ov.conf and can emit warnings (e.g. "Ignoring unknown config field") through openviking_cli's shared QueueListener whose default output is stdout. Force the "stdout" listener plus real StreamHandler pair into existence up front and rebind its stream to stderr. Also move the in-chat() redirect ahead of ensure_config and extend it to swap the shared stdout handler as a second-line guard. Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory(python-protocol): silently drop unknown-field edits Extraction DSL programs occasionally reference a field name that does not exist in the memory schema. Failing the whole program on this is brittle: unrelated valid statements in the same commit are lost. Match the tolerance kwargs already have on create()/set()/update() and treat unknown-field attribute access as a compile-time no-op: return a _FieldHandle flagged is_noop=True, and skip any .update()/.edit()/.drop() chained on it plus the final _apply_field_handle. Sibling operations on real fields keep applying. Update the corresponding regression tests: replace the literal-`field` placeholder rejection test with two new cases asserting the whole program still commits and a real content.edit() still lands when a bogus field appears in the same batch. Co-authored-by: TRAE CLI <traecli@bytedance.com> * memory: tree-lock delete parents so overview cleanup covers empty dir rm `generate_overview` recursively removes a memory directory once the last file is deleted, but `_operation_tree_lock_paths` only tree-locked rename source parents. Batches that plain-deleted the last file in a directory ran the follow-up `rm -r` under a lease that did not cover the parent, and RAGFS rejected the request with "pathlock lease ref does not cover the requested operation". Add each delete's parent directory (unless a same-directory rename replaces it) to the tree-lock set. Co-authored-by: TRAE CLI <traecli@bytedance.com> --------- Co-authored-by: TRAE CLI <traecli@bytedance.com>
VikingBot
VikingBot is the multi-channel AI agent built into OpenViking. You can use it directly from the command line or run it as a long-lived Gateway connected to Feishu, Slack, Telegram, and other platforms. When connected to OpenViking, it also gains resource retrieval, user memory, experience memory, and session consolidation.
Key Capabilities
- Multiple chat entry points:
vikingbot chat,ov chat, HTTP APIs, and multiple chat platforms. - Agent tools: built-in file, shell, web, image generation, scheduled task, and OpenViking tools.
- Skills and subagents: load Skills on demand and delegate independent work to background subagents.
- Long-term context: recall Resources, Peer Memories, and Experiences from OpenViking, and commit sessions automatically.
- Safer execution: Direct, SRT, OpenSandbox, and AIO Sandbox backends.
- Service deployment: the Gateway provides synchronous chat, SSE streaming, feedback, and an OpenViking API proxy.
Installation
OpenViking Server requirement: VikingBot addresses the caller's own context space through the
viking://~home alias (for exampleviking://~/memories/), so it requires a server withviking://~support. The uid-less shorthandviking://user/memoriesis no longer emitted and is rejected by newer servers.
Install from PyPI
pip install "openviking[bot]"
Install from Source
Python 3.11 or later is required. We recommend using uv:
git clone https://github.com/volcengine/OpenViking.git
cd OpenViking
uv venv --python 3.11
source .venv/bin/activate
uv pip install -e ".[bot]"
On Windows, activate the virtual environment with:
.venv\Scripts\activate
Quick Start: Choose Your Scenario
VikingBot supports three primary usage scenarios. They are different entry points for different needs rather than mutually exclusive modes.
| Scenario | Best for | Start command | OpenViking |
|---|---|---|---|
| A. OpenViking + Bot together | A complete local experience with resources, memory, and the Agent | openviking-server --with-bot |
The Bot uses the OpenViking Server started by this command |
| B. Debug the Agent locally | Quickly testing the Bot or developing Tools and Skills | vikingbot chat |
Optional; without it, the Bot cannot use OpenViking features |
| C. Unified Gateway entry point | Starting the Bot separately and connecting it to an existing OpenViking Server | vikingbot gateway |
May be configured explicitly or omitted |
Scenario A: Start OpenViking and the Bot Together
Use this for the complete local experience. OpenViking Server and VikingBot Gateway start together. ov chat first calls OpenViking Server, whose /bot/v1 route forwards the request to VikingBot.
ov chat → OpenViking Server → VikingBot Gateway → Agent
1. Prepare the configuration
Follow the OpenViking quickstart to configure the models and storage required by OpenViking. By default, the Bot inherits the root-level vlm configuration as its Agent model. Configure bot.agents only if the Bot should use a separate model.
In this combined mode, the Bot always uses the OpenViking Server started by the same command. bot.ov_server.server_url is ignored, while an explicit bot.ov_server.api_key and other Bot OpenViking settings are preserved. In api_key mode, that key must be a User/Admin key. OpenViking Server injects an authenticated request-scoped identity into every Chat request sent to the Bot.
2. Start both services
openviking-server --with-bot
This command starts the current OpenViking Server and a managed VikingBot Gateway. The Bot uses this Server and does not connect to the service named by bot.ov_server.server_url.
3. Configure and use the ov CLI
Run the interactive configuration manager:
ov config
Point the active CLI configuration to OpenViking Server, for example http://127.0.0.1:1933. If the Server requires authentication, also enter the caller's User/Admin API Key. Then run:
ov chat
ov chat -m "Remember that I prefer concise answers"
ov find "my response preferences"
The identity flow is:
ovcli.conf.api_keyrepresents the current caller.- OpenViking Server validates the identity and passes a request-scoped connection to the Bot.
- The request identity takes priority over any process-level default identity, preventing multiple callers from sharing one Bot user.
Scenario B: Debug the Agent Locally
Use this to try VikingBot quickly or develop Agents, Tools, and Skills. vikingbot chat starts the Agent in the current process. It does not require a running Gateway and does not use ovcli.conf as the Bot configuration.
1. Configure a model
Edit ~/.openviking/ov.conf:
{
"bot": {
"agents": {
"provider": "openai",
"model": "gpt-4o-mini",
"api_key": "<your-model-api-key>",
"max_tokens": 8192
}
}
}
Alternatively, configure only the root-level vlm section. VikingBot inherits its model, provider, API key, API base, timeout, and output-token settings. bot.agents.max_tokens is optional; when omitted, VikingBot lets the model provider choose the output limit. A max_tokens value on an individual credential overrides the Agent-level value.
2. Start chatting
# Send one message
vikingbot chat -m "Summarize the structure of the current project"
# Start an interactive multi-turn conversation
vikingbot chat
# Use a specific session
vikingbot chat --session my-session
If no OpenViking Server is available, VikingBot runs in standalone mode. File, shell, web, and Skill capabilities remain available, but OpenViking memory and file tools are disabled.
To connect local debugging to OpenViking, configure server in the same ov.conf, or set bot.ov_server.server_url explicitly. See Connect to OpenViking.
Scenario C: Use the Gateway as the Unified Entry Point
Use this for long-running deployments, remote access, and multiple chat channels. ovcli.conf.url can point directly to VikingBot Gateway:
ov chat → Gateway /bot/v1/chat
ov ls/find/session/... → Gateway /api/v1/* → OpenViking Server
The Gateway has three OpenViking connection states:
| State | Condition | Behavior |
|---|---|---|
| Explicit | bot.ov_server.server_url is configured |
Connects to the specified OpenViking service; startup fails if it is unreachable |
| Inherited | No explicit URL, but the same ov.conf contains server |
Connects to that OpenViking service; falls back to standalone if it is unreachable |
| Standalone | No OpenViking service is available | Chat works; OpenViking tools are disabled and /api/v1/* returns 503 |
1. Configure the Gateway and OpenViking
The following example connects explicitly to a remote OpenViking service:
{
"bot": {
"agents": {
"provider": "openai",
"model": "gpt-4o-mini",
"api_key": "<your-model-api-key>"
},
"gateway": {
"host": "127.0.0.1",
"port": 18790
},
"ov_server": {
"server_url": "https://openviking.example.com",
"api_key": "<bot-openviking-user-api-key>"
}
}
}
If the remote OpenViking service uses trusted mode, set api_key_type to "root" and provide the Root Key in api_key.
2. Start the Gateway
vikingbot gateway
The startup log reports the effective state, such as openviking_explicit, openviking_inherited, or standalone_local.
3. Point the ov CLI to the Gateway
Use ov config, or edit ~/.openviking/ovcli.conf:
{
"url": "http://127.0.0.1:18790",
"api_key": "<caller-openviking-user-or-admin-api-key>",
"actor_peer_id": "cli"
}
Chat and other OpenViking commands now use the same entry point:
ov chat -m "Search the project resources and give me a conclusion"
ov ls viking://resources/
ov find "project release process"
4. Configure a Gateway Token for public listeners
By default, the Gateway listens only on 127.0.0.1. If you change the host to 0.0.0.0 or another non-localhost address, you must configure a token or the Gateway will refuse to start:
{
"bot": {
"gateway": {
"host": "0.0.0.0",
"port": 18790,
"token": "<strong-random-token>"
}
}
}
Add the token to ovcli.conf on the client:
{
"url": "https://bot.example.com",
"api_key": "<caller-openviking-user-or-admin-api-key>",
"gateway_token": "<strong-random-token>",
"actor_peer_id": "cli"
}
The Gateway Token protects only the Gateway entry point. The OpenViking API Key represents the caller identity. They cannot replace one another, and the Gateway Token is never forwarded to OpenViking.
Connect Chat Platforms
To use Feishu, Slack, Telegram, Discord, WhatsApp, DingTalk, QQ, Email, or MoChat, configure bot.channels on top of Scenario C and start the Gateway.
For example, to configure Feishu:
{
"bot": {
"channels": [
{
"type": "feishu",
"enabled": true,
"app_id": "<feishu-app-id>",
"app_secret": "<feishu-app-secret>",
"allow_from": [],
"ov_tools_enable": true
}
]
}
}
vikingbot gateway
vikingbot channels status
You can configure multiple instances of the same channel type. VikingBot uses type + channel_id + chat_id to isolate sessions and route replies. See Channel Configuration for credentials, event subscriptions, and permissions for each platform.
Connect to OpenViking
VikingBot and OpenViking share ~/.openviking/ov.conf. Connections are resolved as follows:
- A managed Bot started by
openviking-server --with-botuses the current Server. - A normal
vikingbot gateway/chatprocess first uses an explicitbot.ov_server.server_url. - Without an explicit URL, it derives the address from
ov.conf.serverin the same file. - Without an available address, it runs in standalone mode.
Authentication requirements:
OpenViking auth_mode |
Bot credential | Gateway request |
|---|---|---|
dev |
Local use | The Gateway must listen on localhost |
api_key |
bot.ov_server.api_key must be a User/Admin Key |
The Chat caller must also provide a valid User/Admin Key; Root Keys cannot access data APIs |
trusted |
Explicit connections use a Root Key; inherited connections may read server.root_api_key |
Non-local entry points must also pass the Gateway Token first |
The Gateway validates the upstream service and Bot credential at startup, then checks the current OpenViking authentication mode on every request. If the mode changes at runtime, it fails closed and asks you to fix the configuration or restart the Gateway.
VikingBot uses OpenViking to:
- read the current Peer Profile;
- recall events, entities, and preferences by type;
- retrieve Agent Experiences;
- browse, search, and read Resources;
- incrementally synchronize and commit Sessions to extract long-term memories and experiences.
See VikingBot and OpenViking Integration for the complete call flow. The Gateway entry points and authentication boundaries follow RFC #3042.
Configuration
The default configuration file is ~/.openviking/ov.conf. Use an environment variable to select another file:
export OPENVIKING_CONFIG_FILE=/path/to/ov.conf
Restart vikingbot gateway after changing the configuration.
Common Settings
| Setting | Default | Description |
|---|---|---|
bot.agents.temperature |
0.7 |
Model sampling temperature |
bot.agents.thinking |
true |
Enable reasoning/thinking when supported by the Provider |
bot.agents.timeout |
Inherits vlm.timeout |
Timeout for one model request |
bot.agents.max_tool_iterations |
50 |
Maximum tool iterations in one turn |
bot.agents.memory_window |
50 |
Local history window and session commit message threshold |
bot.agents.subagent_enabled |
true |
Whether to expose the spawn tool |
bot.agents.subagent_max_concurrency |
4 |
Maximum number of background subagents running at once |
bot.gateway.host |
127.0.0.1 |
Gateway listen address |
bot.gateway.port |
18790 |
Gateway listen port |
bot.sandbox.backend |
direct |
Execution backend |
bot.sandbox.mode |
shared |
Workspace isolation mode |
bot.sandbox.backends.direct.allow_compile_exec |
true |
Set to false to disable |
bot.heartbeat.enabled |
true |
Whether to check HEARTBEAT.md periodically |
bot.heartbeat.interval_seconds |
600 |
Heartbeat interval |
bot.mode |
normal |
One of normal, readonly, or debug |
OpenViking Recall Settings
| Setting | Default | Description |
|---|---|---|
bot.ov_server.memory_recall_events_limit |
10 |
Event memories recalled per turn |
bot.ov_server.memory_recall_entities_limit |
10 |
Entity memories recalled per turn |
bot.ov_server.memory_recall_preferences_limit |
3 |
Preference memories recalled per turn |
bot.ov_server.memory_recall_max_chars |
4000 |
Character budget for injected Peer Memories |
bot.ov_server.exp_recall_limit |
5 |
Number of Experiences recalled |
bot.ov_server.exp_recall_max_chars |
10000 |
Character budget for injected Experiences |
bot.ov_server.exp_write_tools |
write_file,edit_file |
Tools that trigger experience recall before writes |
Workspace and Agent Customization
The Workspace is VikingBot's local working directory. It contains Agent bootstrap instructions, Skills, Heartbeat tasks, and files used by file and Shell tools. The OpenViking workspace is accessed through openviking_* tools for Resources, Memories, and Skills; it is not the same local directory.
Find the Active Workspace
The Workspace root is derived from storage.workspace:
<storage.workspace>/bot/workspace
When storage.workspace is omitted, the default is ~/.openviking/data/bot/workspace. Check the resolved path with:
vikingbot status
With managed OpenSandbox (backend=opensandbox, managed=true), the active workspace root is
<storage.workspace>/bot/runtime/opensandbox/workspaces, bind-mounted into containers as described below.
The active directory used by the Agent also depends on bot.sandbox.mode:
| Mode | Active Workspace |
|---|---|
shared (default) |
<workspace>/shared |
per-session |
<workspace>/<session-key> |
per-channel |
<workspace>/<channel-key> |
For example, with the default configuration, edit ~/.openviking/data/bot/workspace/shared/SOUL.md.
Customize the Agent
When an active Workspace is first used, VikingBot copies initial files from the built-in bot/workspace template. The main customization points are:
| File or directory | Purpose | How it is loaded |
|---|---|---|
SOUL.md |
Personality, values, and communication style | Added to the system prompt on every turn |
AGENTS.md |
Global working rules and task constraints; create it when needed | Added to the system prompt on every turn |
IDENTITY.md |
Agent name, role, and identity background; create it when needed | Added to the system prompt on every turn |
TOOLS.md |
Tool selection, execution boundaries, and safety rules | Added to the system prompt on every turn |
skills/<name>/SKILL.md |
Workflows and supporting resources for a class of tasks | A summary is injected first; full instructions are loaded progressively |
HEARTBEAT.md |
Tasks checked periodically | Read only by Heartbeat |
For example, edit SOUL.md in the active Workspace:
# Soul
You are the team's engineering assistant.
- Lead with the conclusion, then add only necessary detail
- Inspect the current state before changing code
- Run relevant verification after making a change
- State assumptions clearly and never invent results
Saved changes normally take effect on the next Agent turn without restarting the Gateway. SOUL.md changes prompt behavior only; it cannot bypass Channel permissions, tool visibility, or Sandbox restrictions.
Note
Edit files in the active Workspace.
bot/workspacein the repository or installed package is an initialization template and does not overwrite an existing Workspace. Never store API Keys or other secrets in bootstrap files.
For the complete loading order, file responsibilities, and customization boundaries, see Agent Capabilities.
Agent Tools
Built-in Tools
| Category | Tools |
|---|---|
| Files and commands | read_file, write_file, edit_file, list_dir, exec |
| Web | web_search, web_fetch |
| OpenViking | openviking_list, openviking_search, openviking_grep, openviking_glob, openviking_multi_read, openviking_add_resource, openviking_memory_commit |
| Other | message, generate_image, cron, spawn |
readonly mode does not register openviking_add_resource. When a channel sets ov_tools_enable: false, it does not expose OpenViking tools or inject Profiles, Memories, and Experiences.
Scheduled Task Configuration
Scheduled tasks are disabled by default. Set bot.tools.cron.enabled to true in ov.conf to enable them:
{
"bot": {
"tools": {
"cron": {
"enabled": true
}
}
}
}
This switch controls both cron tool registration and the scheduler in Gateway and local Chat modes. When set to false or omitted, the tool is not registered and the scheduler does not start. Existing jobs remain on disk but do not run automatically.
Restart the Bot after changing this setting. Existing deployments must explicitly set enabled: true after upgrading to continue running scheduled jobs automatically. Subagents and --eval mode still do not provide scheduled task capabilities.
The vikingbot cron commands remain available for manual job management; this switch does not restrict CLI management operations.
MCP Tools
Configure third-party MCP Servers under bot.tools.mcp_servers:
{
"bot": {
"tools": {
"mcp_servers": {
"filesystem": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
"tool_timeout": 30,
"enabled_tools": ["*"]
},
"remote": {
"type": "streamableHttp",
"url": "https://example.com/mcp",
"headers": {"Authorization": "Bearer $MCP_TOKEN"},
"enabled_tools": ["search"]
}
}
}
}
}
Supported transports are stdio, sse, and streamableHttp. Tool names use the form mcp_<server>_<tool>. A failed MCP connection does not block other Agent capabilities.
Sandbox
| Backend | Description |
|---|---|
direct |
Default; executes directly on the Bot host and is not a strong isolation boundary |
srt |
Supports file and network allow/deny policies |
opensandbox |
Connects to OpenSandbox Server |
aiosandbox |
Connects to an AIO Sandbox service |
Workspace modes:
shared: all sessions share one workspace;per-session: every Session has an independent workspace;per-channel: sessions on the same channel instance share a workspace.
DirectBackend defaults to restrict_to_workspace: false. For a Gateway exposed to untrusted users, choose an isolated backend and configure channel allowlists and network/file policies.
{
"bot": {
"sandbox": {
"backend": "srt",
"mode": "per-session"
}
}
}
Managed Docker sandboxes for Gateway
The default remains direct: no Docker checks or OpenSandbox service are started. To opt in,
install and start Docker (Docker Desktop on macOS; Docker Desktop with WSL2 integration on Windows),
then set bot.sandbox.backend to opensandbox and optionally bot.sandbox.mode to per-session
in the ov.conf used by Gateway.
Both vikingbot gateway --config /path/to/ov.conf and OpenViking --with-bot check dependencies,
prepare images, generate a private Server configuration and API key, start OpenSandbox, and verify
command execution plus file round trips before accepting requests. Failure aborts startup;
there is no fallback to Direct. --with-bot waits for readiness rather than process existence.
Generated configuration and logs live in {storage.workspace}/bot/runtime/opensandbox/gateway-*/.
The generated credential-bearing configuration is removed on normal shutdown; logs are retained.
There is no need to edit ~/.sandbox.toml.
The managed service also restricts Docker-published sandbox ports to 127.0.0.1, adapting
OpenSandbox Server 0.1.6's all-interface bindings without modifying external Servers.
Settings under bot.sandbox.backends.opensandbox:
| Setting | Default | Purpose |
|---|---|---|
managed |
true |
Manage a local Docker-backed Server; false connects to an external service |
server_url |
http://localhost:18792 |
Local HTTP endpoint in managed mode; port is configurable |
api_key |
empty | Generated in managed mode; supply the external service's key otherwise |
startup_timeout |
600 |
Total startup timeout in seconds, including first-time image pulls |
use_server_proxy |
true |
Access sandbox endpoints through the Server |
default_image |
opensandbox/code-interpreter:v1.0.1 |
Workload image; needs shell and Python 3 |
execd_image |
opensandbox/execd:v1.0.6 |
Execution daemon image |
egress_image |
opensandbox/egress:v1.0.1 |
Network policy sidecar image |
pids_limit |
256 |
Managed Docker sandbox process limit |
runtime.cpu / runtime.memory |
500m / 1Gi |
Per-sandbox resource limits |
runtime.timeout |
300 |
Sandbox lifetime in seconds, renewed on use |
network.allowed_domains / network.denied_domains |
[] / [] |
Default-deny egress; deny rules precede allow rules |
Managed sandboxes use bridge networking, dropped capabilities and no-new-privileges. Each container
bind-mounts only its dedicated host workspace at /workspace, with read/write access:
The workload container, including execd, runs as the workspace owner's numeric UID/GID with
HOME=/workspace. This supports ordinary Linux users' 0755 directories and 0644 files without
relaxing permissions or restoring CAP_DAC_OVERRIDE. Egress and image-cache containers keep their
own execution identities.
{storage.workspace}/bot/runtime/opensandbox/
├── gateway-*/ # Server configuration and logs; never mounted
└── workspaces/
├── shared/ # shared mode → container /workspace
├── <session/channel-key>/ # one workspace per session/channel
└── compile/<task-id>/ # isolated Compile task workspaces
Files are directly visible in Finder and edits from either side affect the same directory.
Chat workspace files survive container destruction and are reused on recreation. Bootstrap files
and enabled local Skills are initialized only when the directory is first created; user edits are
preserved thereafter. Compile task directories retain their existing cleanup lifecycle. Bot config,
Server credentials, the Docker socket and other session directories are not mounted.
Existing bot/workspace/shared files are not migrated automatically; customize the new workspace.
shared creates and retains one sandbox at startup. Other modes use a disposable startup probe and
create real instances on demand. Compile gets a sandbox per task. Shutdown cancels active work,
cleans up sandboxes, then stops the owned Server. SIGKILL/power loss can leave containers behind;
expiration cleanup cannot run while the managed Server is stopped, so verify recovery on restart.
External mode uses file APIs without local bind mounts, skips local Docker checks and never starts or stops the external service. Its operator
must configure the runtime, egress component and security policy. Automatic management applies to
Gateway / --with-bot; standalone vikingbot chat requires a prestarted OpenSandbox service.
Run the optional Docker permission regression with the images above available. It uses native
Linux volume storage to exercise UID 1000 ownership, 0755 directories, 0644 files, command/file
API writes and container recreation without Docker Desktop host file-sharing permission translation:
VIKINGBOT_TEST_DOCKER=1 PYTHONPATH=bot python -m pytest -q -o addopts='' bot/tests/test_opensandbox_docker_permissions.py
HTTP API
The Gateway Bot API uses the /bot/v1 prefix:
| Method | Path | Purpose |
|---|---|---|
| POST | /bot/v1/chat |
Synchronous chat |
| POST | /bot/v1/chat/stream |
SSE streaming chat |
| POST | /bot/v1/feedback |
Submit response feedback |
| GET/POST | /bot/v1/sessions |
List or create API Sessions |
| GET/DELETE | /bot/v1/sessions/{id} |
Retrieve or delete a Session |
When an OpenViking upstream is configured, /api/v1/* is proxied to OpenViking Server.
Operations Commands
| Command | Purpose |
|---|---|
vikingbot status |
Show model and configuration status |
vikingbot channels status |
Show configured channels |
vikingbot channels login |
Log in to the WhatsApp bridge |
vikingbot cron list |
List scheduled jobs |
vikingbot cron add |
Add a scheduled job |
vikingbot cron run |
Run a job manually |
vikingbot feedback-stats |
Aggregate response feedback and outcome metrics |
Enable Langfuse with:
{
"bot": {
"langfuse": {
"enabled": true,
"secret_key": "<langfuse-secret-key>",
"public_key": "<langfuse-public-key>",
"base_url": "http://localhost:3000"
}
}
}
The repository includes deploy/docker/deploy_langfuse.sh for local deployment.
Security Notes
- Never commit model API Keys, OpenViking API Keys, or Gateway Tokens to the repository.
- A non-localhost Gateway requires a strong random Token and should be protected with HTTPS at the network layer.
X-Gateway-Tokenprotects only the Gateway; it does not replace an OpenViking user identity.allow_from: []allows every sender. Configure an explicit allowlist for public deployments.- The
directbackend executes files and shell commands with the Bot process user's permissions and is not suitable for untrusted callers. openviking_connectionmay come only from a trusted Server proxy or a trusted local path. Do not accept identity claims directly from a public request body.