Skip to content

macOS app — claimward-vpn-app-osx

A menu-bar (tray) app written in Go whose entire user interface is a Svelte single-page app rendered in a webview.

Design

 claimward-app (tray process)
 ├─ systray            menu: status / Connect / Disconnect / Open / Quit
 ├─ uiserver           loopback HTTP: embedded Svelte SPA + token-guarded JSON API
 └─ appcore            OIDC login, enroll, drive the helper
        │ spawns "ui" subprocess          │ Unix socket (JSON)
        ▼                                 ▼
   webview (WKWebView)              claimward-helper (root LaunchDaemon)
   renders the Svelte UI            wireguard-go: utun up/down

The tray process owns all state and serves both the UI and a small JSON API on 127.0.0.1. The webview is a thin window pointed at that loopback URL. Tunnel setup needs root, so it lives in a separate privileged helper; the UI app is unprivileged.

Build

cd frontend && npm install && npm run build && cd ..   # build the Svelte UI
CGO_ENABLED=1 go build -o bin/claimward-app    ./cmd/claimward-app
CGO_ENABLED=1 go build -o bin/claimward-helper  ./cmd/claimward-helper

Configure

~/Library/Application Support/Claimward/config.json (GitHub is the default):

{
  "server_url": "https://vpn.example.com",
  "provider": "github",
  "github_client_id": "Iv1.0123456789abcdef"
}

The app uses the GitHub device flow: clicking Connect shows a code to enter at the displayed URL. For OIDC, set "provider": "oidc" with "oidc_issuer" / "oidc_client_id".

Install the helper and run

sudo ./scripts/install-helper.sh   # installs the root LaunchDaemon
./bin/claimward-app                # tray app → click Connect

Hardening before shipping

The MVP helper socket is 0666; tighten to a dedicated group + 0660 with peer-credential checks, move session tokens to the Keychain, and ship a signed, notarized .app.