HTTP-API-Referenz
Der ccmux-Daemon stellt einen HTTP und einen JSON API zum Auflisten von Sitzungen, zum Lesen von Projektdaten und zum Erstellen von Remote-Clients bereit. Diese Referenz behandelt den Verbindungsaufbau, das Vertrauensmodell, Endpunkte und Antworttypen.
Die Routen und Typen werden im verwaltet ccmux source. Ältere Daemon-Versionen unterstützen möglicherweise nicht jeden hier dokumentierten Endpunkt. Überprüfen Sie den Integritätsendpunkt auf die laufende Version.
Transport & Erreichbarkeit
Dieselben Routen werden über zwei Transporte bedient:
| Transport | Adresse | Standard |
|---|---|---|
| Lokaler Unix-Socket | ~/.local/state/ccmux/ccmuxd.sock (Modus 0600) |
immer an |
| Tailnet HTTP | http://TAILNET-IPv4:PORT (Port-Standardeinstellung 7474) |
aus |
Um einen Daemon von einem anderen Gerät aus zu erreichen, auf dem -Daemon Host: eingestellt
daemon.listen_tailnet = true (optional daemon.tailnet_port) in
~/.config/ccmux/config.toml und starten Sie ccmuxd neu. Die Bindungsadresse ist die des Hosts tailscale ip -4. Wenn Tailscale nicht ausgeführt wird, startet der Tailnet-Listener nicht stillschweigend (nur der Unix-Socket wird bedient).
- Alle Pfade sind unter
/v1/. Es gibt keine/v2. - Anforderungs-/Antwortkörper sind
application/json— außer/v1/events(text/event-stream) und der Attach-Endpunkt (ein WebSocket-Upgrade). - Kein TLS. Der WireGuard-Tunnel von Tailscale ist die Grenze zwischen Verschlüsselung und Identität. Der HTTP-Listener ist einfach HTTP an die Tailnet-IP gebunden.
- Anforderungstexte sind auf begrenzt 64 KiB.
Authentifizierungs- und Vertrauensmodell – lesen Sie dies zuerst
Auf dem HTTP API gibt es keine Authentifizierung auf Anwendungsebene. Kein Bearer-Token, kein API-Schlüssel, keine Signatur pro Anfrage, keine IP-Zulassungsliste. Die Vertrauensgrenze ist Ihr Tailscale-Tailnet: jeder Host, der den Daemon erreichen kann
100.x.x.x:7474 kann jeden Endpunkt aufrufen – Schlüssel auflisten / erstellen / töten / umbenennen / senden, an ein vollständig interaktives Terminal anschließenund Notizen/Konversationen/Nutzung lesen.
Sichern Sie dies mit Tailscale ACLs, keine Authentifizierung auf App-Ebene. Behandeln Sie alles, was zur Tailnet-IP des Daemons weitergeleitet werden kann, als vollständig vertrauenswürdig.
Zwei Endpunkte validieren pro Anfrage und nur zum Bootstrap-Gerätevertrauen + Push – nicht zum Schutz des API:
POST /v1/pairerfordert ein gültiges, einmal verwendbares Pairing-Token plus ein analysierbarer öffentlicher SSH-Schlüssel. Durch das Einlösen wird dieser Schlüssel auf dem Host installiert~/.ssh/authorized_keys.POST /v1/pair-tokenist Nur Unix-Socket (nie im Tailnet) und kehrt zusätzlich zurück503es sei dennlisten_tailnetist aktiviert.
Fehlerform
Fehler sind Klartext (text/plain): eine einzeilige Nachricht, nicht ein JSON-Umschlag. Überprüfen Sie die Statuscodeund lesen Sie den Text als Zeichenfolge für die menschliche Nachricht. Erfolgreiche JSON-Antworten sind application/json.
| Code | Bedeutung |
|---|---|
200 |
OK (JSON-Körper) |
204 |
OK, kein Text (Kill / Send-Keys / Geräteregister) |
400 |
Falsche Eingabe/Validierungsfehler |
401 |
ungültiges oder abgelaufenes Pairing-Token (/v1/pair nur) |
404 |
nicht gefunden – oder dieser Daemon ist älter als der Endpunkt |
405 |
falsche HTTP-Methode |
500 |
Server / tmux / Gerüstfehler |
503 |
Funktion nicht verfügbar (z. B. /v1/pair-token mit listen_tailnet aus) |
Der API entwickelt sich weiter Hinzufügen JSON-Felder (alle optionalen Felder verwenden omitempty) und Hinzufügen Routen. Behandle a 404 auf einem gesamten Endpunkt als „Dieser Daemon ist älter als diese Funktion“ und wird ordnungsgemäß heruntergefahren.
Endpunktzusammenfassung
| Methode | Pfad | Antwort |
|---|---|---|
GET |
/v1/health |
HealthInfo |
GET |
/v1/peers |
[]PeerInfo |
GET |
/v1/sessions |
[]SessionState |
POST |
/v1/sessions |
SessionState (erstellen oder anhängen) |
POST |
/v1/sessions/bare |
NewBareSessionResponse |
POST |
/v1/sessions/NAME/kill |
204 |
POST |
/v1/sessions/NAME/rename |
SessionState |
POST |
/v1/sessions/NAME/send-keys |
204 |
GET |
/v1/sessions/NAME/preview |
PreviewResponse |
GET |
/v1/sessions/NAME/attach |
WebSocket (interaktives PTY) |
GET |
/v1/projects |
[]ProjectInfo |
POST |
/v1/projects |
NewProjectResponse |
GET |
/v1/conversations |
[]Conversation |
GET |
/v1/usage?window=… |
AgentUsage |
GET |
/v1/notes?project=…[&file=…] |
[]NoteEntry oder NoteContent |
GET |
/v1/notes/search?project=…&q=… |
[]SearchHit |
GET |
/v1/events |
SSE-Stream von SessionEvent |
POST |
/v1/pair-token (nur Unix) |
PairTokenResponse |
POST |
/v1/pair |
PairResponse |
POST |
/v1/devices |
204 |
POST |
/v1/devices/test |
204 |
(Wo ein Pfad angezeigt wird NAME ist das Sitzungsnamenpfadsegment tmux, d. h. /v1/sessions/{name}/kill.)
Endpunktdetails
Sitzungen
GET /v1/sessions→[]SessionState. Jede Sitzung, die dieser Daemon verwaltet, mit Daemon-Ableitungstate(active/idle/needs_input/error/unknown).POST /v1/sessions(NewSessionRequest,projecterforderlich) →SessionState. Erstellen oder Anhängen einer projektgebundenen Agentensitzung (idempotent für tmux-Name).pathist standardmäßig aufPROJECTS_ROOT/PROJECTauf dem Daemon-Host.POST /v1/sessions/bare(NewBareSessionRequest) →NewBareSessionResponse. Eine reine Shell-Sitzung (kein Projekt, kein Gerüst).POST /v1/sessions/NAME/kill→204. Gibt einkilledSSE-Ereignis.POST /v1/sessions/NAME/rename(RenameRequest) →SessionState.NAMEist der aktuelle Name; Der Körper trägt das Neue.POST /v1/sessions/NAME/send-keys(SendKeysRequest) →204. Durchgereicht antmux send-keys(z. B. eine Antwort eingeben +\n).GET /v1/sessions/NAME/preview?lines=N→PreviewResponse. Die letzten N Zeilen des aktiven Bereichs als Klartext (ANSI entfernt).Nist1..200, Standard24. Ein leichter Einblick, ohne die Anschlussbuchse zu öffnen.
Interaktives Terminal – GET /v1/sessions/NAME/attach (WebSocket)
Upgrade auf einen WebSocket mit Bridge zu einem echten tmux attach-session in einem PTY: ein echtes interaktives Terminal (Live-Ausgabe, Eingabe, Größenänderung) ohne ssh/mosh. Verwendungsmöglichkeiten coder/websocket Rahmen.
- Client → Server, Binärrahmen: Roh-Standardbytes (Tastenanschläge).
- Client → Server, Textrahmen: JSON
{"cols":N,"rows":N}zum Ändern der Größe. - Server → Client, Binärrahmen: Roh-PTY-Ausgabebytes.
Die anfängliche PTY-Größe beträgt 80x24 bis zur ersten Größenänderung. Der Server pingt alle 25 Sekunden mit einer Frist von 10 Sekunden – antworten Sie auf Pongs oder rechnen Sie mit einem Teardown.
InsecureSkipVerify ist gesetzt (Nr Origin prüfen). Nur die Steckdose schließen
wird getrennt; Die tmux-Sitzung läuft weiter. Dies ist dem Polling vorzuziehen
/preview für eine interaktive Ansicht.
Projekte
GET /v1/projects→[]ProjectInfo. Projekte, die im Projektstammverzeichnis des Daemons entdeckt und mit seinem Hostnamen markiert sind.POST /v1/projects(NewProjectRequest,nameerforderlich) →NewProjectResponse. Erstellt ein neues Projekt (nur Verzeichnis – NrCLAUDE.md/git) und startet eine Agentensitzung.namemuss ein einzelnes, nicht ausgeblendetes Pfadsegment sein (Nr/,\, kein Anfang.).
Gespräche, Verwendung, Notizen
GET /v1/conversations→[]Conversation. Frühere Agentenprotokolle, aktuellste zuerst;idist die eigene UUID des Agenten (its--resumeid).GET /v1/usage?window=DURATION→AgentUsage. Pro-Agent-Token + Kosten über ein rollierendes Fenster (Go Dauer wie2h,24h; Standard5h).GET /v1/notes?project=NAME→[]NoteEntry; mit&file=REL→NoteContent.filemuss projektbezogen sein.mdPfad, Nr...GET /v1/notes/search?project=NAME&q=QUERY→[]SearchHit. Ripgrep-Rückseite. (Ältere Dämonen404– als „Suche nicht verfügbar“ behandeln.)
Live-Updates – GET /v1/events (SSE)
text/event-stream. Jeder data: Frame ist ein JSON SessionEvent; kind ist
created / killed / state_change / needs_input. Herzschläge: : connected beim Öffnen, : ping alle 20 Sekunden (Kommentarzeilen beginnen mit : sind ignorierbar). Ein event: drops / data: N -Frame bedeutet, dass Sie N Ereignisse verpasst haben und einen erneuten Abruf durchführen sollten /v1/sessions zur Neusynchronisierung.
Gesundheit und Entdeckung
GET /v1/health→HealthInfo. Lebendigkeit + Identitätsprüfung.GET /v1/peers→[]PeerInfo. Jeder Tailnet-Peer + ob jeder ccmuxd ausführt (gibt zurück[], nicht500, wenn Tailscale nicht vorhanden ist).
Pairing & Push (mobil)
Optionaler Ablauf für natives Push (APNs/iOS, FCM/Android) und Installation eines SSH-Schlüssels, damit das Gerät dies kann ssh/mosh anhängen. Nicht ist erforderlich, um die Lese-/Aktionsendpunkte über das Tailnet zu verwenden.
POST /v1/pair-token(nur Unix-Socket) →PairTokenResponse. Prägen Sie einen einmaligen Token (128-Bit-Hex, einmalig, 5-Minuten-TTL) + accmux://pair?…Deep-Link.503wennlisten_tailnetist ausgeschaltet.POST /v1/pair(PairRequest) →PairResponse. Lösen Sie einen Token ein: Installieren Sie den öffentlichen Schlüssel SSH des Geräts und registrieren Sie optional einen Push-Token inline.401bei ungültigem/abgelaufenem Token. Erreichbar über das Tailnet.POST /v1/devices(RegisterDeviceRequest) →204. Registrieren/aktualisieren Sie ein Push-Token auf einem bereits gekoppelten Host. Gerät identifiziert durch seinen SSHpublic_key(nur als SHA-256-Hash gespeichert).provideristapns(Standard) oderfcm; APNs-Anforderungenenv=development/production, FCM muss leer seinenv.POST /v1/devices/test({"public_key":"…"}) →204. Bestätigungs-Push an das Gerät für diesen Schlüssel.
Löst zwei Übergänge aus: eine Sitzung beginnt needs_input, und
active → idle („Agent beendet“). Die Sitzungs-ID des Pushs lautet
local/SESSIONNAME. APNs und FCM sind standardmäßig deaktiviert und serverseitige Konfiguration erforderlich; FCM-Routing ist vorhanden, aber die Android-Bereitstellung ist noch nicht verkabelt.
Typen
Kopiert von internal/daemon/protocol.go (Go-Strukturen mit ihren JSON-Tags).
type SessionState struct {
Name string `json:"name"`
Host string `json:"host"` // "local" or a remote host name
Project string `json:"project"`
Path string `json:"path"` // session working directory
State string `json:"state"` // active|idle|needs_input|error|unknown
Attached bool `json:"attached"`
Windows int `json:"windows"`
Created time.Time `json:"created"`
LastChange time.Time `json:"last_change"`
PromptCount int `json:"prompt_count"`
Agent string `json:"agent,omitempty"`
}
type HealthInfo struct {
OK bool `json:"ok"`
Hostname string `json:"hostname"`
Version string `json:"version"`
Sessions int `json:"sessions"`
SleepMode string `json:"sleep_mode"` // off|safe|dangerous|very_dangerous
}
type SessionEvent struct {
At time.Time `json:"at"`
Kind string `json:"kind"` // state_change|created|killed|needs_input
Session SessionState `json:"session"`
}
type NewSessionRequest struct {
Project string `json:"project"`
Path string `json:"path"` // defaults to PROJECTS_ROOT/PROJECT
Continue bool `json:"continue"` // start the agent with --continue
Name string `json:"name,omitempty"`
Agent string `json:"agent,omitempty"`
}
type NewBareSessionRequest struct {
Name string `json:"name,omitempty"`
Path string `json:"path,omitempty"`
Agent string `json:"agent,omitempty"`
}
type NewBareSessionResponse struct {
Session string `json:"session"`
Path string `json:"path"`
Host string `json:"host"`
}
type NewProjectRequest struct {
Name string `json:"name"`
Agent string `json:"agent,omitempty"`
}
type NewProjectResponse struct {
Session string `json:"session"`
Path string `json:"path"`
Host string `json:"host"`
}
type ProjectInfo struct {
Name string `json:"name"`
Host string `json:"host"`
Path string `json:"path"` // absolute on the daemon host
HasGit bool `json:"has_git"`
HasCM bool `json:"has_cm"`
HasAgents bool `json:"has_agents,omitempty"`
HasDocs bool `json:"has_docs"`
Agent string `json:"agent,omitempty"`
Modified time.Time `json:"modified"`
}
type PeerInfo struct {
Hostname string `json:"hostname"`
Addr string `json:"addr"` // tailnet IPv4
OS string `json:"os"`
Online bool `json:"online"`
RunsCCMuxd bool `json:"runs_ccmuxd"`
Port *int `json:"port,omitempty"`
}
type Conversation struct {
ID string `json:"id"` // agent UUID (its --resume id)
Agent string `json:"agent"`
Project string `json:"project,omitempty"`
Path string `json:"path,omitempty"`
Preview string `json:"preview,omitempty"`
Modified time.Time `json:"modified"`
}
type AgentUsage struct {
Claude UsageSummary `json:"claude"`
Codex UsageSummary `json:"codex"`
Antigravity UsageSummary `json:"antigravity"`
}
type UsageSummary struct {
HasData bool `json:"has_data"`
WindowSeconds int `json:"window_seconds"`
Prompts int `json:"prompts"`
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
EstimatedCost float64 `json:"estimated_cost"` // USD
}
type NoteEntry struct {
Rel string `json:"rel"`
Dir string `json:"dir"`
Display string `json:"display"`
Modified time.Time `json:"modified"`
}
type NoteContent struct {
Rel string `json:"rel"`
Content string `json:"content"`
}
type SearchHit struct {
Rel string `json:"rel"`
LineNum int `json:"line_num"`
Snippet string `json:"snippet"`
}
type PreviewResponse struct {
Lines int `json:"lines"`
Content string `json:"content"`
}
type RenameRequest struct{ Name string `json:"name"` }
type SendKeysRequest struct{ Keys string `json:"keys"` }
type PairRequest struct {
Token string `json:"token"`
PublicKey string `json:"public_key"`
DeviceToken string `json:"device_token,omitempty"`
APNsEnv string `json:"apns_env,omitempty"` // development|production
}
type PairResponse struct{ Hostname string `json:"hostname"`; Version string `json:"version"` }
type PairTokenResponse struct{ Token string `json:"token"`; URL string `json:"url"` }
type RegisterDeviceRequest struct {
Token string `json:"token"`
Env string `json:"env,omitempty"` // apns: development|production; fcm: empty
PublicKey string `json:"public_key"`
Provider string `json:"provider,omitempty"` // apns (default) | fcm
}
// POST /v1/devices/test body is { "public_key": "..." }
Validierungsregeln zur clientseitigen Spiegelung
- tmux session names darf nicht enthalten
/,\, oder:. - Projektnamen (für
POST /v1/projects) muss ein einzelnes, nicht ausgeblendetes Pfadsegment sein. - notiert Dateipfade muss projektbezogen sein, nein
.., endet in.md. - Anforderungstexte sind auf begrenzt 64 KiB.
Beispiele
# Health (over the tailnet)
curl -s http://100.75.64.20:7474/v1/health | jq
# List sessions
curl -s http://100.75.64.20:7474/v1/sessions | jq
# Type a reply into a session and press Enter
curl -s -X POST http://100.75.64.20:7474/v1/sessions/c-auth/send-keys \
-H 'content-type: application/json' \
-d '{"keys":"yes, ship it\n"}'
# Peek at the last 40 lines of a pane
curl -s 'http://100.75.64.20:7474/v1/sessions/c-auth/preview?lines=40' | jq -r .content
# Subscribe to live events
curl -sN http://100.75.64.20:7474/v1/events
Der Go-Referenzclient in
internal/daemon/client.go
ist der kanonische Verbraucher und eine nützliche Zuordnung von Methode → Endpunkt.
Hilf mit, ccmux zu verbessern
Fehler gefunden, eine Anleitung unklar oder eine Übersetzung verbesserungswürdig? Auch kleine Beiträge sind willkommen. Erstelle ein Issue oder einen Pull Request.
Haben Sie einen Fehler entdeckt oder ist etwas veraltet? Bearbeiten Sie diese Seite auf GitHub.