# Architecture decision record — HOLD 0.2.0, 2026-10-15

## Decision

Use Electron 44.7.2 + React 19.3.1 + TypeScript for the first Windows desktop build, a dependency-light Node core, and a pinned llama.cpp b11429 native sidecar. Native mobile uses Expo/React Native + llama.rn. The marketing site is a separate static build with no private app state. Versions were retrieved from the live package registry and locked during this build.

## Rationale and alternatives

The available machine is Windows x64 with Node 24, 32 GB RAM, Ryzen 5950X and RTX4080. Electron provides a reproducible Windows packaging path, sandboxed renderer, OS dialogs, and OS-backed safeStorage. Its Chromium footprint is a tradeoff. Tauri offers a smaller shell but requires Rust and Microsoft C++ build tooling, while adding no automatic inference or mobile-model compatibility. A fully native desktop UI would increase duplicated UI and storage effort. These are engineering tradeoffs, not a claim that Electron is universally superior.

[Electron security](https://www.electronjs.org/docs/latest/tutorial/security), [context isolation](https://www.electronjs.org/docs/latest/tutorial/context-isolation), and [Tauri prerequisites](https://tauri.app/start/prerequisites/) informed the decision. The desktop has no mandatory Ollama installation or service; HOLD owns its engine lifecycle. llama.cpp was chosen for direct GGUF inference and CPU portability. CPU remains the default. A separately pinned Vulkan runtime enables explicit GPU selection; positive layer-offload evidence is required before it reports ready. CPU and Vulkan were measured on the available RTX 4080 computer.

Native mobile needs its own memory, lifecycle, speech, and thermal boundaries. A WebView around the desktop API would fail the on-device requirement. See `mobile.md` for the native runtime comparison, including Google AI Edge's current LiteRT-LM migration. macOS/iOS verification needs a Mac; it is not inferred from Windows success.

## Data and trust boundaries

1. A sandboxed renderer displays local packaged assets. It has no Node globals, untrusted HTML renderer, remote frames, or arbitrary IPC method access.
2. Electron main validates sender frame and action payloads, owns file dialogs and model lifecycle, and supplies narrowly defined operations.
3. LocalStore serializes transactions and atomically replaces the data file, preserving a prior revision. OS encryption is injected by the desktop shell. Exports are documented, unencrypted, and revoke file/tool grants on import.
4. llama.cpp is an owned child process with an ephemeral loopback port and random secret. The renderer cannot call it directly. CPU, Vulkan and CUDA bundles are pinned and verified; GPU readiness requires actual offloaded layers. There is no internet inference fallback. An explicitly chosen hosted-model connection is a separate Power workflow with a native disclosure and approval.
5. ModelDownloads accepts pinned catalog entries, approved HTTPS distribution redirects, exact sizes, GGUF headers, and full SHA-256 checks. Partial files support range resume. Local GGUF import uses a native picker, a bounded verified copy and explicit unreviewed-model labeling. Publisher update checks require consent and retain the current model until an independently verified replacement is selected.
6. ProjectTools enforce approved roots and reject escapes and sensitive paths. Reads and proposed edits are bounded; immutable one-shot proposals require review and stale-content checks. They are not a general OS sandbox.
7. Agent tasks use a bounded JSON action protocol, a durable queue, explicit grants and aggregate step/time/output/delegation budgets. Permissions come from persisted user configuration, never from model output. Parallel independent work and sequential delegation share one inference slot. Approved reads, reviewed edits/checkpoints, constrained HTTPS reads, selected MCP tools and fixed isolated test profiles use separate brokers. There is no unrestricted host shell or autonomous paid-agent execution.
8. Project retrieval and saved-conversation search use a separately downloaded pinned local embedding model. Indexes are encrypted, bounded and invalidated on source edits, deletion, grant changes and memory disable. Results retain source references; saved-chat results are not silently inserted into prompts.
9. Phone pairing is opt-in pinned TLS with an expiring one-time challenge, mutual key proof, signed bounded requests, session rotation, replay protection and revocation. OS-protected device records contain separate chat/model and saved-agent grants. Phone tasks require a second native desktop approval, use opaque owner-scoped handles, revalidate configuration after approval, and expose only the approved task's bounded result. See [pairing.md](/reference/docs/pairing.md).
10. Code execution runs only fixed Node profiles inside an owned local Linux container. The job receives copied reviewed sources, no original project mount, no network, a read-only root, unprivileged identity and CPU/RAM/process/time/output limits. Pre-start inspection and durable cleanup leases fail closed. See [isolated-execution-v2.md](/reference/docs/isolated-execution-v2.md).
11. Optional Power connections keep credentials in OS-protected main-process storage. Endpoint selection, disclosure, approval, quotas and cancellation are explicit. A controlled real hosted-model endpoint is tested; provider-funded workloads and commercial settlement remain unavailable. Network is a transparent future extension, never silently active hardware sharing.
12. Native mobile uses local SQLite, platform-protected credentials, offline model/vision/speech engines and native file/camera/share workflows. Android backup and ML Kit scanner telemetry paths are disabled. Android emulator evidence and unbuilt iOS code remain clearly distinguished.

## Source map and acceptance tests

- `apps/desktop/electron/main.js`: secure window, lifecycle, OS encryption, trusted IPC; exercised by `scripts/test-desktop.mjs`.
- `apps/desktop/electron/controller.js`: consumer workflows and validation; `tests/desktop/controller.test.js`.
- `apps/desktop/src`: responsive UI; native-window screenshots and interaction assertions.
- `packages/core/storage.js`: persistence/migrations/import; `tests/core/storage.test.js`.
- `packages/core/downloads.js`: verified acquisition; download tests include interruption/tamper/redirect behavior.
- `packages/core/projects.js`: file boundaries, proposals and rollback; traversal/junction/hardlink/stale approval tests.
- `packages/core/agent-execution.js`: bounded durable orchestration; delegation, cancellation, restart, budget and real local-agent tests.
- `packages/pairing`, `packages/retrieval`, `packages/git`, `packages/execution`, `packages/power`: independent trust boundaries with focused regression and integration evidence.
- `scripts/verify-inference.mjs`: actual model download, model switch, authenticated streaming, loopback-only transport and benchmarks.
- `apps/mobile`, `apps/website`: independently locked native/mobile and static deliverables, each with its own verification notes.

## Deliberate limitations

Desktop storage uses serialized atomic JSON with a 128 MiB input limit and rewrites each committed revision. The UI pages histories in groups of 60; a 1,000-message conversation is exercised, but multi-year datasets and prolonged production load are not certified. A future SQLite migration should preserve the current transaction and grant-revocation semantics. Mobile already uses SQLite.

The authenticated model sidecar is not a strong OS sandbox for malicious native model parsers. Curated verified weights and explicit import warnings reduce accidental exposure; arbitrary untrusted models must not be treated as safe. Container isolation protects the fixed code-test workflow only, not arbitrary GPU marketplace workloads. Supported-platform claims remain bound to actual hardware evidence. Owner signing/publication, physical-device validation and independent release security review are separate release gates. Future Network operations, payment settlement and general-purpose terminals are not implemented.
