Gateway

The Chatcode Gateway

What the Chatcode gateway is, where it runs (Linux vs macOS), how it talks to Chatcode, the trust trade-offs, and why macOS shows permission prompts.

The gateway is the small program that makes Chatcode work on a machine you own. It runs your coding agents — Claude Code, Codex, Gemini, OpenCode — and connects them to the browser at app.chatcode.dev and to Telegram, without exposing anything inbound on your machine.

This page explains what it is, where it lives, how it talks to Chatcode, and the trust trade-offs in plain terms — including why macOS sometimes shows alarming-looking permission prompts (short version: it’s your agent doing what you asked).

What the gateway is

  • A single binary, chatcode-gateway (built per platform: Linux/macOS × Intel/ARM), installed as a background service.
  • It holds one outbound connection to the Chatcode control plane and runs your agent sessions in tmux workspaces under ~/workspace.
  • It’s the “Your server + gateway” box in the Chatcode architecture: browser ⇄ Chatcode ⇄ your gateway ⇄ agent session.
  • The release binary is downloaded over HTTPS and checksum-verified (SHA-256) before it’s installed.

Where it runs: Linux vs macOS

The gateway uses two different models depending on the OS — this is the biggest practical difference.

Linux — a dedicated vibe user (isolated)

Used by easy-mode DigitalOcean droplets and bring-your-own Linux servers. The installer runs as root and:

  • Creates a dedicated vibe user — agents run as vibe, not as your login account — with its workspace at /home/vibe/workspace.
  • Installs a systemd service (chatcode-gateway.service) that runs as vibe.
  • Installs base tools including tmux, git, ripgrep, and bubblewrap (for sandboxing agent sessions).
  • Pre-installs Claude Code + Codex and enables a daily maintenance timer that updates the agent CLIs and the gateway itself.

Because agents run as their own user, they’re isolated from your personal account and files on that box.

macOS — your own user

Used when you bring your own Mac. The installer runs as your normal user (never sudo) and:

  • Installs the binary to ~/.local/bin/chatcode-gateway and a launchd agent (dev.chatcode.gateway) that starts at login and stays running (kept awake with caffeinate on desktop Macs).
  • Requires Homebrew plus tmux, git, curl.

Here the agent runs as you, so it has the same file access you have. That’s convenient — and it’s the reason macOS shows the permission prompts described below.

Where things live

Linux (managed) macOS (your user)
Binary /usr/local/bin/chatcode-gateway ~/.local/bin/chatcode-gateway
Service systemd chatcode-gateway.service launchd dev.chatcode.gateway
Config/env /etc/chatcode/gateway.env (mode 600) ~/.config/chatcode/gateway.env (mode 600)
Runs as dedicated vibe user your login user
Workspace /home/vibe/workspace ~/workspace
Logs journalctl -u chatcode-gateway ~/Library/Logs/chatcode-gateway.log

Installing

In the app, add a server and Chatcode gives you a ready-to-run command with your gateway ID, auth token, and control-plane URL already filled in. It pipes the canonical installer at https://chatcode.dev/install.sh (which redirects to the latest release) into your shell:

# Linux — run as root (installs under a dedicated `vibe` user)
curl -fsSL https://chatcode.dev/install.sh | sudo bash -s -- \
  --gateway-id gw_xxx --gateway-auth-token tok_xxx \
  --cp-url wss://<control-plane>/gw/connect

# macOS — run as your normal user (do NOT use sudo)
curl -fsSL https://chatcode.dev/install.sh | bash -s -- \
  --gateway-id gw_xxx --gateway-auth-token tok_xxx \
  --cp-url wss://<control-plane>/gw/connect

The installer pulls the gateway binary from releases.chatcode.dev and verifies its SHA-256 checksum before installing. You can pin a version (--version vX.Y.Z) instead of tracking latest, or build the binary yourself and install it with --binary-source /path/to/chatcode-gateway.

Passwordless sudo (and how it’s kept honest)

When an agent you’re driving needs an admin action — install a package, restart a service — it can’t stop and ask you for a password through Telegram. So the gateway is set up for unattended admin:

  • Linux: the vibe user gets NOPASSWD sudo — but every command is recorded (use_pty) to a root-owned, append-only log at /var/log/chatcode/sudo-vibe.log that vibe can read but not alter, rotated for 14 days. You can audit exactly what ran.
  • macOS: passwordless sudo is optional and only offered if you opt in during install. Decline it and the gateway still works; you’ll just be asked for your password (or admin tasks won’t run) like any normal Mac.

This is a deliberate trade-off for “works while you’re away,” made auditable rather than hidden.

macOS permission prompts — don’t panic

If you run the gateway on your Mac, you’ll likely see macOS prompts such as:

“chatcode-gateway” would like to access files in your Documents folder.

…or prompts about your Desktop, Downloads, removable volumes, Accessibility, or Screen Recording. This can look alarming — as if the gateway wants to snoop. It doesn’t.

Here’s what’s actually happening: on macOS the gateway runs as you, and it runs the agent you asked for. When that agent reads a file in a protected folder, or a tool it runs needs to automate the UI or capture the screen, macOS’s privacy system (TCC) steps in and asks for consent — and it attributes the request to the process that launched the work, which is chatcode-gateway. So the name on the dialog is the gateway, but the intent is your agent doing the task you gave it.

What to do:

  • Allow what you expect. If you asked the agent to work with files in ~/Documents, allowing Documents access is exactly right.
  • Deny what you don’t. You’re always in control; denying just means the agent can’t touch that resource.
  • Manage it anytime in System Settings → Privacy & Security (Files and Folders, Accessibility, Screen Recording).

On Linux there’s no equivalent modal — the vibe user simply has its own Unix permissions, isolated from your account.

How it communicates

Browser / Telegram  ⇄  Chatcode control plane  ⇄  your gateway  ⇄  agent session
      (you)                  (TLS relay)            (your machine)   (Claude/Codex/…)
  • The gateway dials out to the control plane over a TLS WebSocket (wss://…). It never opens an inbound port on your machine.
  • The browser and Telegram connect to the control plane, which relays traffic to your gateway.
  • Your auth uses a session cookie; the gateway authenticates with a per-gateway token. The control plane does not store your private SSH keys.

No end-to-end encryption — and why

There is no end-to-end encryption between your browser and the gateway, and that’s a deliberate design choice, not an oversight. Each hop is TLS-encrypted, but the control plane terminates those connections so it can process the session content that makes the Telegram experience work — formatting agent output for chat, rendering file previews, transcribing voice notes, and deciding when to notify you. That processing needs readable payloads, so the control plane sits in a trusted-relay position and can read terminal traffic.

We’d rather state this plainly than imply a guarantee we don’t offer. Your code, credentials, and agent sessions still live only on your machine. If you want the relay under your control as well, you can self-host the control plane in your own Cloudflare account.

What the gateway shows in Telegram

Telegram is the continuity layer, and the gateway is what serves it. When you open a file preview from a chat, the gateway reads that file and hands it back through the relay. The bounds are deliberately tight:

  • Read-only. Previews never write. Only session work and uploads write, and those are confined to ~/workspace.
  • Scoped. Previews resolve under ~/workspace plus non-hidden paths in the gateway user’s home. Hidden stores like ~/.ssh, ~/.claude, and ~/.codex are off-limits unless they live inside ~/workspace — and the check runs on the symlink-resolved real path, so it can’t be tricked with a symlink.
  • Short-lived links. A preview download uses a cookieless bearer URL that expires in about five minutes.

One caveat worth stating plainly: the preview read-root includes non-hidden files anywhere under the gateway user’s home — so keep secrets out of that home directory (on Linux that’s /home/vibe), not just out of dotfiles.

Trust & privacy summary

  • Your machine, your subscription. Agents run on hardware you own using your own Claude/ChatGPT login. Chatcode is the relay, not a cloud that copies your repo elsewhere.
  • No inbound ports. Only an outbound WebSocket. SSH access for a debug session is separate and opt-in (you add a key; it’s empty by default).
  • Workspace boundary. Session work and uploads are constrained to ~/workspace; Telegram previews are read-only and scoped (details above).
  • Auditable admin. Passwordless sudo on Linux is logged to an append-only, root-owned file.
  • Secrets at rest. The env file holding your gateway token is mode 600.

Verify what runs

You don’t have to take our word for it:

  1. Read the installer. It’s a plain shell script you run yourself — inspect it before it touches the machine.
  2. Verify the checksum. Every release ships a .sha256; the installer checks it, and you can re-verify.
  3. Pin a version with --version vX.Y.Z instead of latest.
  4. Build from source and install with --binary-source.
  5. Self-host the control plane in your own Cloudflare account for full custody of the relay.

We’re documenting the gateway protocol and opening more of the source over time, so the relay and on-machine behavior can be independently verified — transparency is how this earns trust.

Updating & uninstalling

  • Update: a daily maintenance job updates the gateway and agent CLIs automatically. To update now, rerun the installer with a newer --version.
  • Status / logs: systemctl status chatcode-gateway and journalctl -u chatcode-gateway on Linux; ~/Library/Logs/chatcode-gateway.log on macOS.
  • Uninstall: run the cleanup script. It’s destructive by default — read it first.
# Linux — removes the service, config, sudoers + log policy, and the `vibe` user + home
curl -fsSL https://chatcode.dev/cleanup.sh | sudo bash -s -- --yes

# macOS — removes the launchd agents, binary, config, and Chatcode-managed hooks; keeps ~/workspace
curl -fsSL https://chatcode.dev/cleanup.sh | bash -s -- --yes

Useful flags: --keep-user (Linux: keep the vibe user and its home), --keep-workspace, --remove-workspace (macOS: also delete ~/workspace), and --kill-tmux. On macOS ~/workspace is kept unless you pass --remove-workspace.


New to all this? Start with How to use Claude Code in Telegram with Chatcode for the big-picture setup.

Frequently asked questions

Do I need my own server to run the gateway?

No. In easy mode, Chatcode connects to DigitalOcean and provisions a Linux droplet that runs the gateway for you. The bring-your-own path installs the same gateway on a Linux server or a Mac you already own.

Why does macOS say chatcode-gateway wants to access my Documents / Downloads / screen?

On macOS the gateway runs as your own user, so the coding agent you’re driving (Claude Code, Codex, etc.) has the same file access you do. When that agent reads a protected folder or uses a protected capability, macOS shows its standard privacy prompt and attributes it to the process that launched the agent — the gateway. It’s your agent doing what you asked, surfaced through macOS’s normal consent flow. Allow what you expect; deny what you don’t.

Does the gateway open ports on my machine?

No inbound ports. The gateway makes an outbound TLS WebSocket connection to the Chatcode control plane and the control plane relays traffic from your browser and Telegram. (SSH for a debug session is separate and opt-in.)

Can Chatcode see my terminal traffic?

In transit, yes. Traffic is TLS-encrypted per hop, but there is no end-to-end encryption between your browser and the gateway — by design. The control plane processes session content to power the Telegram experience (formatting agent output, file previews, voice transcription, notifications), so it sits in a trusted-relay position and can read terminal payloads. Your code, keys, and agent sessions still live only on your machine; to control the relay too, self-host it.

Why does the gateway use passwordless sudo on Linux?

So an agent you’re driving can perform admin tasks (install a package, restart a service) without stalling on a password prompt you can’t answer remotely. On Linux it runs as a dedicated vibe user, and every sudo command is written to a root-owned, append-only log you can review.