Isolation Model - Z-M-Huang/openhive GitHub Wiki
Isolation Model
OpenHive uses cooperative isolation within a trust boundary. All teams run in the same process -- isolation is enforced by the SDK and organization tools, not by OS-level mechanisms.
This is an explicit design choice: the trust boundary is the container. Within it, isolation is cooperative and audited, not adversarial.
Isolation Mechanisms
| Concern | Mechanism | Enforcement Point |
|---|---|---|
| File system boundaries | cwd per session |
Built-in tools resolve relative paths against team cwd via resolve(cwd, file_path), then validate with assertInsideBoundary() |
| Secret scoping | Secret refs resolved per team | Vault is_secret=1 entries read at spawn; values registered with SecretString[] for session-wide scrubbing |
| Tool access | Deny-by-default via activeTools |
Only tools matching config.yaml allowed_tools are included in activeTools array; unlisted tools invisible |
| MCP server access | Scoped MCP servers per team | Only servers listed in team's config.yaml are connected via @ai-sdk/mcp |
| Communication boundaries | Org tree validation | Organization tools enforce parent/child only for all messaging tools |
| Behavioral constraints | Rules (system prompt per team) | Rule cascade loaded at session start |
| Shared data | Explicit .run/shared/ directory |
Teams must explicitly place files there; not in any team's cwd |
| Credential protection | SecretString class + audit wrappers + scrubber | Defense in depth: class wrapping + withAudit() tool wrappers + transport-layer scrubbing; vault_set/vault_delete reject is_secret = 1 rows; vault_list omits secret values |
| SSRF protection | URL validator on browser tools | Private/reserved IPs blocked regardless of domain allowlist configuration |
| Inbound sender trust | TrustGate (channels.yaml policy + SQLite sender_trust) |
Between ChannelRouter and TopicClassifier; code-enforced, not LLM-dependent |
Team Directory Paths
Team directories are located at .run/teams/{name}/ within the container. The cwd option passed to each session's built-in tools is set to this path. All file-system tools (Read, Write, Edit) resolve file_path against team cwd using resolve(cwd, file_path) before any I/O — this ensures relative paths resolve against the team workspace, not process.cwd(). The resolved path is then checked by assertInsideBoundary() to block traversal outside the team root. The exact workspace path is also injected into the system prompt so agents use correct absolute paths.
Governance Classifications
The classifyPath() function in tool-guards.ts evaluates each file-system operation against the following classification:
| Rule Source | Guard Action | Rationale |
|---|---|---|
| system-rules (baked into image) | block violation | Immutable invariants; cannot be overridden by any config |
admin-org-rules (/data/rules/) |
block violation | Admin-managed policies; teams cannot override |
own-team directory (.run/teams/{name}/) |
allow | Team operates within its own directory |
other-team directory (.run/teams/{other}/) |
block | Cross-team file access is never permitted |
Defense in Depth
Invariant enforcement uses three layers:
- Inline tool guards -- each built-in tool's
execute()validates workspace boundaries, governance rules, and credential write protection before executing - Tool handler validation -- Organization tools validate caller identity, parent-child relationships; browser tools enforce SSRF protection and domain allowlists
- Credential scrubbing --
withAudit()wrappers +scrubSecrets()strip secret values from all tool inputs/outputs before logging - Post-execution audit -- structured logging of all operations with duration, tool name, and result summary
- Inbound trust gating -- TrustGate evaluates all inbound messages against sender trust policy before any LLM processing. Code-enforced, not LLM-dependent (see Architecture-Decisions#ADR-30).
No single layer is trusted alone.
Plugin Tool Sandboxing
Plugin tools (team-local TypeScript tools in .run/teams/{name}/plugins/) operate within additional sandboxing constraints beyond the standard isolation mechanisms above. Plugin tools are namespaced ({team_name}.{tool_name}), directory-scoped to their owning team, and wrapped with withAudit() for invocation logging.
Before loading, each plugin passes through static analysis (forbidden pattern detection, secret scanning, TypeScript validation) and interface validation. At runtime, filesystem boundary checks and audit logging provide ongoing enforcement.
For the full guard specification — pre-generation checks, post-creation verification, runtime guards, and error handling — see Organization-Tools#Plugin Tool Guards.