Multi-Tenant
OpenViking multi-tenancy does not mean "deploy one isolated server per team." Instead, a single OpenViking Server uses three identity boundaries, account, user, and agent, to control sharing and isolation.
This model fits two common scenarios:
- Multiple teams or customers share one OpenViking service, but their data must stay isolated
- Multiple users and multiple agents inside one team need shared resources but isolated memories
What It Enables
With multi-tenancy enabled, you can:
- Serve multiple teams, customers, or applications from one OpenViking Server
- Isolate different teams with
account - Share
resourceswithin the sameaccount - Isolate user memories and sessions with
user - Further separate agent memories, skills, and instructions with
agent_id - Manage permissions with ROOT / ADMIN / USER roles
- Support different integration patterns such as OpenClaw plugin, Vikingbot, CLI, and HTTP SDKs
Core Identity Model
account_id
account is the outer tenant boundary. You can think of it as a workspace, team, or customer space.
- Data is isolated across different
accountvalues by default - ROOT can create and delete
accounts resources,user,agent, andsessionall live inside anaccount
user_id
user is the per-account user boundary.
- User memories and user sessions are isolated by
user_id - A normal user can only access its own user space
- An admin can manage users inside the same
account
agent_id
agent_id separates agent-level space.
- Agent URI shape is controlled by per-account namespace policy
isolate_agent_scope_by_user = falsemeansviking://agent/{agent_id}/...isolate_agent_scope_by_user = truemeansviking://agent/{agent_id}/user/{user_id}/...
Roles
| Role | Scope | Typical capabilities |
|---|---|---|
| ROOT | Global | Create/delete accounts, cross-tenant access, user management |
| ADMIN | Single account | Manage users in the same account, regenerate user keys |
| USER | Single account | Access its own user/agent/session data and shared resources in the same account |
Authentication Modes
OpenViking Server supports two multi-tenant related authentication modes:
| Mode | Config | Identity source | Typical use case |
|---|---|---|---|
api_key | server.auth_mode = "api_key" | Root key or user key | Standard deployment |
trusted | server.auth_mode = "trusted" | Upstream-injected X-OpenViking-Account / X-OpenViking-User | Behind a trusted gateway |
What root_api_key Does
Once server.root_api_key is configured, OpenViking enters formal multi-tenant mode:
- The root key manages accounts and users
- User keys are generated by the Admin API for normal data access
- The server resolves
account_id,user_id, and role from the user key
If auth_mode = "api_key" and root_api_key is not configured, the server runs in dev mode:
- All requests are treated as ROOT
- The default identity is
default/default/default - This is only allowed on localhost
Sharing and Isolation Boundaries
Logical Layer
| Data type | Shared across accounts | Shared inside one account | Default isolation boundary |
|---|---|---|---|
resources | No | Yes | account |
user | No | No | user |
agent | No | Depends on account namespace policy | Default: agent |
session | No | No | user / session |
Storage Layer
For users, URIs still look like normal viking://... paths:
viking://resources/project-a/
viking://user/alice/memories/
viking://agent/91f3ab12cd34/memories/But the underlying storage automatically gains an account prefix:
/local/{account_id}/resources/project-a/
/local/{account_id}/user/alice/memories/
/local/{account_id}/agent/91f3ab12cd34/memories/So multi-tenant isolation does not rely on a special public URI format. It relies on request context, account_id, user_id, and agent_id, applied consistently through the stack.
Retrieval Layer
Semantic retrieval is tenant-aware as well:
- Non-ROOT requests are automatically filtered by
account_id resourcescan include account-shared resourcesmemoryandskillare further filtered by the currentuser spaceandagent space
This keeps "what you can search" aligned with "what you can read."
Standard Usage Flow
1. Enable multi-tenancy
{
"server": {
"auth_mode": "api_key",
"root_api_key": "your-secret-root-key"
}
}2. ROOT creates an account and the first admin
curl -X POST http://localhost:1933/api/v1/admin/accounts \
-H "Content-Type: application/json" \
-H "X-API-Key: your-secret-root-key" \
-d '{
"account_id": "acme",
"admin_user_id": "alice"
}'3. ADMIN or ROOT registers normal users
curl -X POST http://localhost:1933/api/v1/admin/accounts/acme/users \
-H "Content-Type: application/json" \
-H "X-API-Key: <admin-or-root-key>" \
-d '{
"user_id": "bob",
"role": "user"
}'4. Prefer user keys for normal application traffic
For normal reads, writes, searches, and session commits, prefer a user key:
curl http://localhost:1933/api/v1/fs/ls?uri=viking:// \
-H "X-API-Key: <bob-user-key>" \
-H "X-OpenViking-Agent: coding-agent"This lets the server resolve identity directly from the key, without extra tenant headers.
5. Only ROOT uses explicit tenant headers for tenant-scoped data APIs
ROOT does not need tenant headers for Admin APIs, but it does need them for tenant-scoped data APIs such as ls, find, and sessions:
curl http://localhost:1933/api/v1/fs/ls?uri=viking:// \
-H "X-API-Key: <root-key>" \
-H "X-OpenViking-Account: acme" \
-H "X-OpenViking-User: alice" \
-H "X-OpenViking-Agent: coding-agent"Integration Patterns
OpenClaw Plugin 2.0: one instance uses one user key
The current OpenClaw plugin follows a "plugin holds one user identity" model:
- Remote mode config is
baseUrl + apiKey + agentId apiKeyshould normally be a user key- The server resolves
account_idanduser_idfrom that user key - The plugin explicitly passes
X-OpenViking-Agent
Typical config:
openclaw config set plugins.entries.openviking.config.mode remote
openclaw config set plugins.entries.openviking.config.baseUrl "http://your-server:1933"
openclaw config set plugins.entries.openviking.config.apiKey "<user-api-key>"
openclaw config set plugins.entries.openviking.config.agentId "<agent-id>"Characteristics of this model:
- Simple integration, because the plugin does not manage account/user lifecycle
- Best for "one OpenClaw instance maps to one OpenViking user identity"
agentIddistinguishes different OpenClaw instances or agent rolesresourcescan be shared inside the same account, whileuserandagentmemory stay identity-scoped
Why the OpenClaw plugin usually does not set account / user
In api_key mode, a user key is already enough to express identity:
accountanduserare resolved server-side from the key- The plugin only needs to provide
agentId - Internally, the plugin derives default
userandagentmemory scopes from the runtime identity
If you give the plugin a root key directly, normal tenant-scoped data APIs will lack X-OpenViking-Account and X-OpenViking-User, so that is not a good default for day-to-day access.
Vikingbot: root key manages many end users
Vikingbot uses a different practice. It behaves more like a platform serving many end users:
- The bot connects to OpenViking with a root key
- The bot config fixes an
account_id - The bot automatically registers users inside that account
- The bot caches per-user user keys and uses them for memory commit/search whenever possible
Example config:
{
"bot": {
"ov_server": {
"server_url": "http://127.0.0.1:1933",
"root_api_key": "test",
"account_id": "default",
"admin_user_id": "default"
}
}
}Characteristics of this model:
- Good for one bot service serving many chat users
- All users inside the same account share
resources - User memories are isolated through auto-managed user identities
- The bot takes on more tenant lifecycle management logic than the OpenClaw plugin
Which Practice to Choose
| Scenario | Recommended pattern |
|---|---|
| One OpenClaw instance maps to one fixed identity | OpenClaw plugin + user key |
| One gateway or bot service serves many end users | Vikingbot + root-key-managed users |
| A trusted gateway injects identity upstream | trusted mode |
| Local single-user experience without formal tenant isolation | Dev mode without root_api_key |
Common Misunderstandings
1. root_api_key is not the normal business-access key
The root key is mainly for:
- Creating and deleting accounts
- Registering users
- Regenerating keys
- Operations and diagnostics
Normal application traffic should prefer user keys.
2. agentId does not define the tenant
agentId only defines the agent-level space.
- The tenant boundary is
account_id - The user boundary is
user_id - The agent boundary is
agent_id
3. No root_api_key does not mean "formal single-tenant production mode"
That is only dev mode:
- All requests run as ROOT
- It is not suitable for public or shared deployments
4. OpenClaw plugin and Vikingbot are not the same multi-tenant pattern
- OpenClaw plugin is closer to "a client directly uses one user identity"
- Vikingbot is closer to "a platform manages many users and their user keys"
Related Documentation
- Authentication - Auth modes, headers, and key rules
- Configuration -
root_api_key,auth_mode, andagent_id - Admin API - Admin API reference
- API Overview - CLI and HTTP connection patterns
- Data Encryption - At-rest encryption in multi-tenant deployments
- Multi-tenant Example - End-to-end management workflow
- OpenClaw Plugin - OpenClaw integration
- Vikingbot - Bot-side multi-user integration
