You built a Hermes Agent. It works. Then a client says: "Can I get one of those?"
The solopreneur's edge is build once, sell to many. Hermes ships with profile-based isolation that is the right foundation for multi-client deployments — but profiles alone are not a complete security boundary. This article shows what profiles actually isolate, where you need to add sandboxing, and how Kanban boards keep multi-team work organized.
What Are Hermes Profiles?
A profile is a fully independent agent instance with its own config.yaml, .env, SOUL.md, memory store, session history, skills, cron jobs, and gateway state.
hermes profile create acme # Now: acme chat, acme gateway start, acme cron list
What profiles isolate: Hermes state. Profile A cannot read Profile B's memory, sessions, or cron jobs.
What profiles do NOT isolate: The filesystem. On the default local terminal backend, every profile runs as your OS user with the same file access as any other process you own.
Can Hermes Agent Support Multiple Clients on One Server?
Yes — with the right guardrails.
The Isolation Reality
| Layer | Isolated by Profiles? | What You Must Add |
|---|---|---|
| Memory (SQLite DB) | Fully | Nothing |
| Sessions | Fully | Nothing |
| API keys (`.env`) | Fully | Nothing |
| Telegram bot | Token lock enforced | One token per running gateway |
| Filesystem | Not on local backend | terminal.cwd, Docker, SSH, or Modal backends |
| Execution | Not on local backend | Docker sandbox, Modal, or Singularity |
| Dashboard visibility | Shared dashboard can expose profiles and Kanban work across the same host | Keep the shared dashboard private; a client-facing dashboard needs a separate isolated Hermes runtime, not just a profile-scoped process |
Without filesystem restrictions or a sandbox, one client-facing session may access files available to the host user account. Always pair profiles with a sandboxed terminal backend for client-facing work.
Architecture Overview
external_users { # External Clients
n1: circle label="Client A User"
n2: circle label="Client B User"
}
shared_host { # Shared Host Environment
n3: rectangle label="Reverse Proxy"
n4: rectangle label="Shared Gateway"
n5: rectangle label="Admin Dashboard"
n6: rectangle label="Profile: acme"
n7: rectangle label="Profile: beta"
n8: rectangle label="kanban.db"
n9: rectangle label="Host Filesystem"
}
client_a_tools { # Client A Internal
n10: rectangle label="Engineering tools"
n11: rectangle label="Support tools"
n12: rectangle label="Research tools"
}
external_users.n1.handle(right) -> shared_host.n3.handle(left) [label="webhook"]
external_users.n2.handle(right) -> shared_host.n3.handle(left) [label="webhook"]
shared_host.n3.handle(right) -> shared_host.n4.handle(left)
shared_host.n4.handle(right) -> shared_host.n6.handle(left) [label="Token A"]
shared_host.n4.handle(bottom) -> shared_host.n7.handle(top) [label="Token B"]
shared_host.n4.handle(top) -> shared_host.n5.handle(bottom) [label="ops only"]
shared_host.n6.handle(right) -> client_a_tools.n10.handle(left) [label="/engineering"]
shared_host.n6.handle(right) -> client_a_tools.n11.handle(left) [label="/support"]
shared_host.n6.handle(bottom) -> client_a_tools.n12.handle(top) [label="/research"]
shared_host.n6.handle(bottom) -> shared_host.n8.handle(top) [label="shared board"]
shared_host.n7.handle(bottom) -> shared_host.n8.handle(top) [label="shared board"]
shared_host.n6.handle(bottom) -> shared_host.n9.handle(top) [label="local backend risk"]
shared_host.n7.handle(bottom) -> shared_host.n9.handle(top) [label="local backend risk"]
Critical: The Dashboard Problem
A single Hermes dashboard instance pointed at the shared Hermes runtime shows more than one profile's work on the same host.
This is not a customer-facing multi-tenant portal. It is an internal operator console. The Kanban docs are explicit that Kanban is shared across all Hermes profiles, stored in ~/.hermes/kanban.db, and that tasks are profile-agnostic by design.
The fix is straightforward:
- Keep the shared dashboard as your private ops console. Never give clients access to it.
- For client-facing dashboards, run a separate isolated Hermes runtime per client, not just a profile-scoped dashboard process. In practice that means a separate install, container, VM, or at minimum a separate OS-user/runtime boundary.
- Put each behind its own subdomain with authentication (nginx reverse proxy + basic auth or OAuth). Never let a client hit the shared instance.
- Or skip the dashboard entirely — many solopreneurs operate purely through Telegram channels and never expose a web UI to clients.
The rule is simple: one client-visible dashboard = one isolated Hermes runtime. If a client needs stronger separation than that, move them to a separate host or container with its own Hermes installation.
Are Hermes Profiles Secure Enough for Multi-Tenant Use?
Profiles are a foundation, not a complete solution. Security depends on what you add around them.
Minimum Security Checklist
# 1. Fresh profile (never --clone from personal) hermes profile create client-x # 2. Restrict workspace client-x config set terminal.cwd /var/hermes-clients/client-x # 3. Use a sandboxed backend client-x config set terminal.backend docker # 4. Set a separate bot token nano ~/.hermes/profiles/client-x/.env # TELEGRAM_BOT_TOKEN=789012:ABC-DEF-... # 5. Disable irrelevant toolsets client-x config set agent.disabled_toolsets '["flowzap","creative","media"]' # 6. Start its gateway client-x gateway start
Why --clone is risky: It copies your personal config, API keys, and memory into the new profile. Always create fresh profiles for clients.
Telegram Bot Tokens
Each running gateway should use its own bot token. If two profiles share the same token, the second gateway is blocked with a clear error.
ERROR: [Telegram] Bot token already in use by profile acme (PID X).
For multi-department clients, either use one profile with internal routing (simpler) or create multiple Telegram bots with separate tokens.
Architecture Options: 4 Setup Patterns
Option 1: Single Client, Single Department
external_actor { # External Actor
n1: circle label="Client User"
}
hermes_runtime { # Hermes Runtime
n2: rectangle label="Telegram Bot"
n3: rectangle label="Gateway"
n4: rectangle label="Profile: client-x"
}
execution_sandbox { # Execution Boundary
n5: rectangle label="Isolated Workspace"
}
external_actor.n1.handle(right) -> hermes_runtime.n2.handle(left) [label="message"]
hermes_runtime.n2.handle(right) -> hermes_runtime.n3.handle(left)
hermes_runtime.n3.handle(right) -> hermes_runtime.n4.handle(left) [label="routes"]
hermes_runtime.n4.handle(right) -> execution_sandbox.n5.handle(left) [label="executes code"]
Use for: First client, proof-of-concept, simple Q&A bots.
Option 2: Single Client, Multiple Departments (Internal Routing)
One profile, one bot. The SOUL.md routes by prefix (/engineering, /support).
team_users { # Client Entry Points
n1: circle label="Team User"
}
entry_point { # Bot & Routing
n2: rectangle label="Shared Bot"
n3: rectangle label="Gateway"
n4: diamond label="Prefix Router"
}
hermes_profile { # Unified Profile
n5: rectangle label="Profile: acme"
}
department_modes { # Internal Logic
n6: rectangle label="Engineering Mode"
n7: rectangle label="Support Mode"
}
team_users.n1.handle(right) -> entry_point.n2.handle(left) [label="/eng or /support"]
entry_point.n2.handle(right) -> entry_point.n3.handle(left)
entry_point.n3.handle(right) -> entry_point.n4.handle(left)
entry_point.n4.handle(right) -> hermes_profile.n5.handle(left) [label="loads profile"]
hermes_profile.n5.handle(right) -> department_modes.n6.handle(left) [label="if /eng"]
hermes_profile.n5.handle(right) -> department_modes.n7.handle(left) [label="if /support"]
Use for: A client with 2-3 teams sharing one chat channel.
Option 3: Multiple Clients, Separate Bots (Core Multi-Tenant)
Each client gets their own bot token, gateway, and isolated profile.
external_tenants { # External Tenants
n1: circle label="Client A User"
n2: circle label="Client B User"
}
client_gateways { # Per-Token Entry Points
n3: rectangle label="Gateway A"
n4: rectangle label="Gateway B"
}
hermes_profiles { # Isolated Profiles
n5: rectangle label="Profile: acme"
n6: rectangle label="Profile: beta"
}
execution_sandboxes { # Execution Boundaries
n7: rectangle label="Sandbox A"
n8: rectangle label="Sandbox B"
}
external_tenants.n1.handle(right) -> client_gateways.n3.handle(left) [label="Token A"]
external_tenants.n2.handle(right) -> client_gateways.n4.handle(left) [label="Token B"]
client_gateways.n3.handle(right) -> hermes_profiles.n5.handle(left)
client_gateways.n4.handle(right) -> hermes_profiles.n6.handle(left)
hermes_profiles.n5.handle(right) -> execution_sandboxes.n7.handle(left) [label="Docker / Modal"]
hermes_profiles.n6.handle(right) -> execution_sandboxes.n8.handle(left) [label="Docker / Modal"]
Use for: 2+ paying clients. Each gets "their own bot" from one server.
Tip: Create fresh profiles (hermes profile create client-x), never --clone. No inherited API keys, no cross-client memory leaks.
Option 4: Full Multi-Tenant with Departments
client_users { # External Participants
n1: circle label="Acme Users"
n2: circle label="Beta Users"
}
entry_gateways { # Client Gateways
n3: rectangle label="Acme Gateway"
n4: rectangle label="Beta Gateway"
}
hermes_profiles { # Routed Profiles
n5: diamond label="Acme Router"
n6: rectangle label="acme-eng"
n7: rectangle label="acme-support"
n8: rectangle label="beta"
}
execution_sandboxes { # Secure Runtimes
n9: rectangle label="Acme Sandbox"
n10: rectangle label="Beta Sandbox"
}
client_users.n1.handle(right) -> entry_gateways.n3.handle(left) [label="/eng or /support"]
client_users.n2.handle(right) -> entry_gateways.n4.handle(left) [label="general"]
entry_gateways.n3.handle(right) -> hermes_profiles.n5.handle(left)
hermes_profiles.n5.handle(right) -> hermes_profiles.n6.handle(left) [label="if /eng"]
hermes_profiles.n5.handle(bottom) -> hermes_profiles.n7.handle(top) [label="if /support"]
entry_gateways.n4.handle(right) -> hermes_profiles.n8.handle(left)
hermes_profiles.n6.handle(right) -> execution_sandboxes.n9.handle(left) [label="executes"]
hermes_profiles.n7.handle(right) -> execution_sandboxes.n9.handle(left) [label="executes"]
hermes_profiles.n8.handle(right) -> execution_sandboxes.n10.handle(left) [label="executes"]
Where to Start
| Your Situation | Start With | Upgrade |
|---|---|---|
| First client, testing | Option 1 | → Option 2 for departments |
| One client, 2+ teams | Option 2 | → Option 4 for client 2 |
| 3+ paying clients | Option 3 | → Option 4 as clients grow |
How Do Kanban Boards Help Multi-Client Teams?
Hermes Kanban is a durable task board — every task is a row in SQLite, every handoff is a row any profile can see and edit. Since v0.13.0, the dispatcher runs embedded in the gateway by default.
Use separate boards per client to reduce coordination errors and keep work segmented. In Hermes Kanban, boards are the hard isolation boundary inside Kanban, while tenants are only a soft filter.
task_planner { # Planner
n1: rectangle label="Orchestrator"
}
shared_board { # Coordination
n2: rectangle label="Shared kanban.db"
}
specialized_workers { # Workers
n3: rectangle label="Researcher A"
n4: rectangle label="Researcher B"
n5: rectangle label="Writer"
}
final_result { # Output
n6: circle label="Deliverable"
}
task_planner.n1.handle(right) -> shared_board.n2.handle(left) [label="create tasks"]
shared_board.n2.handle(right) -> specialized_workers.n3.handle(left) [label="claim task 1"]
shared_board.n2.handle(right) -> specialized_workers.n4.handle(left) [label="claim task 2"]
specialized_workers.n3.handle(right) -> shared_board.n2.handle(right) [label="complete"]
specialized_workers.n4.handle(right) -> shared_board.n2.handle(right) [label="complete"]
shared_board.n2.handle(bottom) -> specialized_workers.n5.handle(top) [label="unblock writer"]
specialized_workers.n5.handle(right) -> final_result.n6.handle(left) [label="publish"]
The /kanban slash command works across all gateway platforms — Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Mattermost, email, and SMS.
What Changed in Hermes Agent v0.13.0?
As of May 7, 2026, the latest public Hermes Agent release is v0.13.0. The items below are the features most relevant to this setup in the current docs and release notes.
- Kanban dispatcher is gateway-embedded by default — no separate daemon process for the normal setup.
- Multiple Kanban boards — clean project separation per client or project.
- Profile auto-aliases — every profile becomes its own CLI command.
- Token lock safety — duplicate bot tokens trigger a clear error.
- Bundled skills sync to all profiles on
hermes update.
Step-by-Step: Onboarding a New Client
# 1. Fresh profile — no --clone hermes profile create client-x # 2. Restrict workspace and sandbox client-x config set terminal.cwd /var/hermes-clients/client-x client-x config set terminal.backend docker # 3. Configure bot token nano ~/.hermes/profiles/client-x/.env # TELEGRAM_BOT_TOKEN=789012:ABC-DEF-... # 4. Disable bundled toolsets the client does not need client-x config set agent.disabled_toolsets '["flowzap","creative","media"]' # Note: bundled skills auto-sync on hermes update. # Custom skills go in ~/.hermes/profiles/client-x/skills/ only if you build them for this profile. # 5. Start gateway client-x gateway start
For multi-department clients, use one profile + SOUL.md routing instead of fighting the token lock.
~/.hermes/profiles/beta/SOUL.md → "Beta Corp agent. Prefix /engineering for tool access. Prefix /support for read-only ticket resolution."
What Works vs. What Breaks
| Scenario | Verdict |
|---|---|
| 3 clients, 1 department each, Docker backend | Works. Profiles + sandboxed backends cover Hermes state + execution isolation. |
| 1 client, 5 departments, shared bot token | Token lock blocks. Use internal routing or separate bots. |
| Two profiles, same token | Second gateway refuses to start. |
| Different backends per client (Docker vs local) | Works. Set terminal.backend per profile. |
| Cross-client filesystem via local backend | Profiles don't isolate filesystem. Use Docker. |
| Kanban task in wrong client's board | Separate boards per client reduces this risk because boards are the hard isolation boundary inside Kanban. |
| Client accesses shared dashboard | Shared dashboard is admin-only. Kanban is shared across profiles on the same host, so a client-facing dashboard should run on a separate isolated Hermes runtime, not just an isolated HERMES_HOME. |
--clone from personal to client profile |
Risk of API key leakage. Create fresh profiles. |
The Bottom Line
Hermes Agent can support multiple clients on one server, but safe multi-tenant deployment depends on how you configure execution. Profiles isolate Hermes state well; Docker, SSH, Modal, Daytona, Vercel Sandbox, or Singularity provide the stronger execution boundaries you need for client-facing work. If you want client-facing dashboards, use a separate isolated Hermes runtime per company; if you stay on a shared host, keep the dashboard private and use messaging channels for client access.
Inspirations
Official Documentation
- Hermes Agent Profiles
- Kanban boards
- Security model
- Terminal backend config
- Telegram gateway setup
- Profile distributions
- v0.13.0 release notes
- Full documentation
Community & Commentary
- Profiles as governance primitive
- Defense-in-depth analysis
- Production deployment patterns
- Community builds
