Skip to content

Latest commit

 

History

History
242 lines (197 loc) · 10.8 KB

File metadata and controls

242 lines (197 loc) · 10.8 KB

Agent Overflow

Desktop app for using coding agents (Claude Code, Codex) with a shared UX. Built on Go 1.26, Wails v3, and Svelte 5. Inspired by t3-code.

For friends helping test

This is a direct test release. There is no auto-update or code signing, so Gatekeeper / SmartScreen will warn on first launch. Install from the artifact bundle with install.sh; build from source only if you're developing the app.

When something breaks, runtime logs and the SQLite DB live in your platform's config directory (see Files & locations below). Zip the logs/ folder and send it with what you were doing.

Files & locations

The app writes everything under your OS's user-config directory:

Platform Config root
macOS ~/Library/Application Support/agent-overflow/
Linux ~/.config/agent-overflow/ (honors $XDG_CONFIG_HOME)
Windows via WSL WSL distro Linux config root, e.g. ~/.config/agent-overflow/
Windows launcher %APPDATA%\agent-overflow\ for launcher config/logs only

Inside that root: agent-overflow.db (SQLite — every thread, item, payload, attachment metadata), logs/ (NDJSON provider stdio capture when AGENT_OVERFLOW_DEBUG=provider, plus runtime logs), attachments/ (uploaded image / file bytes referenced by the DB), and settings.json.

Setup

Requires Go 1.26.6+, Node 24+, and pnpm 10+. On Linux, Wails v3 also needs libgtk-4-dev, libwebkitgtk-6.0-dev, pkg-config, and gcc (install via your distro's package manager; the GTK4 / WebKitGTK 6.0 stack ships on Ubuntu 23.04+ / Debian 13+).

make install    # installs wails3 CLI (via go.mod tool directive) + pnpm deps

Direct install

The normal tester path is a GitHub release asset install. The script auto-detects Linux, macOS, or WSL, downloads the matching artifact, verifies SHASUMS256, and copies it into the platform install location.

curl -fsSL https://github.com/randalmurphal/agent-overflow/releases/latest/download/install.sh | sh

Pin a specific release when you do not want "latest":

curl -fsSL https://github.com/randalmurphal/agent-overflow/releases/download/v0.0.1/install.sh | sh -s -- --version 0.0.1

Before an official release exists, use the same installer against a local release directory. make release writes artifacts to dist/release/<version>/; the --source path makes the installer read from that directory instead of GitHub. A Linux/WSL host can produce the Linux and WSL artifacts; the macOS zip is produced on macOS unless a working wails-cross Docker image is available.

make release
./scripts/install.sh --linux --download --source ./dist/release/0.0.1
./scripts/install.sh --macos --download --source ./dist/release/0.0.1
./scripts/install.sh --wsl --download --source ./dist/release/0.0.1

Linux installs to ~/.local/bin and writes the desktop entry/icon under ~/.local/share. macOS installs to ~/Applications/Agent Overflow.app. The WSL installer must be run from inside WSL; it copies the Windows launcher to %LOCALAPPDATA%\Programs\Agent Overflow\agent-overflow.exe on the Windows filesystem and creates a Start Menu shortcut at %APPDATA%\Microsoft\Windows\Start Menu\Programs\Agent Overflow.lnk. That shortcut is what makes Agent Overflow show up in Windows app search with its icon, and the local %LOCALAPPDATA% copy keeps Windows from running it through \\wsl.localhost.

Use --dry-run to preview, --system for system locations where supported, and --uninstall to remove app-owned install files. Passing a local artifact path still works when you want to bypass download mode:

./scripts/install.sh --linux ./dist/release/0.0.1/agent-overflow-linux-amd64
./scripts/install.sh --macos ./dist/release/0.0.1/agent-overflow-darwin-arm64.zip
./scripts/install.sh --wsl ./dist/release/0.0.1/agent-overflow-wsl-amd64.exe

A release also carries agent-overflow-headless-linux-amd64, the same backend built without GTK or WebKit, for a machine you reach from elsewhere rather than sit at. It is not an install.sh target — it wants no desktop entry and no icon; copy it onto PATH and run agent-overflow serve. See serve-mode.md.

The installer download path has a local smoke test:

./scripts/test-install-download.sh

Run

make dev        # dev mode with hot reload (local supervisor)
make build      # production build (wails3 build)

Developing the Windows + WSL path

The native Windows backend isn't fully supported (terminals and provider lifecycle are stubs); production Windows runs the WSL launcher under cmd/agent-overflow-windows/. To dev the Windows path from inside a WSL shell:

make dev-wsl    # cross-compile Linux ELF + Windows .exe, then launch
                # the .exe with --distro $WSL_DISTRO_NAME so the picker
                # is skipped (you're already shelled into your distro).
                # The .exe opens on the Windows desktop via WSL interop.
make build-wsl  # build only; hand the .exe off without launching.

make dev-wsl is non-persistent — it doesn't overwrite the saved distro choice in %APPDATA%\agent-overflow\wsl.json, so a dev session won't change which distro a production double-click of the .exe lands in.

Check

make check      # go build + frontend type check
make test       # go test + frontend unit tests
make verify     # full release gate

Remote access

Agent Overflow's transport (the HTTP+WebSocket layer between the Svelte SPA and the Go backend) defaults to loopback-only and binds to a fresh ephemeral port at every launch. Every page gets a one-time ticket and exchanges it, on its first load, for an HttpOnly session cookie — so no page script ever holds a credential. A URL a person opens carries the ticket (http://127.0.0.1:<port>/?t=<ticket>), stripped from the address bar once spent, so a copied URL opens exactly one session. The embedded webview's URL carries no credential at all: the same process that mints the ticket owns the window, so it hands the loaded page its ticket directly rather than putting it somewhere copyable.

A handful of opt-in modes extend that:

  • --listen <addr> binds the transport to a different interface (e.g. 0.0.0.0:54321 for LAN). Settings → Remote access renders a share URL for the bound interface; each render carries its own one-time ticket, so a second device needs the panel read again. Equivalent to flipping the "Allow remote access" toggle in Settings → Network, which persists the preference across launches.
  • --connect <endpoint | pairing link | backend> runs the desktop binary as a thin client against a remote backend. The local process boots a stub static-asset server, keeps the upstream credential in Go, and carries the webview's WebSocket to the remote backend. No local transport, store, or providers are started. Three forms:
    • ws://host:port/ws?token=<value> — a backend on this machine, or one reached through an SSH tunnel. The upstream's launch token authenticates the hop.
    • a pairing link copied from the other machine's Settings → Remote access — for a backend across a network, where a launch token is not enough. The terminal shows a six-digit verification number to compare and confirm on that machine; once confirmed, this installation holds a device credential it renews on its own and pins that backend's TLS certificate with.
    • the paired backend's name or endpoint — reuses that stored credential, so pairing happens once per machine.
  • Tailnet access (Settings → Remote access) joins this backend to your own Tailscale network as its own machine, so it is reachable from anywhere you are signed in — with no port forwarded, no public listener, and no tunnel process to run. Turning it on gives you a link to approve the machine in your tailnet; once approved, the panel shows the address it answers on. It answers the same routes and demands the same paired session as every other way in: joining a tailnet is how a device REACHES this backend, never how it gets in. If your tailnet has HTTPS enabled, the address is https:// with a certificate browsers already trust. Turning the feature off keeps the machine's identity, so turning it back on rejoins as the same device; "Forget this node" is the separate act that deletes it.
  • Windows + WSL silent mode (agent-overflow-windows.exe) drops the Linux backend into a chosen WSL distro, runs it headless, and forwards localhost:<port> from inside the distro to the Windows host via WSL2's vEthernet bridge. The WebView2 attaches like any other local launch.

Trust model

Anyone holding the session token — or a live page cookie — can RPC the host as the user that launched the binary. The token never rides a page URL (a URL carries a one-time ticket, exchanged once for an HttpOnly cookie), so page script cannot read a credential back out; that narrows how a credential leaks, not what one is worth. A peer that is not on this machine has to name a paired device's session, and every call it makes is checked against that session's granted scopes — with a host tier (terminal spawn, editor open, updater, credential retrieval) that no session can be granted at all. That's defense-in-depth, not a security boundary you can expose to the public internet.

For non-trusted networks, reach the backend over a network you control rather than exposing a port on one you do not:

  • Tailnet access (above) — the built-in form, and the one to prefer: no listener on any public interface, and every path in is one you enrolled.
  • SSH local port forward (ssh -L 54321:localhost:54321 host).
  • Tailscale Serve, run outside the app, if you would rather manage the node yourself.
  • Reverse proxy (Caddy / Nginx / Cloudflare Tunnel) terminating TLS in front of the backend.

Token hygiene

The token lives in the launch URL — which means it persists in browser history, shell history, and any place that records URLs. Treat it as a password. The token is regenerated on every launch, so closing and reopening the app rotates it; if you've shared a launch URL, restart to invalidate.

Other machines you attach to are stored as PAIRED DEVICE SESSIONS, not as saved tokens: one file per backend under the app config directory, each holding a rotating credential this installation renews and the certificate fingerprint it pinned when it paired. Nothing there is a bearer token you can copy, and nothing is ever handed to the page — the local backend carries the connection itself.

Docs

Start at AGENTS.md for the project overview, stack, principles, and repo map. Area-specific guides live alongside the code as nested AGENTS.md files. Deep-dive design notes are under docs/architecture/; external reference repos and the spike-test policy are under docs/references/.