Référence de l’API HTTP

Le démon ccmux expose un HTTP et un JSON API pour répertorier les sessions, lire les données du projet et créer des clients distants. Cette référence couvre la configuration de la connexion, le modèle de confiance, les points de terminaison et les types de réponse.

Les routes et les types sont conservés dans le ccmux source. Les anciennes versions du démon peuvent ne pas prendre en charge tous les points de terminaison documentés ici ; vérifiez le point de terminaison d’intégrité pour la version en cours d’exécution.

Transport et accessibilité

Les mêmes itinéraires sont desservis sur deux transports :

Transports Adresse Par défaut
Socket Unix local ~/.local/state/ccmux/ccmuxd.sock (mode 0600) toujours activé
tailnet HTTP http://TAILNET-IPv4:PORT (port par défaut 7474) désactivé

Pour accéder à un démon depuis un autre appareil, sur le démon hôte : défini daemon.listen_tailnet = true (en option daemon.tailnet_port) dans ~/.config/ccmux/config.toml et redémarrez ccmuxd. L'adresse de liaison est celle de l'hôte tailscale ip -4. Si Tailscale n'est pas en cours d'exécution, l'écouteur tailnet ne démarre pas silencieusement (seule le socket Unix est servi).

Modèle d'authentification et de confiance — lisez ceci en premier

Il n'y a pas d'authentification au niveau de l'application sur le HTTP API. Pas de jeton de porteur, pas de clé API, pas de signature par demande, pas de liste blanche IP. La limite de confiance est votre filet arrière Tailscale: tout hôte pouvant atteindre le démon 100.x.x.x:7474 peut appeler chaque point de terminaison - lister / créer / tuer / renommer / envoyer des clés, attacher à un terminal interactif completet lire des notes/conversations/utilisation.

Sécurisez-le avec Listes de contrôle d'accès Tailscale, pas d'authentification au niveau de l'application. Traitez tout ce qui peut être acheminé vers l’adresse IP tailnet du démon comme étant entièrement fiable.

Deux points de terminaison sont validés par requête, et uniquement pour amorcer la confiance + le push du périphérique — et non pour protéger le API :

Forme d'erreur

Les erreurs sont texte brut (text/plain) : un message sur une seule ligne, pas une enveloppe JSON. Vérifiez le code d'état, et lisez le corps comme une chaîne pour le message humain. Les réponses JSON réussies sont application/json.

Code Signification
200 OK (corps JSON)
204 OK, pas de corps (kill / send-keys / registre de périphérique)
400 mauvaise entrée/échec de validation
401 jeton d'association invalide ou expiré (/v1/pair uniquement)
404 introuvable — ou ce démon est antérieur au point de terminaison
405 mauvaise méthode HTTP
500 serveur / tmux / panne d'échafaudage
503 fonctionnalité indisponible (par ex. /v1/pair-token avec listen_tailnet désactivé)

Le API évolue de ajout Champs JSON (tous les champs facultatifs utilisent omitempty) et ajout itinéraires. Traiter un 404 sur l'ensemble d'un point de terminaison car « ce démon est plus ancien que cette fonctionnalité » et se dégrade progressivement.

Résumé du point de terminaison

Méthode Chemin Réponse
GET /v1/health HealthInfo
GET /v1/peers []PeerInfo
GET /v1/sessions []SessionState
POST /v1/sessions SessionState (créer ou joindre)
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 (PTY interactif)
GET /v1/projects []ProjectInfo
POST /v1/projects NewProjectResponse
GET /v1/conversations []Conversation
GET /v1/usage?window=… AgentUsage
GET /v1/notes?project=…[&file=…] []NoteEntry ou NoteContent
GET /v1/notes/search?project=…&q=… []SearchHit
GET /v1/events Flux SSE de SessionEvent
POST /v1/pair-token (Unix uniquement) PairTokenResponse
POST /v1/pair PairResponse
POST /v1/devices 204
POST /v1/devices/test 204

(Là où un chemin indique NAME il s'agit du segment de chemin du nom de session tmux, c'est-à-dire /v1/sessions/{name}/kill.)

Détails du point de terminaison

Sessions

Borne interactive — GET /v1/sessions/NAME/attach (WebSocket)

Mise à niveau vers un WebSocket ponté vers un véritable tmux attach-session dans un PTY : une véritable borne interactive (sortie live, entrée, redimensionnement) sans chut/mosh. Utilisations coder/websocket encadrement.

La taille initiale du PTY est 80x24 jusqu'au premier redimensionnement. Le serveur envoie un ping toutes les 25 secondes avec un délai de 10 secondes – répondez aux pongs ou attendez-vous à un démontage. InsecureSkipVerify est défini (non Origin vérification). Fermeture de la prise uniquement se détache; la session tmux continue de s'exécuter. Préférez ceci aux sondages /preview pour une vue interactive.

Projets

Conversations, utilisation, notes

Mises à jour en direct — GET /v1/events (ESS)

text/event-stream. Chaque data: le cadre est un JSON SessionEvent; kind est created / killed / state_change / needs_input. Pulsations cardiaques: : connected à l'ouverture, : ping toutes les 20 secondes (lignes de commentaires commençant par : sont ignorés). Un event: drops / data: N le cadre signifie que vous avez manqué N événements et que vous devez récupérer à nouveau /v1/sessions pour resynchroniser.

Santé et découverte

Couplage et push (mobile)

Flux facultatif pour le push natif (APN/iOS, FCM/Android) et installation d'une clé SSH afin que l'appareil puisse ssh/mosh joindre. Non requis pour utiliser les points de terminaison de lecture/action sur le tailnet.

Pousse le feu sur deux transitions : une session entrante needs_input, et active → idle (« agent terminé »). L'identifiant de session du push est local/SESSIONNAME. Les APN et FCM sont désactivé par défaut et nécessite une configuration côté serveur ; Le routage FCM existe mais la livraison Android n'est pas encore câblée.

Types

Copié depuis internal/daemon/protocol.go (structures Go avec leurs balises JSON).

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": "..." }

Règles de validation pour refléter côté client

Exemples

# 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

Le client de référence Go dans internal/daemon/client.go est le consommateur canonique et une carte utile méthode → point de terminaison.

Contribuez à améliorer ccmux

Vous avez trouvé un bug, une explication peu claire ou une traduction à améliorer ? Toutes les contributions sont bienvenues. Ouvrez une issue ou proposez une pull request.


Vous avez repéré une erreur ou quelque chose de obsolète ? Modifier cette page sur GitHub.