Справочник HTTP API
Демон ccmux предоставляет HTTP и JSON API для листинга сеансов, чтения данных проекта и создания удаленных клиентов. В этом справочнике описывается настройка соединения, модель доверия, конечные точки и типы ответов.
Маршруты и типы сохраняются в ccmux source. Старые версии демонов могут не поддерживать все описанные здесь конечные точки; проверьте конечную точку работоспособности для работающей версии.
Транспорт и доступность
Одни и те же маршруты обслуживаются двумя транспортами:
| Транспорт | Адрес | По умолчанию |
|---|---|---|
| Локальный сокет Unix | ~/.local/state/ccmux/ccmuxd.sock (режим 0600) |
всегда включен |
| tailnet HTTP | http://TAILNET-IPv4:PORT (порт по умолчанию 7474) |
выкл. |
Чтобы связаться с демоном с другого устройства, на демон хост: установлено
daemon.listen_tailnet = true (необязательно daemon.tailnet_port) в
~/.config/ccmux/config.toml и перезапустите ccmuxd. Адрес привязки — это адрес хоста tailscale ip -4. Если Tailscale не запущен, прослушиватель tailnet не запускается автоматически (обслуживается только сокет Unix).
- Все пути находятся под
/v1/. Нет/v2. - Тела запроса/ответа
application/json— кроме/v1/events(text/event-stream) и конечную точку подключения (обновление WebSocket). - Нет TLS. Туннель WireGuard Tailscale — это граница шифрования и идентификации; прослушиватель HTTP — это обычный HTTP, привязанный к IP-адресу tailnet.
- Тело запроса ограничено 64 КиБ.
Модель аутентификации и доверия — прочтите это в первую очередь
На HTTP API нет аутентификации на уровне приложения. Ни токена носителя, ни ключа API, ни подписи каждого запроса, ни списка разрешенных IP-адресов. Граница доверия ваша tailnet Tailscale: любой хост, который может связаться с демоном.
100.x.x.x:7474 может вызывать любую конечную точку — список/создать/уничтожить/переименовать/отправить ключи, прикрепить к полностью интерактивному терминалуи читайте заметки/разговоры/использование.
Защитите это с помощью Tailscale ACL, а не аутентификация на уровне приложения. Считайте все, что может маршрутизироваться к IP-адресу tailnet демона, полностью доверенным.
Две конечные точки проверяют каждый запрос и только для начальной загрузки устройства Trust + Push, а не для защиты API:
POST /v1/pairтребуется действительный одноразовый токен сопряжения. плюс анализируемый открытый ключ SSH. При его активации этот ключ устанавливается в файл хоста.~/.ssh/authorized_keys.POST /v1/pair-token— это Только Unix-сокет (никогда не в tailnet) и дополнительно возвращает503если толькоlisten_tailnetвключен.
Форма ошибки
Ошибки обычный текст (text/plain): однострочное сообщение, нет конверт JSON. Проверьте код состоянияи прочитайте тело как строку человеческого сообщения. Успешные ответы JSON: application/json.
| Код | Значение |
|---|---|
200 |
ОК (тело JSON) |
204 |
ОК, нет тела (уничтожить/отправить ключи/регистрировать устройство) |
400 |
неправильный ввод/сбой проверки |
401 |
недействительный или просроченный токен сопряжения (/v1/pair только) |
404 |
не найдено - или этот демон предшествует конечной точке |
405 |
неправильный метод HTTP |
500 |
сервер / tmux / сбой платформы |
503 |
функция недоступна (например. /v1/pair-token с listen_tailnet выкл.) |
API развивается добавление Поля JSON (во всех необязательных полях используются omitempty) и добавление маршруты. лечить 404 на всей конечной точке как «этот демон старше этой функции» и плавно деградировать.
Сводка конечных точек
| Метод | Путь | Ответ |
|---|---|---|
GET |
/v1/health |
HealthInfo |
GET |
/v1/peers |
[]PeerInfo |
GET |
/v1/sessions |
[]SessionState |
POST |
/v1/sessions |
SessionState (создать или прикрепить) |
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) |
GET |
/v1/projects |
[]ProjectInfo |
POST |
/v1/projects |
NewProjectResponse |
GET |
/v1/conversations |
[]Conversation |
GET |
/v1/usage?window=… |
AgentUsage |
GET |
/v1/notes?project=…[&file=…] |
[]NoteEntry или NoteContent |
GET |
/v1/notes/search?project=…&q=… |
[]SearchHit |
GET |
/v1/events |
SSE-поток SessionEvent |
POST |
/v1/pair-token (только Unix) |
PairTokenResponse |
POST |
/v1/pair |
PairResponse |
POST |
/v1/devices |
204 |
POST |
/v1/devices/test |
204 |
(Там, где указан путь NAME это сегмент пути имени сеанса tmux, т.е. /v1/sessions/{name}/kill.)
Подробности конечной точки
Сессии
GET /v1/sessions→[]SessionState. Каждый сеанс, которым управляет этот демон, с помощью производных от демонаstate(active/idle/needs_input/error/unknown).POST /v1/sessions(NewSessionRequest,projectобязательно) →SessionState. Создайте или подключите сеанс агента, привязанный к проекту (идемпотентный по имени tmux).pathпо умолчаниюPROJECTS_ROOT/PROJECTна хосте демона.POST /v1/sessions/bare(NewBareSessionRequest) →NewBareSessionResponse. Сеанс только оболочки (без проекта, без каркаса).POST /v1/sessions/NAME/kill→204. ИздаетkilledСобытие SSE.POST /v1/sessions/NAME/rename(RenameRequest) →SessionState.NAME— текущее имя; тело несет новое.POST /v1/sessions/NAME/send-keys(SendKeysRequest) →204. Прошел доtmux send-keys(например, введите ответ +\n).GET /v1/sessions/NAME/preview?lines=N→PreviewResponse. Последние N строк активной панели в виде обычного текста (без ANSI).N— это1..200, по умолчанию24. Легкий просмотр без открытия разъема для крепления.
Интерактивный терминал — GET /v1/sessions/NAME/attach (Веб-сокет)
Обновление до WebSocket, подключенного к реальному tmux attach-session в PTY: настоящий интерактивный терминал (живой вывод, ввод, изменение размера) без ssh/мош. Использование coder/websocket кадр.
- клиент → сервер, двоичный кадр: необработанные байты стандартного ввода (нажатия клавиш).
- клиент → сервер, текстовый фрейм: JSON
{"cols":N,"rows":N}для изменения размера. - сервер → клиент, двоичный кадр: необработанные выходные байты PTY.
Начальный размер PTY 80x24 до первого изменения размера. Сервер пингует каждые 25 секунд с крайним сроком в 10 секунд — отвечайте на запросы или ожидайте отключения.
InsecureSkipVerify установлен (нет Origin проверьте). Только закрытие сокета
отсоединяется; Сеанс tmux продолжает работать. Предпочитаю это опросу
/preview для интерактивного просмотра.
Проекты
GET /v1/projects→[]ProjectInfo. Проекты, обнаруженные в корне проектов демона, помеченные его именем хоста.POST /v1/projects(NewProjectRequest,nameобязательно) →NewProjectResponse. Создает новый проект (только каталог — нетCLAUDE.md/git) и запускает сеанс агента.nameдолжен представлять собой один нескрытый сегмент пути (нет/,\, без начального.).
Разговоры, использование, заметки
GET /v1/conversations→[]Conversation. Стенограммы прошлых агентов, сначала самые последние;id— собственный UUID агента (его--resumeидентификатор).GET /v1/usage?window=DURATION→AgentUsage. Токен для каждого агента + стоимость в течение скользящего окна (длительность Go, например2h,24h; по умолчанию5h).GET /v1/notes?project=NAME→[]NoteEntry; с&file=REL→NoteContent.fileдолжен быть родственником проекта.mdпуть, нет...GET /v1/notes/search?project=NAME&q=QUERY→[]SearchHit. Поддерживается Ripgrep. (старые демоны404— рассматривать как «поиск недоступен».)
Постоянные обновления — GET /v1/events (ССЕ)
text/event-stream. Каждый data: рама JSON SessionEvent; kind — это
created / killed / state_change / needs_input. Сердцебиение: : connected при открытии, : ping каждые 20 секунд (строки комментариев начинаются с : игнорируются). Ан event: drops / data: N фрейм означает, что вы пропустили N событий и должны выполнить повторную выборку /v1/sessions для повторной синхронизации.
Здоровье и открытия
GET /v1/health→HealthInfo. Живость + проверка идентичности.GET /v1/peers→[]PeerInfo. Каждый узел tailnet + работает ли каждый из них ccmuxd (возвращает[], а не500, когда Tailscale отсутствует).
Сопряжение и отправка (мобильное устройство)
Дополнительный процесс для встроенной отправки (APNs/iOS, FCM/Android) и установки ключа SSH, чтобы устройство могло ssh/mosh прикрепляю. Нет требуется для использования конечных точек чтения/действия через tailnet.
POST /v1/pair-token(только Unix-сокет) →PairTokenResponse. Выпустите одноразовый токен (128-битный шестнадцатеричный, одноразовый, TTL 5 минут) +ccmux://pair?…глубокая ссылка.503еслиlisten_tailnetвыключен.POST /v1/pair(PairRequest) →PairResponse. Активируйте токен: установите открытый ключ устройства SSH, при необходимости зарегистрируйте встроенный push-токен.401о недействительном токене/токене с истекшим сроком действия. Доступен через tailnet.POST /v1/devices(RegisterDeviceRequest) →204. Зарегистрируйте/обновите push-токен на уже сопряженном хосте. Устройство идентифицировано по SSH.public_key(хранится только как хэш SHA-256).provider— этоapns(по умолчанию) илиfcm; потребности APNenv=development/production, FCM должен быть пустымenv.POST /v1/devices/test({"public_key":"…"}) →204. Отправьте подтверждение на устройство для этого ключа.
Вызывает огонь при двух переходах: вход в сеанс needs_inputи
active → idle («агент закончил работу»). Идентификатор сеанса push-уведомления:
local/SESSIONNAME. APN и FCM по умолчанию отключено и требуется настройка на стороне сервера; Маршрутизация FCM существует, но доставка Android еще не подключена.
Типы
Скопировано из internal/daemon/protocol.go (структуры Go с их тегами 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": "..." }
Правила проверки для зеркалирования на стороне клиента
- tmux session names не должен содержать
/,\или:. - названия проектов (для
POST /v1/projects) должен представлять собой один нескрытый сегмент пути. - отмечает пути к файлам должен быть привязан к проекту, нет
.., заканчивающийся на.md. - Тело запроса ограничено 64 КиБ.
Примеры
# 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
Эталонный клиент Go в
internal/daemon/client.go
— канонический потребитель и полезная карта метода → конечной точки.
Помогите улучшить ccmux
Нашли ошибку, непонятную инструкцию или неудачный перевод? Мы рады любым улучшениям. Создайте issue или отправьте pull request.
Обнаружили ошибку или что-то устарело? Отредактируйте эту страницу на GitHub..