What is a tenant?
A tenant is a namespace inside a single OpenClawMU gateway. It owns a directory on disk, a SHA-256-hashed token, a quota budget, a set of channel pairings, and an optional config overlay that can override anything except admin-only keys.
From the operator's perspective, a tenant is the unit you bill, snapshot, rotate credentials for, or delete. From an end-user's perspective, a tenant is just "the AI assistant they talk to" — they never see the multi-tenant machinery.
What's isolated per tenant
- Session store — chat history, conversation state, tool-use traces.
- Memory — sqlite-vec embeddings + content store.
- Plugins / skills — installed extensions from ClawHub or local paths.
- Sandboxes — per-tenant bubblewrap or Docker roots.
- Cron jobs — scheduled tasks owned by this tenant.
- Channel credentials — WhatsApp pairing, Telegram bot token, Slack OAuth, etc.
- Devices & nodes — paired clients for distributed setups.
- Config overlay — tenant-level overrides (model choice, max tokens, allowed tools).
What's shared across tenants
- The Node process — one gateway, one event loop. Tenants share CPU.
- LLM clients — stateless adapter classes; calls are tagged with tenant ID.
- The dispatcher — the function that routes JSON-RPC method names to handlers.
- Gateway config — admin keys, rate-card, S3 bucket configuration.
The token model
Tenant tokens are generated by the gateway when you run tenants
create. The plaintext token is shown once in the CLI
output — you copy it into your client app or your CI secret store.
The gateway stores only the SHA-256 hash. There is no recovery path
if you lose the token; tenants token rotate is the
correct response.
Verification on every inbound request:
- Extract the Bearer token from the
Authorizationheader. - Compute its SHA-256 hash.
- Look up the hash in the keystore.
- Constant-time compare with
crypto.timingSafeEqual. - If match: resolve the tenant ID and attach it to the request context. If not: 401, no detail leaked.
Admin-only key protection
Tenants can override their own config via a YAML overlay — model choice, max tokens, system prompt, allowed tools. But certain keys are admin-only: API credentials for downstream LLMs, the rate card, S3 bucket credentials, gateway-wide feature flags. Tenant overlays that try to override these keys are rejected at load time with a clear error.
# Allowed overrides — tenant scope
model: claude-opus-4-7
max_tokens: 8192
system_prompt: |
You are the Acme Corp AI assistant.
Be concise. Cite sources.
allowed_tools:
- bash
- web_search
- file_read
# Forbidden — these would fail load (admin-only keys)
# anthropic_api_key: ... # ✗ rejected
# rate_card: ... # ✗ rejected
# s3_credentials: ... # ✗ rejected Path-traversal protection
Every method that takes a path string normalizes the path with
path.resolve and asserts that the resolved path starts
with the tenant's root directory. Inputs containing
.., absolute paths, or symlinks that escape the root
are rejected. This applies to: plugin loaders, file_read /
file_write tools, sandbox mount specs, backup target paths, restore
source paths, and config overlay file paths.
The 59 tenant-scoped methods
The dispatcher allow-lists 59 JSON-RPC methods that a tenant token can invoke. Categories:
- tenants.* — self-info, token-rotate (own scope only).
- terminal.* — spawn, write, resize, close.
- config.* — read & write the tenant config overlay.
- agents.* — create runs, stream outputs, cancel.
- sessions.* — create, list, get, delete.
- cron.* — add, list, remove, run-now.
- skills.* — install from ClawHub, list, uninstall.
- channels.* — pair WhatsApp/Telegram/Slack/etc., list, unpair.
- voice.* — wake-word config, transcript history.
- devices.* / nodes.* — pair external clients.
- health — gateway and per-tenant liveness.
Methods outside this allow-list (e.g. tenants.create,
tenants.delete, billing rate-card management) require
the admin key — they never accept a tenant token.