Packaging and Desktop Shell
Status: proposed — consolidated 2026-09-01 from material already designed in 001-system-overview.md, 050-ui-dashboard.md, 060-configuration.md, 061-security.md, and 070-roadmap.md. No new design decisions were made here, except where flagged as a new Open Question below.
Scope
Distribution of the single pam binary (cargo-binstall, release process)
in its two roles — central orchestrator (pam serve, needs a PostgreSQL
DSN) and worker (pam worker) — for the supported worker platforms:
macOS (primary) and Linux (secondary); Windows out of scope, mobile
excluded (ADR-011/ADR-012). Includes the confirmed Tauri v2 desktop
shell (ADR-006; Electron recorded as fallback).
Distribution
One pam binary, two roles selected by subcommand — pam serve (central
orchestrator: axum HTTP API + embedded SPA + run dispatch + worker
registry, needs a reachable PostgreSQL DSN) and pam worker (outbound-only
registration, no inbound listener, supervises tmux sessions on the host’s
dedicated pam socket) — the same binary can run both roles on one
machine to recover a single-machine deployment
(001-system-overview.md § Distribution Topology).
Each role reads its own platform data dir (config.toml/token for the
orchestrator, worker.toml/provider catalog/per-run logs for a worker),
resolved via the directories crate — full paths and precedence rules
are 060-configuration.md § Platform Data Dir, not
duplicated here.
Platform Targets
Accepted 2026-08-29 (ADR-011): worker hosts target Unix only — macOS
primary, Linux secondary; Windows is out of scope (no tmux), mobile is
excluded. Code and CI stay platform-neutral (CI already builds and tests
on Linux, ubuntu-latest); the interactive tmux gates (spawn, reconcile,
attach) remain macOS-verified until a Linux environment is set up for
manual verification (OQ-9, 071-risks-and-open-questions.md).
The central orchestrator itself has no platform restriction beyond
PostgreSQL reachability — ADR-011 binds worker hosts specifically, not
pam serve.
Production Build
trunk build --release compiles the Leptos/WASM SPA into web/dist/;
rust-embed includes that directory in the compiled binary; Axum serves
the embedded files at / (index.html → /, assets/* → /assets/*,
all non-API routes fall back to index.html for SPA routing) while API
routes stay under /api/v1 unaffected
(050-ui-dashboard.md § Production Build). This is
what the Phase 3 embedding test proves: renaming web/dist away on disk
after the binary is built still serves the SPA, showing embedding rather
than static-file serving
(063-testing-strategy.md § Test Pyramid by
Phase).
Desktop Shell (Tauri v2)
Accepted 2026-08-29 (ADR-006): a Tauri v2 shell around the same embedded SPA, confirmed as a monitoring/viewing thin client that never executes work — every user action is forwarded as an API request only, and the orchestrator + its workers perform all actual work (050-ui-dashboard.md § Desktop Shell). Target platforms mirror the worker split: macOS + Linux desktops, Windows out of scope, mobile excluded; the shell is platform-independent by construction since it just loads the orchestrator URL.
Rationale for Tauri v2 over Electron: web-first (the embedded SPA already works standalone) suffices for single-operator local use; OS webview reuse costs 5-10 MB versus a bundled Chromium’s ~150 MB; the shell reuses the existing Rust core with no Node.js sidecar; and the security surface drops a Node.js runtime entirely, which matters because the shell needs no local capabilities. Electron is a recorded fallback for an ecosystem gap (e.g. a macOS auto-update issue), not a current alternative — the SPA uses only standard web APIs, so either shell stays viable without a rewrite.
Security posture (least privilege): Tauri v2 capabilities ship with
everything disabled — no invoke, no Electron-style ipcRenderer/
remote equivalents used from SPA code
(061-security.md § Shell Least-Privilege). This follows
directly from the monitoring-only role: a client that never executes work
locally needs no filesystem or shell capability to disable-by-exception,
so the default-disabled posture is the complete posture, not a starting
point to relax later.
Implementation is scheduled for Phase 6, not yet built: the Tauri app
loads PAM_API_URL, optionally spawns pam serve as a child process
(sidecar mode, process management only — the sidecar is still the same
pam serve binary, not shell-specific code), bridges IPC for tray icon /
notifications / window management, and reuses the Rust core directly
(050-ui-dashboard.md § Implementation Note).
Phase 6 Packaging Gate
070-roadmap.md § Phase 6 scopes two deliverables:
implementing the Tauri v2 shell as a wrapper (sidecar daemon or linked
core; the web UI codebase stays single-source, no shell-specific fork),
and cargo-binstall metadata plus release polish (app icon, naming,
packaging defaults). The phase gate: a built .app/dmg launches on the
dev Mac, starts or attaches to the same core, and passes the Phase 3
embedding gate through the shell webview; cargo-binstall metadata is
present and validates locally via a dry run (no network publish
required) — cross-ref 063-testing-strategy.md
§ Test Pyramid by Phase for how this gate fits the overall test pyramid.
As of this consolidation, no cargo-binstall metadata exists yet in the
workspace Cargo.toml files — Phase 6 has not started in code.
Open Questions
- None registered from this consolidation — no contradiction or
undocumented decision was found across
001-system-overview.md,
050-ui-dashboard.md,
060-configuration.md,
061-security.md, and
070-roadmap.md. The earlier apparent ADR-006/ADR-011/
ADR-012 discrepancy in 003-architecture-decisions.md
was a false alarm from a plain-text search of that file — the ADR
index’s actual content lives in an embedded SVG table
(
docs/assets/tables-src/003-architecture-decisions/table-01.md), which does record all three ADRs correctly; no doc content was found missing.