Wails v3 desktop app: scaffold, shell features, token mgmt, auto-update, CI
Problem: the Oikos control room was browser-only — no native desktop
experience (system tray, notifications, keychain-persisted auth).
Change: add a Wails v3 thin-shell desktop app at cmd/desktop/ that embeds
the existing SPA in a webview. The Go side is ~380 lines — no bundled
server, no Postgres connection. It reads auth from the OS keychain,
injects it into the SPA on load, and the SPA talks HTTPS to the homelab
same as a browser.
Phase 1.0 — Scaffold + window:
- Embed web/dist/ into the Wails binary
- Inject window.__OIKOS_CONFIG__ with keychain-stored apiUrl + token
- 1400×900 window, min 1024×700
- System tray: Open/Quit, click toggles window
Phase 1.1 — Native shell:
- Poll /api/v1/dashboard/summary every 30s; osascript notification
when approvals or critical signals increase
- Save/restore window position to ~/.config/oikos/window.json
- EnableAutoStart/DisableAutoStart — macOS LaunchAgent plist
Phase 1.2 — Token management:
- Config.svelte calls window.wails.Call.ByName('SaveConfig') after
successful connection — persists to OS keychain
- ConfigService binds SaveConfig, ClearConfig, EnableAutoStart,
DisableAutoStart to the Wails runtime
Phase 1.3 — Auto-update:
- Poll Gitea releases API every 6h, compare semver, show dialog
- 'Check for Updates' tray menu item triggers immediate poll
Phase 1.4 — Distribution:
- macOS entitlements.plist: network client + keychain access
- .gitea/workflows/desktop.yml: CI builds macOS arm64 + Linux amd64
on 'desktop-*' / 'v*' tags, attaches artifacts to release
- Makefile: desktop (build), desktop-package (build + zip/tar.gz)
- CONTRIBUTING.md: documented desktop app + commands
Risk: low. Wails v3 alpha API may shift; the Go glue is ~380 lines and
trivially portable. The desktop app is additive — zero changes to the
existing server or SPA logic. No config mutation, no infrastructure
impact.
Verification: go build, go vet, go mod tidy all pass.
This commit is contained in:
@@ -9,6 +9,7 @@ import { getToken, isOIDCConfigured } from './oidc'
|
||||
export interface OikosConfig {
|
||||
apiUrl: string // e.g. "https://oikos.hubris.network", or "" for same-origin
|
||||
token?: string // bearer token for auth
|
||||
isDesktop?: boolean // true when running inside the Wails desktop app
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
error = res.status === 401 ? 'Invalid token' : `Server responded ${res.status}`
|
||||
return
|
||||
}
|
||||
saveToDesktop()
|
||||
onConnected()
|
||||
} catch (e) {
|
||||
error = 'Could not reach server — check the URL'
|
||||
@@ -51,6 +52,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
function saveToDesktop() {
|
||||
const wails = (window as any).wails
|
||||
if (!wails?.Call?.ByName) return
|
||||
try {
|
||||
wails.Call.ByName('SaveConfig', apiUrl.trim(), token.trim())
|
||||
} catch {
|
||||
// ignore — optional desktop-only path
|
||||
}
|
||||
}
|
||||
|
||||
async function loginWithAuthentik() {
|
||||
error = ''
|
||||
oidcLoggingIn = true
|
||||
|
||||
Reference in New Issue
Block a user