Multi-Tenant Setup
Heartbit’s daemon mode supports multi-tenant isolation out of the box. Each authenticated user gets isolated memory, workspace, and audit context.
JWT/JWKS Authentication
Section titled “JWT/JWKS Authentication”Configure JWT validation in the daemon auth section:
[daemon.auth]bearer_tokens = ["$ADMIN_API_KEY"] # static keys (optional)jwks_url = "https://idp.example.com/.well-known/jwks.json" # JWKS endpointissuer = "https://idp.example.com" # validate iss claimaudience = "heartbit-daemon" # validate aud claimuser_id_claim = "sub" # JWT claim for user ID (default: "sub")tenant_id_claim = "tid" # JWT claim for tenant ID (default: "tid")roles_claim = "roles" # JWT claim for roles (default: "roles")Both bearer token and JWT auth can be active simultaneously. Bearer tokens are useful for service-to-service calls, while JWT provides per-user identity.
UserContext Extraction
Section titled “UserContext Extraction”When a request arrives with a valid JWT, Heartbit extracts a UserContext:
user_id— from the configureduser_id_claim(default:sub)tenant_id— from the configuredtenant_id_claim(default:tid)roles— from the configuredroles_claim(default:roles)raw_token— the original JWT, carried through for token exchange
This context flows through the entire task lifecycle: agent execution, memory operations, tool calls, and audit records.
NamespacedMemory
Section titled “NamespacedMemory”Memory is automatically namespaced per tenant and user. The NamespacedMemory wrapper prefixes all memory operations with tenant:{tid}:user:{uid}:
tenant:acme:user:alice: -> Alice's memories in Acme orgtenant:acme:user:bob: -> Bob's memories in Acme orgtenant:globex:user:alice: -> Alice's memories in Globex org (separate)This ensures complete isolation — users in different tenants never see each other’s memories, even if they share the same user_id.
Memory pruning is also namespace-scoped: pruning via a NamespacedMemory only affects entries within that namespace.
Workspace Scoping
Section titled “Workspace Scoping”Each tenant/user combination gets an isolated workspace directory:
{workspace_root}/{tenant_id}/{user_id}/Path traversal prevention is enforced at the tool layer — agents cannot access files outside their scoped workspace.
Role-Gated Memory Writes
Section titled “Role-Gated Memory Writes”Control which roles can write to shared (institutional) memory:
[daemon.memory]shared_write_roles = ["admin", "lead"]When configured, only users with matching roles get write access to shared memory. All users retain read access. This prevents junior agents or external users from polluting the shared knowledge base.
Audit Trail
Section titled “Audit Trail”All audit records include tenant context:
user_id— the authenticated usertenant_id— the tenant organizationdelegation_chain— tracks the chain of delegations from orchestrator to sub-agents
The AuditTrail trait provides entries_for_tenant() for tenant-scoped audit queries.
Task Isolation
Section titled “Task Isolation”Tasks are filtered by tenant context:
GET /tasksreturns only tasks belonging to the authenticated tenantGET /tasks/{id}rejects unauthenticated access to tenant-scoped tasksDELETE /tasks/{id}enforces tenant boundaries
Complete Example
Section titled “Complete Example”[provider]name = "anthropic"model = "claude-sonnet-4-20250514"
[daemon]bind = "0.0.0.0:3000"max_concurrent_tasks = 8
[daemon.auth]jwks_url = "https://auth.example.com/.well-known/jwks.json"issuer = "https://auth.example.com"audience = "heartbit"tenant_id_claim = "org_id"
[daemon.memory]shared_write_roles = ["admin"]
[daemon.kafka]brokers = "kafka:9092"
[memory]type = "postgres"database_url = "postgresql://heartbit:password@db/heartbit"
[[agents]]name = "assistant"description = "General purpose assistant"system_prompt = "You are a helpful assistant."Submit a task with JWT authentication:
curl -X POST http://localhost:3000/tasks \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer <jwt-token>' \ -d '{"task": "Summarize recent activity"}'The agent runs with the user’s identity: memory is namespaced, workspace is scoped, and all audit records include the tenant context.