Architecture Decisions
May 5, 2026 ยท View on GitHub
This file records decisions that are still relevant to the current implementation.
ADR-000: Platform-First Runtime
OpenHermit optimizes for hosted multi-agent operation instead of a single local assistant process. The gateway is the control plane, and agents are registered resources with durable state and lifecycle management.
ADR-001: Internal State Is Outside The Workspace
Conversation history, memories, instructions, users, schedules, skills, MCP assignments, and sandbox rows (per-agent execution environments) are internal state. They live in PostgreSQL, scoped by agent_id where applicable.
The workspace is external task state: user files, generated artifacts, repositories, and mounted data.
ADR-002: PostgreSQL With Drizzle Stores
packages/store uses Drizzle and PostgreSQL. SQL migrations live in packages/store/drizzle/; schema definitions live in packages/store/src/schema.ts.
PostgreSQL gives shared durable state for many agents, indexed event/session queries, and full-text memory search. Drizzle keeps query code close to TypeScript types without introducing a generated ORM client.
ADR-003: Gateway-Managed In-Process Agents
The current gateway starts AgentRunner instances in-process through AgentInstanceManager. This is simpler than a reverse proxy over per-agent processes while preserving explicit /api/agents/{agentId}/... routing and per-agent lifecycle.
The AgentInfo.port field remains protocol-compatible metadata, but current managed agents do not require per-agent ports.
ADR-004: Durable Sessions Do Not Close Permanently
Sessions are durable threads. They can be idle, running, awaiting approval, or inactive. /new and channel session switching mark old sessions inactive rather than destroying history.
ADR-005: User Identity Is Channel-Based And Role-Gated
Users are resolved from (channel, channelUserId) identities. Roles are per-agent: owner, user, or guest.
Owners get full management tools. Users get standard memory/web/session access. Guests are intentionally restricted. Channel adapters can auto-create unknown identities as guests.
ADR-006: Introspection And Compaction Are Separate
Compaction keeps model context within limits. Introspection maintains long-term memory, working memory, and session descriptions. They run on different triggers and use different tool surfaces.
ADR-007: Program-Driven Scheduler
Scheduling is a first-class subsystem with cron and one-shot schedules. Schedules are stored in PostgreSQL, run through Scheduler, and post prompts back into agent sessions through AgentRunner host callbacks.
ADR-008: Skills Are Prompt Assets, MCP Servers Are Tool Providers
Skills are directories containing SKILL.md and optional supporting files. They are indexed in the prompt and synced into each exec backend's sandbox at <agent_home>/.openhermit/skills/system/ via runner.syncSkills.
MCP servers are executable external tool providers. Their tools are discovered at runtime and exposed as namespaced mcp__{serverId}__{toolName} tools.
ADR-009: Built-In Channels Run Beside Agents
Telegram, Discord, and Slack adapters are built-in channel packages. The gateway starts them when the agent config enables them, registers per-channel bearer tokens, and records status for the UI/API.
Adapters use the OpenHermit SDK over gateway agent routes instead of reaching into runner internals.
ADR-010: Execution Backends Are Pluggable
The exec tool runs through an ExecBackendManager, built from per-agent rows in the sandboxes table. Four backends are implemented: docker, host, e2b, and daytona. Gateway-created agents auto-provision a sandbox from the preset named by autoProvisionSandbox in gateway.json (default: docker-ubuntu); the host backend is reserved for the privileged "devops" agent (see sandbox-model.md).