Skip to content

Latest commit

 

History

History
40 lines (34 loc) · 11.9 KB

README.md

File metadata and controls

40 lines (34 loc) · 11.9 KB

Architecture Decision Records

Baseline: 9bb20d860 · Reviewed: 2026-10-07

Numbers are never reused. 0015 was skipped; the gap is intentional and the number stays unused.

Status vocabulary. Proposed = recorded design, implementation may be partial. Accepted = adopted by the repository. Superseded by NNNN = replaced, kept for history.

These records capture repository design decisions and their implementation context. An Accepted status means the repository adopted the stated design; it is not evidence of operational effectiveness, compliance, certification, formal risk acceptance, or continued production use.

ADR Status Decision scope
0001 — Development Mode restart recovery uses replacement attempts Accepted Restart recovery preserves worktree/artifact state and continues through a new attempt rather than resuming an interrupted provider stream.
0002 — Development cloud authorization uses ChatOptions.AdditionalProperties Accepted Version-aware carrier and enforcement seam for Development Mode cloud authorization.
0003 — Six-plan implementation scope and hardware evidence decisions Accepted Operator-supplied scope decisions, unavailable-hardware evidence handling, and embedding width.
0004 — Docker permitted for Development Mode execution only, as a stopgap ahead of MXC Accepted Narrows the runtime-rearchitecture epic's "no Docker anywhere" decision to "no Docker on the inference path" and unblocks its container-execution slices.
0005 — Training runs in a uv-managed Python runtime, holds the node exclusively, and lands in a thin provider project Accepted Training semantics live in Python behind a structured stdio contract; a run holds a training marker plus the runtime-mutation lease (never the GPU load-admission semaphore); a thin Providers.Training project owns only uv/venv/subprocess mechanics.
0006 — Agentic MCP keys capture bounded operator-equivalent execution authority Accepted Explicit inbound authority, durable capture across restart and rotation, agentic-root tool adaptation, and strict audit-before-invocation without granting the Operator role.
0007 — The sandbox execution substrate is capability-declared, and the backend is selected, never named Accepted A consumer declares execution requirements rather than naming a backend; a selector resolves one that can honour them and fails closed when none can. Amends ADR 0004 Decision §1 only; §2–§5 stand.
0008 — External integrations invoke a saved agent through a keyed, loopback-only surface inside /api/local/v1 Accepted An external caller invokes a saved agent through hand-mapped integration-api/… routes inside /api/local/v1 with their own xeint_ keys; admission is one BEGIN IMMEDIATE transaction bounded per node and per principal, runs are unattended and fail closed, and V1 is explicitly loopback-only.
0009 — A 409 uses the global conflict envelope, unless the refusal is an operational block with its own typed body Accepted A thrown typed domain exception answers with the one ConflictProblemDetails envelope; a refusal the service reports as a returned value — a runtime, process, build or prerequisite standing in the way — answers with a per-feature typed *BlockedResponse, built in one place per family.
0010 — User-managed application containers from the XE catalog are a separate consumer class with their own runtime layer Accepted (2026-10-07) Curated, digest-pinned application containers run on an engine-owned runtime layer beside the sandbox SPI, from an XE-owned manifest whose schema is the allow-list; ADR 0004 §5 is replaced for this class alone, and ADR 0004 §2–§4 and ADR 0007 stand.
0011 — An application container reaches the node's own inference surface through one guarded, non-loopback listener Accepted (2026-10-07) The engine opens ONE extra Kestrel listener on a LAN-facing address, serving only /llm/v1/* behind a same-host peer guard and a mandatory per-instance token; llama-server keeps binding loopback and /api/local/v1 stays loopback-only.
0012 — Local audio transcription runs on a supervised whisper.cpp daemon, captures in the browser, and never persists audio Accepted whisper.cpp is a third independent supervised runtime; capture is browser-first with Windows per-application capture as a later slice; speakers are attributed by channel (You/Others), never clustered; the Linux CUDA lane is a managed source build; and audio is never written to the database, enforced by an architecture test, a single-owner temp slot and a streaming upload.
0013 — The native desktop is a separate thin shell process that supervises the engine, never a host it links against Accepted A thin Avalonia + NativeWebView shell with no engine project references; engine ownership is attach-or-own, carried by a named-pipe parent lifetime with bounded shutdown on both sides; one shell per data root with activation; first close offers Keep in tray / Quit / Cancel with a remembered preference; Linux runs a restricted GTK document policy with microphone-only consent while Windows relies on WebView2 defaults; browser, headless, MCP-only and CLI modes bypass the window.
0014 — Update channels are app state, and Development builds ship on their own Velopack feed Accepted The selected update channel is node-settings state (Stable / Preview / Development) passed explicitly on every check, never the installed package's sticky channel; Development snapshots of develop publish daily as dev/<version> prereleases on their own win-dev / linux-dev Velopack feeds, which a Stable or Preview feed read skips silently; the app ships a paginating update source because the stock GitHub source reads only the 10 newest releases; versions extend the anchor tag with dots and never a second hyphen; updates stay forward-only.
0016 — Managed Python is one shared uv layer in its own provider project, with one pinned uv and one XE-owned toolchain store Accepted A leaf-like Providers.Python (references Providers.Abstractions and Providers.ProcessSupervision; consumed by Providers.Training and Client.Application) owns uv acquisition, the uv pin, the uv environment allowlist and the scrubbed runner; uv is pinned and digest-verified at run time, never bundled; uv, CPython and the uv cache move to one machine-global store with no automatic pruning while feature environments stay in their roots; each environment carries an explicit identity. Amends ADR 0005 Decision §3 only; lands across slices M1–M4.
0017 — Web access is two operator-enabled built-in tools, and a graph reaches the web only through an allowlisted fetch Accepted (2026-10-07) web_search (DuckDuckGo HTML default, operator SearXNG URL) and web_fetch are Network tools behind an off-by-default node setting; every fetch hop passes CustomToolSsrfGuard; output is fenced as untrusted and consented to per request and reviewed by the user before it enters context (per-conversation auto mode skips both, behind a one-time notice); graph Agent nodes never get them, and a Tool node may fetch only against a URL-prefix allowlist, the single exception to the ReadLocal-only rule (amended 2026-10-15: ADR 0021 adds a second).
0018 — The persisted node key is a random master key wrapped by the admin password and a recovery code, and a locked node serves only an unlock page Accepted (2026-10-07) node.key becomes a v2 JSON vault (PBKDF2-SHA512 password wrap plus HKDF recovery-code wrap of the unchanged operator secret) that is mandatory, with a one-time automatic wrap of a legacy key; the vault passphrase is the admin password; a locked node runs only a pre-host that serves the SPA and the unlock routes while the whole real host waits; operator-supplied secrets, the HKDF derivations and existing ciphertext are untouched.
0019 — Preview sandbox mechanisms are operator-enabled, and on Windows the AppContainer boundary satisfies the filesystem floor Accepted (2026-10-07) Launch mechanisms carry a maturity (Stable / Preview); a Preview mechanism is measured always but serves a role only while the Operator-only ExecutionPreviewsEnabled node setting is on, read per call; on Windows the MXC AppContainer boundary (a launch mechanism inside the process provider) then satisfies the Filesystem floor for floor workloads only (run_python, Sandboxed MCP; AgentHome stays on the plain process path), callers compose paths from SandboxHandle.IsolatedPaths, and the role reports PreviewIsolated, never Isolated. Explicit deny roots never shadow a grant; DACL residue is swept at startup. Records timeout-only execution on Windows as accepted residual risk.
0020 — The sandbox security profile is a stored, Operator-only node setting that promotes declared preferences to preconditions Accepted (2026-10-08) A stored low / high / pending node setting (null = backfillable; effective high only when stored high), read per call. One rule, SandboxSecurityProfilePolicy, shared by enforcement and the capability summary: under high, egress denial, a Stable filesystem boundary and resource ceilings become preconditions exactly where a workload declares them; floors are untouched and the two RequireEgressDenial keys stay tighten-only above it. Chosen in the first-run chain after external access, backfilled to low on upgrade, never agent-writable, confirmed on high-to-low. Amends the configuration-only ruling on tightening switches for the profile only; high refuses every executing workload on today's Windows and on Linux without a systemd user scope.
0021 — Unattended actions are flagged custom tools inside the graph Tool node, audited before the side effect, with a per-tool private-host egress exception Accepted (2026-10-15) A custom tool of either kind may carry an Operator-set AllowUnattended flag (acknowledged, Fixed mode only); a graph definition may carry an Operator-set AllowsActions flag that import never sets; a Tool node then runs the tool through a third admission branch that writes a metadata-only ApprovalDecision row through the generalised ADR 0006 recorder before the side effect, fails closed on audit failure and attempts once. An HttpFetch tool may list exact private hosts, enforced on both SSRF layers, with loopback, link-local, metadata, multicast and unspecified always denied. Runs record their trigger source and principal, and a definition limits which sources may start it. Unattended host commands are recorded as the highest-risk capability. Amends ADR 0006, 0008, 0013 and 0017.

For the baseline technical/security narrative and its explicit evidence limitations, see the Technical/Security Architecture Dossier. For what the execution substrate behind ADR 0004 and ADR 0007 actually enforces today, and what it still does not, see the sandbox threat model.