Skip to content

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 resources within the same account
  • 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 account values by default
  • ROOT can create and delete accounts
  • resources, user, agent, and session all live inside an account

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 = false means viking://agent/{agent_id}/...
  • isolate_agent_scope_by_user = true means viking://agent/{agent_id}/user/{user_id}/...

Roles

RoleScopeTypical capabilities
ROOTGlobalCreate/delete accounts, cross-tenant access, user management
ADMINSingle accountManage users in the same account, regenerate user keys
USERSingle accountAccess its own user/agent/session data and shared resources in the same account

Authentication Modes

OpenViking Server supports two multi-tenant related authentication modes:

ModeConfigIdentity sourceTypical use case
api_keyserver.auth_mode = "api_key"Root key or user keyStandard deployment
trustedserver.auth_mode = "trusted"Upstream-injected X-OpenViking-Account / X-OpenViking-UserBehind 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 typeShared across accountsShared inside one accountDefault isolation boundary
resourcesNoYesaccount
userNoNouser
agentNoDepends on account namespace policyDefault: agent
sessionNoNouser / session

Storage Layer

For users, URIs still look like normal viking://... paths:

text
viking://resources/project-a/
viking://user/alice/memories/
viking://agent/91f3ab12cd34/memories/

But the underlying storage automatically gains an account prefix:

text
/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
  • resources can include account-shared resources
  • memory and skill are further filtered by the current user space and agent space

This keeps "what you can search" aligned with "what you can read."

Standard Usage Flow

1. Enable multi-tenancy

json
{
  "server": {
    "auth_mode": "api_key",
    "root_api_key": "your-secret-root-key"
  }
}

2. ROOT creates an account and the first admin

bash
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

bash
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:

bash
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:

bash
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
  • apiKey should normally be a user key
  • The server resolves account_id and user_id from that user key
  • The plugin explicitly passes X-OpenViking-Agent

Typical config:

bash
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"
  • agentId distinguishes different OpenClaw instances or agent roles
  • resources can be shared inside the same account, while user and agent memory 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:

  • account and user are resolved server-side from the key
  • The plugin only needs to provide agentId
  • Internally, the plugin derives default user and agent memory 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:

json
{
  "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

ScenarioRecommended pattern
One OpenClaw instance maps to one fixed identityOpenClaw plugin + user key
One gateway or bot service serves many end usersVikingbot + root-key-managed users
A trusted gateway injects identity upstreamtrusted mode
Local single-user experience without formal tenant isolationDev 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"

Released under the Apache-2.0 License.