# 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 `account`s - `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 | 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: ```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: " \ -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: " \ -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: " \ -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 "" openclaw config set plugins.entries.openviking.config.agentId "" ``` 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 | 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](../guides/04-authentication.md) - Auth modes, headers, and key rules - [Configuration](../guides/01-configuration.md) - `root_api_key`, `auth_mode`, and `agent_id` - [Admin API](../api/08-admin.md) - Admin API reference - [API Overview](../api/01-overview.md) - CLI and HTTP connection patterns - [Data Encryption](./10-encryption.md) - At-rest encryption in multi-tenant deployments - [Multi-tenant Example](../../../examples/multi_tenant/README.md) - End-to-end management workflow - [OpenClaw Plugin](../../../examples/openclaw-plugin/README.md) - OpenClaw integration - [Vikingbot](../../../bot/README.md) - Bot-side multi-user integration