Waypoint is a personal remote-control companion for Claude Code, Codex, and OpenCode sessions running on your machine. It provides a private, phone-first interface over Tailscale with:
- a unified session list
- chat-style transcript rendering
- raw terminal fallback
- managed launches for new sessions
- tmux attachment for existing sessions
- a usage-telemetry dashboard (tokens, sessions, tool activity, context and provider-limit health)
Waypoint runs on macOS and Linux, and should also work under WSL when the local tooling and network access are available. Some convenience scripts are still OS-specific, for example the backend LaunchAgent helper is macOS-only.
Use GitHub Issues directly for active bugs and feature requests.
- one leaf issue per actionable bug or feature request
- lowercase title prefixes:
bug: ...andfeat: ... - label bug reports with
bugand feature requests withenhancement
backend/— FastAPI daemon, tmux/session runtime, auth, persistencebackend/src/waypoint/backends/— one package per coding agent (claude_code/,codex/,opencode/) plus the transport packages (claude_tty/for the tty-tail Emulated transport,tmux/for the raw Terminal transport); seedocs/coding_agent_plugins.mdfor the plugin contract and extension recipewaypointctl/— standalone control-plane package and daemon (uv tool install ./waypointctl,pipx install ./waypointctl)frontend/— Next.js PWA client
A single long-lived personal assistant thread — which answers host questions, grounds itself in your running sessions, and manages them via the waypoint sessions CLI — can be enabled in waypoint.yaml. Its coding backend, model, and permission mode are switchable on the fly, and the thread can be terminated, reattached, or context-cleared from the assistant page; see docs/personal_assistant.md.
The Telemetry page turns your session/token/context/rate-limit signals into a local-first, privacy-preserving usage history — token consumption, session and turn activity, tool outcomes, context pressure, and provider limits, with drill-downs and evidence-linked insights (and an optional AI summarizer). Collection is aggregate-only and stays on the host; see docs/telemetry.md.
Waypoint speaks to Claude Code, Codex, and OpenCode through wire formats that change between releases (stream-json envelopes, codex app-server RPCs, hook-event shapes, REST/SSE event streams). Bumps outside the tested range are likely to work but are not guaranteed.
| Agent | Tested versions | Wire entry point | Notes |
|---|---|---|---|
| Claude Code | 2.1.178 |
claude -p --input-format=stream-json --output-format=stream-json --permission-prompt-tool stdio |
Approvals ride the can_use_tool control protocol; CLAUDE_CODE_WORKFLOWS=1 enables the Workflow tool. Also relies on --include-hook-events, the system/status/compact_boundary events, and --session-id/--resume. |
| Codex CLI | 0.139.0 |
codex app-server --listen stdio:// |
Driven via the openai-codex Python SDK (thread_* / turn_* RPCs). Managed local sessions run the bundled openai-codex-cli-bin binary, currently 0.136.0 — the PyPI cli-bin channel lags the npm @openai/codex channel, so it trails the tested 0.139.0. |
| Codex SDK | 0.1.0b3 |
openai-codex on PyPI (pre-release) |
Pinned in backend/pyproject.toml; backend/pyproject.toml overrides the bundled openai-codex-cli-bin to the latest stable (0.136.0). |
| OpenCode | 1.17.4 |
opencode serve with REST + SSE API |
HTTP-based; discovers models from /provider. |
To extend this matrix:
- Update the row above with the new tested version.
- Re-run the integration paths that touch the wire format:
- Claude:
backend/src/waypoint/backends/claude_code/normalize.py(status / compact / rate-limit / approval / content-block helpers) andadapter.py's_handle_*stream handlers, plus_handle_can_use_toolfor tool approval over the control protocol; remote launch inbackends/claude_code/remote.py::_build_remote_claude_command. - Codex:
backend/src/waypoint/backends/codex/normalize.py::map_notificationand the SDK calls inbackends/codex/adapter.py::CodexAppServerAdapter. - OpenCode:
backend/src/waypoint/backends/opencode/normalize.py::map_eventand the HTTP/SSE adapter inbackends/opencode/adapter.py::OpenCodeAdapter.
- Claude:
- If a bump breaks an event shape, prefer adding a branch in the relevant
normalize.pyover hard-pinning.
Adding a brand-new coding agent (Aider, …) is its own flow — the runtime, API, and frontend dispatch by plugin id, so a new agent is "implement an agent plugin that satisfies the BackendPlugin protocol, declare its supported transports, and register it." See docs/coding_agent_plugins.md for the contract, capability descriptor, and a step-by-step recipe.
curl -fsSL https://raw.githubusercontent.com/kaiitunnz/waypoint/main/scripts/install.sh | bashThe installer fetches the latest tagged release, clones the repo to ~/.waypoint/app (or $WAYPOINT_HOME if set — use a dedicated directory, not an existing development clone), seeds default config files with a random password, and installs waypointctl. It does not start the stack — run waypointctl start (or waypointctl start backend) yourself once it finishes. To pin a specific version, pass --ref through the pipe:
curl -fsSL https://raw.githubusercontent.com/kaiitunnz/waypoint/main/scripts/install.sh | bash -s -- --ref v0.1.0or set WAYPOINT_VERSION=v0.1.0 in the environment before running. To track the bleeding edge instead of a release, pass --nightly (the tip of main). Prerequisites: git and Node.js ≥ 20 (npm included); uv is installed automatically if absent.
Bring the stack up with waypointctl start. Use waypointctl update to upgrade to the latest release, waypointctl update --ref vX.Y.Z to pin a specific version, or waypointctl update --nightly to track main; update leaves the stack untouched, so run waypointctl restart afterward to apply the new code. Add --check to any of these to report whether an update is available without applying it (handy on a schedule).
To remove Waypoint, run waypointctl uninstall (add --purge to also delete the state and data directories). It stops the stack, strips the WAYPOINT_HOME shell-profile entry, removes the managed checkout, and uninstalls waypointctl; curl-based installs can instead run scripts/uninstall.sh directly.
OpenCode — the opencode CLI must be installed separately and present on your PATH; Waypoint does not bundle it.
cd backend
cp waypoint.example.yaml waypoint.yaml
# edit waypoint.yaml and set a real password
uv sync --group dev
uv run pre-commit install
uv run waypoint serveWaypoint uses the openai-codex Python SDK (pulled from PyPI) for managed Codex app-server sessions; the matching Codex binary ships with it via openai-codex-cli-bin.
For a full backend quality pass before committing, run:
cd backend
uv run pre-commit run --all-filesTo install the backend as a macOS LaunchAgent:
cd backend
./scripts/install_launchd.shBackend config precedence:
- CLI flags such as
waypoint --config ... serve --host ... --port ...(--configis a top-level option, before the command) WAYPOINT_*environment variables (set in your shell, systemd unit, or LaunchAgent)- values from
backend/waypoint.yaml - built-in defaults
The YAML file is the canonical place for settings; env vars are an escape hatch for machine-specific overrides. See backend/waypoint.example.yaml for the full schema, including optional named ssh_targets that add remote coding backends to the frontend picker.
The frontend launch form also reads default_backend and default_cwd from backend config through /api/me, so switching to a different Waypoint backend updates those defaults automatically. If the selected Waypoint host exposes SSH targets, those appear in the same picker and only affect new managed launches on that host.
When an SSH target is selected, the launch form uses that target's default_cwd as the default remote path and lets you override it per launch, suggesting directories on the target as you type. Managed sessions now use the same cwd value for UI display and the actual SSH-side working directory.
Remote sessions have the same workspace explorer, file finder, git changes, path completion, and skill completion as local sessions. They run over SSH, one command per operation, and require python3 and git on the target. ControlMaster/ControlPath/ControlPersist in ssh_args (see the example config) reuse one connection across operations.
cd frontend
npm install
npm run devFrontend dev notes:
npm run devbinds to0.0.0.0so the phone can reach the dev server over Tailscale.- The dev config auto-allows the machine's non-internal IPv4 addresses, including typical Tailscale
100.x.x.xaddresses, and emits both bare hosts and fullhttp://host:3000origins. - If you still need extra origins, set
NEXT_ALLOWED_DEV_ORIGINS(comma-separated) in your shell or in afrontend/.env.localyou create yourself. - For the most reliable phone check-in flow, prefer
npm run build && npm run startovernpm run dev.
The frontend stores the backend base URL and bearer token in local storage.
When the frontend is opened from a phone, it now infers the backend URL from the current page host and port 8787. Example:
- frontend:
http://100.x.y.z:3000 - inferred backend:
http://100.x.y.z:8787
If one host must participate in more than one tailnet, run one Docker-backed Tailscale profile per tailnet. Each profile is a separate container and a separate Tailscale node.
See waypointctl/README.md for the command syntax, configuration variables, and startup checks.
waypointctl is the canonical way to run the local stack. It's an installable
Python control plane that supervises backend + frontend without depending on
the legacy shell script. Install it once:
uv tool install ./waypointctl
# or: pipx install ./waypointctlThen from the repo root (or anywhere, with --home <repo>):
waypointctl start # bring up backend + frontend, exits when ready
waypointctl status # pid, port, health for each service
waypointctl logs # tail backend + frontend logs
waypointctl restart [backend|frontend|all]
waypointctl stop
waypointctl daemon start # opt in to the long-lived waypointd supervisorWhat it does:
- starts
uv run waypoint serveinbackend/(in its own process group) - builds the frontend if needed, then runs
npm run startinfrontend/ - waits for backend
http://127.0.0.1:8787/healthand the frontend root URL before reporting success - stores PID files, logs, and default backend/uv-cache directories under
WAYPOINTCTL_STATE_DIR(defaults to~/.waypoint/)
Configuration comes from a repo-root .env file if present:
cp .env.example .envUseful overrides (set in .env or per invocation):
WAYPOINT_STACK_BACKEND_PORT=8788 WAYPOINT_STACK_FRONTEND_PORT=3001 waypointctl start
WAYPOINT_STACK_CONFIG=/absolute/path/to/waypoint.yaml waypointctl restart
WAYPOINT_STACK_BACKEND_DATA_DIR=/tmp/waypoint-data waypointctl startBy default each command runs in-process and exits when the work is done.
waypointctl daemon start brings up a long-lived waypointd that subsequent
commands transparently route through; this is useful when an agent hosted by
Waypoint itself needs to issue restart without taking its own process tree
down. See waypointctl/README.md for the daemon
model, agent-restart safety check, and full env-var reference.
For a machine-managed background backend on macOS, backend/scripts/install_launchd.sh
remains the right tool; treat the frontend separately.
The shell-based supervisor is preserved for users on the existing workflow
and will be deprecated once we're confident waypointctl covers all
their cases. It writes state under tmp/waypoint/ (repo-local) and the two
tools do not share state by default. New work should target waypointctl.
./scripts/waypoint.sh start | stop | restart | status | logs