## Summary Adds `tools/hangar` — a macOS desktop control panel for working on Fleet locally: branch management, `fleet serve` orchestration, log tail, dev MySQL backup/restore, `fleetctl`, GitOps, and `osquery-perf`, all in one window. Built with **Go + [Wails 3](https://v3alpha.wails.io)** — the backend is plain Go (`os/exec`, `syscall`, goroutines) so Fleet engineers can contribute to it; only the desktop shell is Wails. The `internal/` packages are pure and unit-tested. ### History note Hangar started as a Rust/Tauri app. It was ported to Go, and **the Go port is now the canonical `tools/hangar`**. The original Rust/Tauri implementation has been removed from the monorepo (preserved in a standalone repo) — so although this branch's earlier commits add and then replace the Rust app, the net diff is just the Go app at `tools/hangar`. The bundle identifier is `com.fleetdm.fleet-hangar`, matching the original app so existing settings carry over. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops > No `changes/` file: `tools/` is contributor tooling, not a user-visible Fleet change. No DB migrations, no Fleet config settings, no fleetd/orbit changes. ## Testing - [x] Added/updated automated tests (Go unit tests across `internal/`, including a path-traversal regression for backup deletion) - [x] QA'd all new/changed functionality manually ## Test plan - [x] `cd tools/hangar && task dev` launches the app (live-reload) - [x] `task build` produces `bin/fleet-hangar`; `go test ./...` is green - [x] First-run gate discovers a local Fleet clone and runs dep checks - [x] Server tab can run the build chain and start `fleet serve` - [x] Git tab branch search finds an older branch (e.g. a stale `qa-*`) by name <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced Fleet Hangar, a comprehensive desktop application for Fleet development workflows, providing unified controls for server/database management, git operations, configuration, logging, and troubleshooting. * Added database backup management with metadata tracking. * Integrated process orchestration for development services (Docker, ngrok, Python). <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: George Karr <georgekarrv@users.noreply.github.com>
4.9 KiB
Fleet Hangar
A desktop control panel for Fleet contributors, built
with Go and Wails 3. Bundles the daily tasks of working on a
Fleet clone (checking out branches, building, running fleet serve, tailing logs, managing
the dev MySQL, driving fleetctl, applying GitOps repos, spinning up osquery-perf) into
one app. macOS-first.
Why Go? To match the rest of the repo so Fleet engineers can contribute to it. The
backend is plain Go (os/exec, syscall, goroutines); only the desktop shell is Wails.
Hangar began as a Rust/Tauri app; it was ported to Go and that port is now the canonical
implementation.
Architecture
internal/— all the logic, pure and unit-tested (each package takes explicit paths/timestamps so tests are hermetic):processes— spawn/log/lifecycle engine: child-process management, streamed log readers (level detection, secret scrubbing, on-disk rotation, in-memory ring),running.jsoncrash-recovery, SIGTERM→SIGKILL on process groups, docker-compose orchestration, TLS probesettings,gitrepo,db,gitops,fleetctl,deps,troubleshoot,perf,perfconfig— one per formersrc-tauri/src/*.rsmodulepaths(macOS dirs + path safety),shellpath(login-shell PATH warming),traymenu(tray menu model)
services/— thin Wails-bound service structs; each exported method is callable from the frontend. They resolve real paths and delegate tointernal/.main.go/tray.go/emitter.go— the native shell: app bootstrap, system tray, and window lifecycle (hide-to-tray, dock reopen, Cmd+Q→confirm).frontend/— the React + TypeScript UI (shared with the Rust app). The only Wails-specific glue issrc/lib/tauri.ts(theapi.*IPC layer over the generated bindings) andsrc/lib/events.ts(thelisten()adapter over Wails events).
Development
Requirements: Go (see go.mod), Node 24+, and the
Wails 3 prerequisites. Install the
CLIs once:
go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha.98
go install github.com/go-task/task/v3/cmd/task@latest
Then, from this directory:
task dev # live-reload dev mode (Vite + Go)
task build # type-check + production build -> bin/fleet-hangar
task package # build + bundle + ad-hoc sign -> "bin/Fleet Hangar.app"
task dist # zip the existing .app into a shareable "bin/Fleet Hangar.zip"
go test ./... # backend unit tests
After changing any Go service signature, regenerate the TypeScript bindings (also run by
task build):
wails3 generate bindings -clean=true -ts
Notes
-
Names. The bundle is
Fleet Hangar.app(thePRODUCT_NAMETaskfile var) so Finder, Launchpad, and Spotlight show "Fleet Hangar". The executable inside staysfleet-hangar(theAPP_NAMEvar) — it's also reused for the-serverbinary and Docker image tags, where a space would break things. The bundle identifier iscom.fleetdm.fleet-hangar— the same ID the original Rust app used, so settings written by it carry over untouched. Settings live under~/Library/Application Support/<id>/and logs under~/Library/Logs/<id>/; DB backups live in<repo>/db-backups/. -
Distribution.
task packageproduces only an ad-hoc-signed.app;task distzips whatever bundle is inbin/intobin/Fleet Hangar.zip(viaditto, so the signature survives) for handoff. Two paths:- Quick / trusted teammate:
task package→task dist, then the recipient clears quarantine after unzipping:xattr -dr com.apple.quarantine "/path/to/Fleet Hangar.app"(an ad-hoc-signed app from another machine is otherwise blocked by Gatekeeper). - Clean install anywhere: configure
SIGN_IDENTITY+KEYCHAIN_PROFILEinbuild/darwin/Taskfile.yml, runtask darwin:sign:notarize(Developer ID sign + Apple notarization), thentask dist. No quarantine step needed.
distnever rebuilds, so running it aftersign:notarizepreserves the notarized signature. - Quick / trusted teammate:
Known issues
- Rare crash on display sleep/wake or monitor changes (upstream Wails v3 alpha bug, not
ours). Wails'
screen_darwin.gostores the autoreleased[NSString UTF8String]buffers for each screen'sid/namein a C struct and reads them from Go later, after the autorelease pool has drained — a use-after-free. On anApplicationDidChangeScreenParametersevent the dangling pointer tripsfatal error: invalid pointer found on stack. It's afatal error, not a panic, so it can't be recovered, and the handler is registered inside Wails so we can't intercept it. It's infrequent (one occurrence observed over an ~11h session). Until a fixed Wails alpha ships, relaunch the app if it happens. Reported upstream: wailsapp/wails#5556.