How it works
Your terminal runs locally. Your browser is the interface.
Agent Panel lets you open, read, and type into persistent terminals on your own Mac or Linux machines from a browser.
The simplest mental model is tmux in your browser: one dashboard for persistent shells across every connected machine. It is designed around supervising coding agents, but it is a general remote terminal. The shell, repository, tools, permissions, and local model processes remain where they already run.
It does not provision a cloud development machine, expose an SSH server, or copy your project into a hosted workspace. Your project files, tools, and running processes stay on your machine.

The connection
What happens when you open a terminal.
Install Agent Panel once on a supported host machine. It creates an outbound route to the relay. When you open a terminal, your authenticated browser is connected to that route—not to a public port on your network.
Your signed-in browser asks Agent Panel to open a session on one of the machines registered to your account.
The relay forwards encrypted frames between browser and host. It does not execute shell commands.
Agent Panel runs the shell under its installed local user, with that user's environment and permissions.
This is not an SSH tunnel. Agent Panel creates an outbound relay route; it never listens for public SSH connections on your network.
The terminal is not simulated. Commands run in the machine's existing environment, alongside its repositories, agents, local models, and credentials.
Session lifetime
Disconnect without stopping the terminal.
Each terminal is a tmux session on the host, under the user who installed Agent Panel. Closing the browser disconnects the view; it does not send a hangup to the shell.
Close the browser or lose the network
The terminal continues. Reconnect to its live session later.
Update or restart Agent Panel on the host
Agent Panel discovers its existing tmux sessions and makes them available again.
Restarting the host ends the session
Sleep only makes the session unreachable until the machine wakes.
The trust boundary
Remote access carries your local permissions.
Agent Panel authenticates and encrypts the route to the machine. It does not sandbox commands or reduce the privileges of the user who installed Agent Panel.
Connection controls
The host and browser authenticate each other.
The browser opens a libp2p stream over WebSockets and the circuit relay, using Noise for transport encryption and Yamux for stream multiplexing. It dials only addresses allowed by the machine's current route advertisement.
Before any terminal message is accepted, Agent Panel on the host signs a P-256 handshake covering the host and session IDs, both peer IDs, relay address, and a fresh nonce. The browser verifies that signature against the registered device key.
The backend then issues a 60-second capability bound to that same handshake and only for a machine owned by the signed-in account. The relay carries the Noise-encrypted stream; it does not execute shell commands, and no public SSH port is opened.
Local authority
Every command has your local user's permissions.
Agent Panel creates or attaches to the tmux-backed shell as the operating-system user who installed it. The working tree, environment, PATH, credentials, local model processes, and filesystem access are that user's—not a hosted replica.
Agent Panel does not containerize the shell, filter commands, or add an approval layer. Anyone who controls the signed-in browser session, Agent Panel account, or local user account can act with that user's authority. Your account, browser, and host are all part of the trust boundary.
Browser session
Closing the browser does not end the signed-in session.
Agent Panel uses the authentication provider's browser session cookie to recognize your account. Reopening the dashboard in the same signed-in browser restores access; a different browser or browser profile must authenticate separately.
A visible dashboard refreshes a valid session before it expires. Hidden tabs do not keep a session alive: the app checks again when the tab becomes visible. Signing out or reaching expiry requires authentication again.
For display recovery, the browser may cache encrypted terminal buffer snapshots in IndexedDB. Agent Panel rejects snapshots more than 14 days old and removes expired snapshots when the dashboard next runs browser-storage cleanup. Payloads are encrypted with AES-256-GCM before they are written. The per-user key is derived by the server, fetched only through an authenticated session, imported as non-extractable, and held in memory rather than persisted in the browser.
Snapshot identity and cursor metadata are authenticated with the ciphertext, so altered or transplanted records fail decryption and are discarded. This browser-local recovery cache does not give the hosted service a durable terminal transcript.
Host metadata, project names and full paths, layouts, and aggregate usage data.
A durable transcript of raw terminal output, commands, files, or diffs.
Credentials and its device identity. Protect the local account and configuration directory.
The install command
What the setup command actually does.
Setup creates a random, one-time token that expires after 15 minutes. The wrapper detects your platform, downloads the matching Agent Panel release, verifies checksums, and installs a user-level service. On macOS, it also checks the release's signing identity.
The safety checks are deliberate. A one-time, 15-minute token limits how long a copied command can authorize an install and prevents it from becoming a durable credential. Platform detection avoids installing the wrong build, checksums reject altered downloads, and macOS verifies the publisher's signing identity before installation.
Treat a generated install command as sensitive until its token is used or expires.
#!/bin/sh
set -eu
token='<one-time-token>'
origin='https://agent-panel.dev'
callback_credential='<outcome-callback-credential>'
callback_url="$origin/install/outcome"
callback_marker="$(mktemp 2>/dev/null || mktemp -t agent-panel-installer-outcome)"
failure_stage='platform_detection'
platform=''
architecture=''
release=''
deliver_outcome() {
callback_body="$1"
if [ -z "$callback_credential" ] || [ -z "$callback_url" ] || ! curl -fsS --max-time 5 --retry 0 -X POST -H "Authorization: Bearer $callback_credential" -H 'Content-Type: application/json' --data "$callback_body" "$callback_url" >/dev/null; then
printf '%s\n' 'Agent Panel could not report the installer outcome; the install result above is unchanged.' >&2
return 1
fi
}
report_outcome() {
status="$1"
outcome='failure'
callback_stage="$failure_stage"
if [ "$status" -eq 0 ]; then
outcome='success'
callback_stage=''
fi
callback_body="{\"outcome\":\"$outcome\",\"platform\":\"$platform\",\"architecture\":\"$architecture\",\"release\":\"$release\""
if [ -n "$callback_stage" ]; then
callback_body="$callback_body,\"failure_stage\":\"$callback_stage\""
fi
callback_body="$callback_body}"
deliver_outcome "$callback_body" || true
}
on_wrapper_exit() {
status="$?"
trap - EXIT
marker_state=''
if [ -s "$callback_marker" ]; then
marker_state="$(cat "$callback_marker")"
fi
case "$marker_state" in
reported) ;;
'') report_outcome "$status" ;;
*) deliver_outcome "$marker_state" || true ;;
esac
rm -f "$callback_marker"
exit "$status"
}
trap on_wrapper_exit EXIT
uname_s="$(uname -s 2>/dev/null || true)"
case "$uname_s" in
Darwin) os="darwin"; platform="$os" ;;
Linux) os="linux"; platform="$os" ;;
*)
printf '%s\n' "Agent Panel installer supports macOS and Linux, not $uname_s." >&2
exit 1
;;
esac
uname_m="$(uname -m 2>/dev/null || true)"
case "$uname_m" in
x86_64|amd64) arch="amd64"; architecture="$arch" ;;
arm64|aarch64) arch="arm64"; architecture="$arch" ;;
*)
printf '%s\n' "Agent Panel installer supports amd64 and arm64, not $uname_m." >&2
exit 1
;;
esac
failure_stage='manifest_download'
manifest_url="$origin/install/manifest?t=$token&os=$os&arch=$arch"
fetch_payload_with_retry() {
attempt=1
while [ "$attempt" -le 5 ]; do
if payload="$(curl -fsSL "$manifest_url")"; then
return 0
fi
if [ "$attempt" -eq 5 ]; then
return 1
fi
sleep "$attempt"
attempt=$((attempt + 1))
done
}
fetch_payload_with_retry || {
printf '%s\n' 'Agent Panel installer assets are unavailable. Generate a new command from the Host setup page in Agent Panel and try again.' >&2
exit 1
}
failure_stage='install'
printf '%s\n' "$payload" | AGENT_PANEL_INSTALL_CALLBACK_CREDENTIAL="$callback_credential" AGENT_PANEL_INSTALL_CALLBACK_URL="$callback_url" AGENT_PANEL_INSTALL_PLATFORM="$platform" AGENT_PANEL_INSTALL_ARCHITECTURE="$architecture" AGENT_PANEL_INSTALL_CALLBACK_MARKER="$callback_marker" sh
Generated at page render from the same source as /install.sh, so this view changes with the installer. The one-time token is replaced by <one-time-token>, and the outcome callback credential is replaced by <outcome-callback-credential>.
Start with one machine
See whether this fits the way you already work.
Connect a development machine, open a terminal, then use the same browser workspace wherever you need it.