05. Rust Host Core
1. Purpose
host-core is the privileged local backend of PI-Desktop.
It does not replace pi. It provides safe host capabilities to:
- Electron shell
- pi agent runtime
- plugin system
2. Responsibilities
- Workspace path canonicalization, boundary checks, and permission-gated explicit outside paths
- Builtin tool execution (Read/Glob/Grep/Write/Edit/Bash)
- Authoritative durable session-mode and tool-policy evaluation
- Permission policy evaluation, including Plan/Goal Bash prompts
- Immutable
.pi/plan/*.mdand.pi/goal/*.mdartifact writer,plan_approvalsbroker, and startup interruption fence - Selectable shell catalog, identity validation, streamed output, and process tree shutdown
- Plugin registry/install/lifecycle services
- Contribution registration bookkeeping (with TS side)
- Persistence adapters (sessions/settings metadata,
plan_approvalsartifact and execution fields, and the durable notification inbox) - Secrets storage integration points
- Audit logging for sensitive actions
3. Non-responsibilities
- LLM provider SDKs
- agent turn graph/orchestration
- React rendering
- marketplace web frontend
4. Suggested crate layout
crates/host-core/
src/
main.rs # sidecar entry
lib.rs
rpc/
tools/
permissions/
workspace/
plugins/
storage/
notifications.rs
secrets/
audit/
util/5. RPC transport
Frozen: stdio JSON-RPC NDJSON with Electron main (see 06-host-rpc-protocol.md).
5a. Control-pipe resource isolation
The host's stdin reader and stdout writer each run on one named, dedicated OS thread. They must not use Tokio's tokio::io::{stdin, stdout} adapters: those adapters obtain a blocking-pool worker for each operation, and an exhausted OS thread budget can otherwise panic the host before a structured error reaches Electron. The dedicated threads retry EINTR and transient EAGAIN/EWOULDBLOCK (errno 11 or 35) with a short delay, preserve NDJSON framing, and stop only on EOF or an unrecoverable pipe error. Failure to create either control thread is a startup error rather than an unhandled panic.
The best-effort login-shell PATH probe follows the same rule: a failed probe thread creation returns None, so Bash falls back to the host environment.
5b. RPC surface (logical)
Domains:
app.*workspace.*tools.*permissions.*plugins.*session.*(adapter level)notification.*(adapter level; durable inbox)plans.*(approval broker and recovery)shell.*(catalog and default selection)settings.*(adapter level)secrets.*audit.*
Example:
tools.execute
plans.resolve
plans.pending
permissions.request
plugins.list
plugins.load_dev
workspace.set
secrets.set
notification.list6. Security invariants
- No unchecked path escape from workspace tools or
.pi/plan/*.md; an explicit outside path is resolved only after host permission evaluation - Host resolves the durable session mode; request-supplied mode is never authoritative
- Plan and Goal deny Write/Edit/plugin/unknown tools before permission evaluation
- Plan and Goal Bash follow the durable permission mode and may mutate under Auto
- Plan and Goal artifact bytes, path, hash, size, and approval/execution identity are host-authenticated
- Plan/Goal approval is host-authenticated, durable, and atomic before Agent entry
- Effective shell ID/dialect is checked before spawn; settings reject unavailable/wrong-platform IDs, and a persisted unavailable choice falls back only during catalog selection
- Secrets never returned to renderer logs
- Crash in plugin, shell, or approval path fails closed and does not grant or replay execution
7. Packaging
- build target per platform
- ship binary next to Electron resources
- versioned protocol handshake with Electron/Node
8. MVP acceptance
- Electron can start Rust host sidecar
- healthcheck RPC succeeds
- at least one tool path executes through Rust
- permission deny path works
- unseen completed/failed turns create exactly one durable notification through the
session.endTurntransaction; results already visible in the focused current chat and aborted turns create none - a durable Plan or Goal session cannot authorize Write/Edit/plugin tools through a conflicting request mode, and Plan/Goal Bash follows the resolved permission mode
- SubmitPlan writes exact Markdown bytes to a new
.pi/plan/*.mdartifact and stores durable path/hash/size plus structured title/question inplan_approvals; approval is approve/reject-only, session/turn/version scoped, and expires at 30 absolute minutes withPLAN_APPROVAL_TIMEOUT - Pending/queued/running Plan or Goal work is interrupted on host restart with no replay; approved interruptions leave the session Agent
- Shell selection/fallback, stale ID/dialect rejection, stdout/stderr streaming, 60s timeout, bounded override, and process-tree abort are host-enforced