Native macOS tray app — sovereign-compute local LLM runner. One symbol, six routing surfaces. See
forge.lthn.sh/lthn/desktop.
Binary: lthn
Status: scaffold — Lit primitives wired from Lethean-5; Go services stubbed.
Licence: EUPL-1.2
A native macOS tray icon + 400×560 popover panel that runs local LLM inference on Apple Silicon. The tray is the process; expansion windows (chat, settings, benchmark, telemetry) are transient surfaces anchored to the tray-process. Closing all windows does NOT quit the app.
The product story: sovereign compute, single-watt — AI on the user's own hardware, no cloud round-trip, airplane-mode capable.
See the canonical spec:
plans/project/lthn/desktop/RFC.first-release.md— first-release scope (P0 tray + the v0 to v1.0 trajectory)plans/project/lthn/desktop/DESIGN-BRIEF.md— design canon (Lethean-4 visual + Lethean-5 Lit port)
lthn/desktop/
├── cmd/lthn/ — main binary entrypoint (tray-rooted, no quit-on-last-close)
├── pkg/tray/ — NSStatusItem + popover anchor + window-spawn router
├── pkg/runner/ — go-mlx inference adapter (start / stop / generate, signals)
├── pkg/telemetry/ — powermetrics / IOReport sampler (watts + memory readings)
├── frontend/ — Vite + Lit
│ ├── src/
│ │ ├── tokens.css — Lethean-4 design tokens (OKLCH, Vi-anchored)
│ │ ├── main.js — entry; mounts windows by ?surface=... URL param
│ │ └── lit/ — Lit primitives + windows from Lethean-5
│ │ ├── chrome.js — renderChrome() + 9 primitives
│ │ ├── chat-window.js — E0 chat
│ │ ├── ops-windows.js — E1 welcome / settings / model browser
│ │ ├── obs-windows.js — E2 benchmark / logs / telemetry
│ │ └── ext-windows.js — E3 + E4 integrations / tools / network / fine-tune / fleet
│ ├── index.html
│ ├── package.json
│ └── vite.config.js
├── build/{darwin,linux,windows}/ — platform build configs (codesigning, packaging)
├── docs/
└── Taskfile.yml
# Frontend-only — Lit windows on the design canvas, no Go runtime:
cd frontend && npm install && npm run dev
# → http://localhost:9245/ (mount any window via ?surface=chat etc.)
# Full hot-reload dev loop — Wails app + Vite + Go rebuild watcher:
wails3 dev
# .app launches on first build cycle; menubar icon = lthn-glyph
# Edits to go/**/*.go and frontend/**/*.ts trigger automatic rebuild.
# One-shot release build (auto-detect OS):
task build # produces bin/lthn{.app,.exe,}
task package # produces a distributable bundle
# Targeted per-OS:
task darwin:build # macOS .app
task linux:build # Linux ELF
task windows:build # Windows .exe| Tool | Min version | Purpose |
|---|---|---|
| Go | 1.26.0 | backend |
| Node | 22 | frontend + Vite |
wails3 |
v3.0.0-alpha.91 | CLI scaffold + dev orchestrator |
task (go-task) |
3.x | build runner |
# One-time tool install:
go install github.com/wailsapp/wails/v3/cmd/wails3@latest
go install github.com/go-task/task/v3/cmd/task@latest
# Linux only — webkit + GTK:
sudo apt-get install libgtk-3-dev libwebkit2gtk-4.1-devGitHub Actions builds darwin-arm64, linux-amd64, windows-amd64 on every push to main / dev and uploads the binaries as workflow artifacts (7-day retention).
Pushing a v* tag creates a GitHub Release with the artifacts attached:
git tag v0.1.0 && git push github v0.1.0
# → .github/workflows/build.yml runs the matrix, then `release` job
# attaches lthn-darwin-arm64.zip + lthn-linux-amd64 + lthn-windows-amd64.exeWorkflow definition: .github/workflows/build.yml.
The systray app detects fresh installs via firstlaunch.Detect() — checks ~/Lethean/conf/lthn.yaml, the state DB, and any configured routes. When all three are absent, the welcome wizard opens on top of the tray:
- Model directory — where models will live (default
~/.lthn/models/) - First model — Gemma 4 E2B (Lethean-recommended starter)
- Connect — opt-in OpenAI-compatible endpoint wiring for Claude Code / OpenCode / Codex
The final "Finish" / "Skip for now" buttons call ConfigService.Set("welcome.completed", "true") and open the settings window, so the user can change their mind without re-running the wizard.
The lthn binary is glue. Real capability comes from:
dappco.re/go/core— Core primitives (Options, Config, Service, Action)dappco.re/go/gui— CoreGUI: window / tray / app lifecycle (wraps the upstream GUI substrate)dappco.re/go/mlx— Apple Metal native inference enginedappco.re/go/store— SQLite KV persistencedappco.re/go/io— filesystem sandboxdappco.re/go/inference/state— portable KV-as-video-file primitive (warm-resume across sessions / machines)
No new library code lives in this repo — that's the architectural rule. If a capability needs new behaviour, it lives in the canonical home for that capability.
| Release | Adds |
|---|---|
| v0 | macOS / Apple Silicon (Apple Metal — go-mlx) |
| v0.2 | AMD HIP (go-rocm has the custom kernels ready) |
| v0.3 | NVIDIA CUDA |
| v0.5 | Heterogeneous multi-card — link every card the user owns |
| v0.7 | Cross-machine federated compute (LetherNet) |
| v1.0 | External API overflow via go-ratelimit |
The USP: one runner, every card the user owns. Mac + AMD + NVIDIA in the same logical flow. When local saturates, overflow routes to a chosen external provider; cloud is the explicit fallback, not the default.
CLAUDE.md— repo-local agent contextforge.lthn.sh/core/ide— the library Lethean Desktop consumesforge.lthn.sh/core/gui— CoreGUI (window/tray substrate)forge.lthn.sh/core/go-mlx— Apple Metal inference engine