Languages: English | 简体中文
Reach your coding agents from iPhone or Mac when you leave the host's local network. Run this relay on your own server; your Corral host connects outward, so you do not need to expose the host's ports or give it a public IP.
Session traffic is end-to-end encrypted between the host and Apple client. The relay sees routing identities, connection timing and byte counts.
On the same network, you can skip the relay. Pair directly with the host using LAN access. For access across networks, you need a server and a domain with TLS.
macOS, Linux and Windows, with Go 1.25+. Linux service and TLS-proxy examples are in deploy/.
Prerequisite: Go 1.25 or newer (go.dev/dl).
go install github.com/corral-dev/corral-relay/cmd/corral-relay@latestThe command works on macOS, Linux and Windows PowerShell. Add the bin directory under go env GOPATH to your PATH if corral-relay is not found. Prebuilt binaries are not available yet.
Install script or source checkout
On macOS or Linux, this installer builds from source and installs into ~/.local/bin:
curl -fsSL https://raw.githubusercontent.com/corral-dev/corral-relay/main/install.sh | shTo build from a checkout:
git clone https://github.com/corral-dev/corral-relay.git
cd corral-relay
go build -o corral-relay ./cmd/corral-relayStart one relay locally. It stores host registrations in a JSON file; no database or account setup is needed.
macOS / Linux:
corral-relay -addr 127.0.0.1:8788 -node-id relay-1 \
-state "${XDG_CONFIG_HOME:-$HOME/.config}/corral-relay/hosts.json"Windows PowerShell:
corral-relay -addr 127.0.0.1:8788 -node-id relay-1 -state "$env:USERPROFILE\.config\corral-relay\hosts.json"Leave it running and check from another terminal:
curl http://127.0.0.1:8788/healthz
# {"ok":true}On Windows, use Invoke-RestMethod http://127.0.0.1:8788/healthz. Give each relay instance its own stable -node-id.
To use the relay across networks, deploy it behind TLS using Deployment, then run on your Corral host:
corral remote on --relay-url wss://relay.example.com
corral remote pairScan or paste the code in the Apple client. The code is one-time and expires after ten minutes; it authorizes access to your host. Keep it out of recordings, logs and public reports.
On the same local network or a private mesh, the Apple client can reach the host directly. Run on the host:
corral remote on --no-relay
corral remote pairSingle-tenant: one JSON file, one line per development machine (routing identity, display name, Ed25519 public key, node, timestamps). Multi-tenant: the same plus accounts, device codes, and usage counters in Postgres. No session content is ever stored. Registrations persist until the operator deletes them; there is no automatic expiry. If a machine loses its old registration key, only the operator can clear the stale registration — a new key can never silently take over an offline machine's slot.
Account and quota configuration
To add accounts and quotas, configure Postgres and a GitHub OAuth application. Set CORRAL_PUBLIC_URL to your deployment's HTTPS origin.
CORRAL_RELAY_MULTI_TENANT=1 \
CORRAL_DATABASE_URL=postgres://corral:PASSWORD@db-host/corral_relay \
CORRAL_PUBLIC_URL=https://relay.example.com \
CORRAL_GITHUB_CLIENT_ID=... \
CORRAL_GITHUB_CLIENT_SECRET=... \
CORRAL_SESSION_SECRET=... \
corral-relay --multi-tenantA host registers to exactly one authenticated account; another account cannot claim that host's routing identity, and host and traffic quotas are accounted per account. Client data-plane connections carry no account login: the one-time pairing code plus the mutually confirmed device keys authorize a client to one specific host, which keeps reconnects working without an account session.
Connection-resource boundary: to let paired clients reconnect account-free, the routing identity must stay reachable, so the relay accepts a rate-limited connection first and the machine verifies the encrypted handshake. Unpaired callers cannot read or control a machine but can still consume limited connection resources. Deployments open to untrusted users at scale should add short-lived per-host connection credentials (not yet implemented) and publish their own metadata retention rules.
APNs configuration for your own iPhone build
To receive iPhone notifications, configure APNs credentials matching your client build. While the app is open, sessions update without APNs.
With your own Apple Developer account and build, set (values shown are placeholders):
CORRAL_PUSH_KEY=/path/to/AuthKey.p8
CORRAL_PUSH_KEY_ID=PLACEHOLDER_KEY_ID
CORRAL_PUSH_TEAM_ID=PLACEHOLDER_TEAM_ID
CORRAL_PUSH_BUNDLE_ID=com.example.corralNotification bodies forwarded by the relay are themselves ciphertext, decrypted
locally on the phone. The relay never sees titles or text. An APNs 200 means
accepted for delivery, not delivered to the phone.
Put TLS in front (Caddy, nginx, or equivalent) and hand the CLI a
wss://your-own-domain URL. The one setting you must get right is disabling
the proxy's read/write timeouts: the machine's outbound connection can sit
idle for hours, and short timeouts silently sever it (client shows online,
traffic is dead). Ready-made examples live in deploy/; the admin
/metrics listener binds to loopback by default.
The wire protocol (v2 frames, host auth, pairing links, end-to-end encryption)
is specified in docs/PROTOCOL_V2.md.
scripts/device_probe.py checks connection, handshake, pairing and session listing:
python3 scripts/device_probe.py --relay wss://relay.example.com \
--key <host-public-key-hex> --code <pairing-code>Report a bug or request a feature. Remove keys, pairing codes and private host details from public reports. Use private security reporting for vulnerabilities.
See CONTRIBUTING.md. Outside contributions require agreeing
to the Contributor License Agreement in the pull request. The
Corral name and artwork are covered by a separate brand policy.
GNU Affero General Public License v3.0 only. If you run a modified version as a network service, you must offer its source to that service's users.