High-level map of the AnyCoding system for contributors. Read
README.mdfirst for the user-facing pitch.
AnyCoding is an open-core project. Two components live here (open source); one lives in a separate private repository (closed source).
┌──────────────────────────────────────────────────────────────────┐
│ This repository — public (Apache-2.0) │
│ │
│ any-code-cli/ Node CLI on the user's Mac/Linux │
│ any-code-android/ Android app │
└──────────────────────────────────────────────────────────────────┘
│
│ WebSocket (wss)
▼
┌──────────────────────────────────────────────────────────────────┐
│ Separate repository — private (closed source) │
│ │
│ any-code-server/ FastAPI relay + user/device/billing │
└──────────────────────────────────────────────────────────────────┘
The relay server is closed source because it handles accounts, subscriptions, and multi-tenant state. Everything that runs on end-user machines is open.
Self-hosted alternative: users who don't want to depend on the hosted relay can run the CLI with any-coding hub --public, which exposes the local hub via a cloudflared tunnel. No account needed, no closed-source server in the loop.
Single-file wrapper (wrapper.js) with two modes:
- Attach mode (default): spawn a local pty running
claude/codex/ another agent CLI, mirror bytes into the hub, and act as a terminal viewer for the current shell. - Hub mode (
--hubor detached): run an HTTP + WebSocket server on127.0.0.1:7321, manage multiple pty sessions, and optionally dial out to the relay server as an "agent" client.
Key dependencies: node-pty, ws, qrcode-terminal. No TypeScript, no framework. One file for easy audit.
Two-activity Android app:
- MainActivity — WebView shell loading
assets/index.html, handles login / register / pairing / device selection. - TerminalActivity — Native terminal using vendored Termux
terminal-emulator+terminal-viewlibraries (com.termux.*). Bytes flow throughrelay/RelayClient.kt(OkHttp WebSocket) directly to the terminal session.
Minimum SDK 24 (Android 7.0). Built with AGP 8.5.2 + Gradle 8.9 (JDK 17).
FastAPI + SQLAlchemy async, backed by MySQL 8 + Redis 7. Acts as:
- Auth + account management (
/v1/register,/v1/login,/v1/me) - Device pairing via 6-digit code + Redis TTL (
/v1/pair/init,/claim,/wait) - Byte bridge between agent (PC) and client (phone) via WebSocket (
/v1/agent,/v1/client)
The relay is stateless with respect to terminal content — it does not parse, log, or interpret pty bytes. It only relays them.
[Android app] [Ghostty / iTerm]
│ │
│ Native TerminalView │ Local TTY viewer
│ RelayClient (OkHttp wss) │ ws://127.0.0.1:7321/ws/session/:id
│ │
▼ ▼
┌────────────────┐ ┌──────────────────────┐
│ Relay server │ │ Local hub │
│ (closed) │ ◀─── wss ─────▶ │ (any-coding --hub) │
│ │ /v1/agent │ │
└────────────────┘ │ pty.spawn('claude') │
└──────────────────────┘
│
▼
[claude / codex / ...]
[Android app] ──── wss ────▶ [cloudflared quick tunnel] ──▶ [local hub] ──▶ [agent CLI]
No account, no closed server, no billing. URL changes on each restart (pair again).
See README.md for the full protocol reference. In short:
- Text frames = JSON control messages (
list,open,close,input,resize,pingfrom client;sessions,opened,err,pongfrom agent). - Binary frames =
[4-byte BE session id][raw pty bytes]. Bidirectional. - Close codes:
4401auth failure,4403subscription expired,4404device offline,4002replaced by newer connection.
So that a local terminal viewer (Ghostty, iTerm) and the phone see the same pty. Closing the terminal window doesn't kill the agent session; opening another terminal rejoins the same output stream with full replay from a 256 KB ring buffer.
Any NAT / home router / coffee shop WiFi can connect without port forwarding. The PC is the agent client; the relay accepts inbound WebSockets on a public domain.
Scale + privacy + protocol agility. Adding a new agent (Codex, Gemini CLI) requires zero relay changes. The hub and the app negotiate everything.
WebView + xterm.js was the original approach. Background requestAnimationFrame throttling, Gboard predictive input, canvas sizing races, and soft-keyboard viewport jumps made it fragile. Termux's native TerminalView is the most complete VT100/xterm implementation on Android, handles CJK widths, true color, bracketed paste, and alt screen correctly. Trade-off: no iOS reuse — an iOS port would need SwiftTerm or a new WebView/xterm.js shell.
The QR is a visualization of the same 6-digit code. Humans can type a 6-digit code as a fallback when the camera fails. The real long-lived device_token never travels inside the QR — it is returned over HTTPS from /v1/pair/wait.
.
├── any-code-cli/ Node CLI (Apache-2.0)
│ ├── wrapper.js Single-file entry, hub + attach modes
│ ├── scripts/ postinstall fixes
│ └── package.json
├── any-code-android/ Android app (Apache-2.0)
│ ├── app/ Main module
│ ├── terminal-emulator/ Vendored Termux VT state machine (Apache-2.0)
│ └── terminal-view/ Vendored Termux Android View (Apache-2.0)
├── LICENSE Apache-2.0
├── NOTICE Third-party attributions
├── README.md
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
├── SECURITY.md
├── ARCHITECTURE.md (this file)
└── .github/ Issue + PR templates
- Fork → branch from
develop - Change CLI or Android code
- Test against cloud relay and
hub --publicmode (both must work) - Open PR against
develop - One maintainer approval → merge
develop→mainon release, tag, publish npm + attach APK to GitHub Release
For anything touching the wire protocol, relay endpoints, or authentication flow, please open an issue first to discuss — those changes ripple into the closed-source relay and need coordination.