The site had drifted from the product. Every screenshot predated the v0.5.0 UI redesign, the reading experience shipped no search and no syntax highlighting, and a third of the pages were internal process artefacts — migration task lists addressed to agentic workers, a release runbook, a proposal marked "historical". Reorganise around the only two people who read this: someone getting the desktop app running for the first time, and someone reading the source. Five sections replace nine — start / desktop / im / cli / internals — and the pages that served neither reader are gone. Site rewrite: - Palette lifted from the desktop app's 「纸·墨·印」 themes, so the site and the product read as one thing. Light mirrors 纯白, dark mirrors 墨夜, and dark mode exists at all now. - Fonts are self-hosted. The old @import from Google Fonts is unreachable from mainland China, which left every heading in a fallback serif; it also only requested weight 600 while the CSS asked for 900, so Latin and CJK in the same heading disagreed. - Docs were shipped as one 968KB manifest downloaded on every page view. Split into a 32KB index plus one lazily imported chunk per page; the entry bundle is now 101KB gzipped. - Add search, syntax highlighting, per-route meta with canonical and hreflang, a sitemap, and an error boundary. Replace the 44vh mobile sidebar with a drawer. - Image dimensions are read at build time and written into the tag, so lazy images reserve their space instead of collapsing. Screenshots are recaptured from a real v0.5.0 build against a clean demo project, with tokens, QR codes and paired accounts redacted. The previous set is deleted rather than kept alongside. Routes follow file paths, so the restructure would have broken every inbound link; 37 old paths redirect, in both languages. The PR policy gate and CODEOWNERS also hardcoded docs/guide/contributing.md. Verified: check:docs 78 pages / 323 links / 0 problems, check:policy 127 pass. Walked every route at 1440 and 390 in both themes for overflow, contrast, keyboard reachability and focus management.
6.8 KiB
| title | nav_title | description | order |
|---|---|---|---|
| Local Server and API | Local Server | Startup flags, access control, REST surface, and chat WebSocket of the local server. | 2 |
Local Server and API
The local server is the runtime boundary between Desktop, the H5 browser client, and the Claude CLI. It provides REST APIs, the chat WebSocket, provider protocol translation, and Desktop web assets. The packaged Desktop app manages it automatically. Start it manually only for source development, headless deployment, or a custom client.
Start the server
From the repository root:
bun run src/server/index.ts
It listens on 127.0.0.1:3456 by default. Check readiness with:
curl http://127.0.0.1:3456/health
Response shape:
{
"status": "ok",
"timestamp": "2026-01-01T00:00:00.000Z"
}
/health is an always-public startup probe. A successful response does not prove that another endpoint is authenticated.
Startup options
| Option | Environment variable | Default | Description |
|---|---|---|---|
--host <host> |
SERVER_HOST |
127.0.0.1 |
Listen address |
--port <port> |
SERVER_PORT |
3456 |
HTTP and WebSocket port |
--cli-path <path> |
CLAUDE_CLI_PATH |
Automatically resolved | CLI started by the server |
--auth-required |
SERVER_AUTH_REQUIRED=1 |
Off | Require explicit authentication for capability endpoints |
Command-line host and port values take precedence over environment variables. Keep the loopback address for development. Listening on 0.0.0.0 accepts external connections but does not configure H5 authorization, TLS, or a reverse proxy.
Serve the H5 client
For a source launch, build the Desktop web assets first:
cd desktop
bun run build
cd ..
bun run src/server/index.ts
The server automatically finds desktop/dist in the repository. When starting from another directory, provide the absolute build path:
CLAUDE_H5_DIST_DIR=/absolute/path/to/desktop/dist \
bun run /absolute/path/to/src/server/index.ts
| Variable | Description |
|---|---|
CLAUDE_H5_DIST_DIR |
H5 build directory; it must contain index.html |
CLAUDE_H5_PUBLIC_BASE_URL |
Fixed public server URL |
CLAUDE_H5_AUTO_PUBLIC_URL=1 |
Try to derive a LAN URL while H5 access is enabled |
Configure the H5 token and exact allowed browser origins in Settings → H5 Access. Treat the token like an API key and regenerate it to revoke existing browser access.
Access control
The server classifies the source of each request before granting access to a capability path:
| Request | Default behavior |
|---|---|
GET /health |
Public startup probe |
| H5 shell and static assets | Public bootstrap content; they do not contain session data |
| Direct loopback request | Trusted only when client address, Host, and Origin are local and no proxy-trace header is present |
| Remote capability request while H5 is disabled | Denied for /api, /proxy, /ws, and file capabilities |
| Remote capability request while H5 is enabled | Requires a valid H5 token and an allowed browser Origin |
--auth-required / SERVER_AUTH_REQUIRED=1 |
Requires explicit authentication for capability endpoints even when H5 is disabled |
A TCP peer address of 127.0.0.1 does not prove that the end user is local. A reverse proxy must preserve the public Host or send at least one of Forwarded, X-Forwarded-*, X-Real-IP, or Via, allowing the server to distinguish proxied traffic from direct loopback traffic.
Passing tokens
- REST, provider proxy, and file endpoints:
Authorization: Bearer <token> - Browser WebSocket:
/ws/<session-id>?token=<token>
H5 mode uses the token generated in Settings. Explicit --auth-required mode can also accept a bearer token equal to the server's ANTHROPIC_API_KEY, but exposing a model credential for remote access is discouraged. Prefer H5 with its separate token.
CORS restricts browser access to responses; it is not authentication. It does not make a non-browser client safe.
HTTP API surface
Business REST APIs live under /api/* and cover:
- sessions, conversations, search, and filesystem access;
- settings, permissions, models, effort, and providers;
- agents, tasks, teams, and scheduled tasks;
- skills, plugins, the market, and MCP;
- IM adapters and Computer Use;
- diagnostics, Doctor, activity statistics, memory, and traces;
- H5 access control.
For exact request and response shapes, use the current handlers under src/server/api/. The internal /sdk/<session-id> WebSocket is used by the Claude CLI process started by the server; it is not a third-party client API.
/proxy/* is the provider protocol-translation boundary and depends on runtime authentication and model-routing state. Do not expose it as a general-purpose stateless OpenAI proxy.
Chat WebSocket
Clients connect to:
ws://127.0.0.1:3456/ws/<session-id>
Common client messages include:
user_messageandstop_generation;permission_responseandcomputer_use_permission_response;set_permission_modeandset_runtime_config;sync_stateandprewarm_session;ping.
The server sends connection and session state, text deltas, thinking, tool calls and results, permission requests, retry or fallback state, errors, task or team updates, and pong. Use src/server/ws/events.ts as the complete field contract.
The Desktop client sends a ping every 30 seconds and reconnects if no pong arrives within 10 seconds. Reconnect delay is capped at 30 seconds; it does not stop permanently after a fixed number of attempts. A custom client should reconnect, resynchronize state, and ignore unknown fields added to future messages.
Reverse-proxy checklist
For remote use:
- Enable H5 on the server, generate a separate token, and configure exact allowed origins.
- Use HTTPS so the token is never sent over a public network in cleartext.
- Proxy static assets,
/api/*,/proxy/*, and/ws/*. - Enable WebSocket upgrade for
/ws/*. - Preserve the public Host and standard proxy headers.
- Do not expose the internal
/sdk/*path.
Troubleshooting
| Symptom | Check |
|---|---|
| Port does not bind | Whether SERVER_PORT is occupied and contains a valid number |
/health works but an API returns 403 |
The request is classified as remote while H5 is disabled |
API or WebSocket returns 401 |
H5 token is missing or stale, or the WebSocket lacks a query token |
| Browser reports CORS | The page's exact Origin is in the H5 allowed-origin list |
| WebSocket reconnects repeatedly | Proxy upgrade support, token forwarding, and proxy idle timeouts |
Page returns 404 |
desktop/dist was not built or CLAUDE_H5_DIST_DIR is wrong |
| Remote traffic is treated as local | The proxy removed both the public Host and every proxy-trace header |