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:

  1. Inline tool guards -- each built-in tool's execute() validates workspace boundaries, governance rules, and credential write protection before executing
  2. Tool handler validation -- Organization tools validate caller identity, parent-child relationships; browser tools enforce SSRF protection and domain allowlists
  3. Credential scrubbing -- withAudit() wrappers + scrubSecrets() strip secret values from all tool inputs/outputs before logging
  4. Post-execution audit -- structured logging of all operations with duration, tool name, and result summary
  5. 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.