Skip to content

Pair the Guuey app to a self-hosted server

If you followed the OSS Quick Start, you already have a local ggui serve running. This page walks you through connecting the Guuey iOS / Android / web app to it, so you can browse agents, start chats, and view generated UIs from the Guuey app while the server stays on your infrastructure.

Your MCP agent ── MCP ──→ ggui serve (your infra)
│ /pair (POST, bearer-issued) · /ws (channel-3)
Guuey app (web / iOS / Android)

The Guuey app is a client. Pairing exchanges a one-shot code for a long-lived bearer token; the app stores the token and uses it on every subsequent call. Your server is authoritative — it never talks to api.guuey.com on your behalf.

  • A running ggui serve that the Guuey app can reach over HTTP/HTTPS + WebSocket. On localhost that means serving on 127.0.0.1 and opening the Guuey app on the same machine; across the network, see Reaching your server from a phone.
  • An operator session on the server — i.e., you can mint codes. By default the built-in AuthAdapter accepts any non-empty bearer as builder, which is fine on localhost and unsafe anywhere else (see Hardening).

Pairing protects a code-bearing first exchange. The server never hands out pairing tokens without a code it minted.

ggui serve exposes a minimal operator UI at http://127.0.0.1:6781/. Click Start pairing — the console calls POST /admin/pair/init with the current session bearer and displays a 6-digit code with an expiry timer (default: 2 minutes).

If you prefer a headless flow (CI, scripts, tmux):

Terminal window
curl -sS -X POST http://127.0.0.1:6781/admin/pair/init \
-H 'Authorization: Bearer dev' \
-H 'Content-Type: application/json' \
-d '{"serverName":"my laptop"}'

Response:

{
"code": "472301",
"codeExpiresAt": "2026-04-20T12:34:56.000Z",
"serverName": "my laptop"
}

Treat the code like a one-time password — anyone who gets it in the next 2 minutes can pair. It’s consumed on the first successful POST /pair.

Open the Guuey app (web at app.guuey.com, or iOS / Android), then:

  1. Go to Settings → Servers → Add server.
  2. Enter your server URL (http://127.0.0.1:6781 or the reachable hostname).
  3. Enter the 6-digit code from Step 1.
  4. Tap Pair.

The app sends POST /pair with {code, deviceName}, receives {pairingId, token, serverName, deviceName}, and stores the token locally. Subsequent calls include Authorization: Bearer <token> on the server’s MCP and WebSocket endpoints.

Once paired, the server shows up in Settings → Servers with a status dot (green when reachable, grey when not). You can pick it as the active origin for the Agents and Chats tabs via the origin switcher.

With the server paired:

  • The Agents tab lists the agents your server registered (via mcp-client-side ggui_register_agent or your own registration flow — OSS default is manual registration; no marketplace).
  • Starting a chat goes to your server’s /ws (channel-3). Messages flow through the paired MCP transport; the chat history lives in your server’s session store (or wherever your agent.entry writes it).
  • When an agent calls ggui_push, the resulting /s/<shortCode> viewer is hosted by your server. The Guuey app embeds it (same-origin cookies on your server, CSP locks in).

The hosted Guuey app does NOT proxy any of this — your server is the only backend involved.

  • List paired servers: In the app, Settings → Servers. Tap one to re-pair (long-press on mobile) or rename it.
  • Revoke server-side: The operator console has a Paired devices list. Revoking here invalidates the bearer token server-side; the app surfaces the revoke on the next call and prompts to re-pair.
  • Rotate a lost token: If you suspect a token leaked, revoke the server-side entry and re-run Step 1 / Step 2.

Localhost-only by default. To pair from a phone on the same LAN:

  1. Bind ggui serve to your LAN address (or 0.0.0.0), not 127.0.0.1:

    Terminal window
    pnpm exec ggui serve --host 0.0.0.0 --port 6781
  2. Note your LAN IP (e.g., 192.168.1.42) and use http://192.168.1.42:6781 as the server URL in the app.

If the phone is NOT on the same network (you’re debugging from mobile data, or behind different NATs), you need a public endpoint. Guuey doesn’t ship a managed tunnel — use any of:

  • ngrokngrok http 6781 and use the https://…ngrok-free.app URL.
  • Cloudflare Tunnelcloudflared tunnel --url http://localhost:6781.
  • Tailscale — put both devices on your tailnet; use the MagicDNS hostname.

Any of these make the pairing flow work across arbitrary networks without exposing your port to the open internet.

ggui serve’s default auth mode is dev-mode — any non-empty bearer token is accepted as builder. This is fine on 127.0.0.1 where the only caller is you. It is NOT safe once the server is reachable beyond localhost.

Before you put the server anywhere network-reachable:

  1. Swap in a real AuthAdapter. createGguiServer({ auth }) accepts a custom adapter. Your adapter gates both the /mcp endpoint and the channel-3 WebSocket. See @ggui-ai/mcp-server-core’s AuthAdapter interface for the contract.
  2. Terminate TLS in front. A reverse proxy (Caddy, nginx, Cloudflare Tunnel) is the expected shape. The bare ggui serve port is plaintext.
  3. Firewall the admin paths. POST /admin/pair/init is the only surface that mints codes. The /admin/ prefix is deliberate — block it from the public internet with one reverse-proxy rule and keep it on a local admin interface or VPN.
  4. Rotate pairing tokens periodically. Revoke paired devices from the operator console when they’re no longer in use.
  • Workspaces, billing, usage metering, team management. Those are Guuey-hosted SaaS surfaces. They do not exist on a self-hosted server.
  • Managed generation. The OSS path ships the protocol + pairing + viewer. Component-code generation is not yet wired on the OSS server — the same caveat from the OSS Quick Start applies.
  • Push notifications, mobile background sync, agent marketplace. Hosted-only for v1.