HTTP API 레퍼런스
ccmux 데몬은 세션 나열, 프로젝트 데이터 읽기 및 원격 클라이언트 구축을 위해 HTTP 및 JSON API를 노출합니다. 이 참조는 연결 설정, 신뢰 모델, 끝점 및 응답 유형을 다룹니다.
경로와 유형은 ccmux source. 이전 데몬 버전은 여기에 설명된 모든 엔드포인트를 지원하지 않을 수 있습니다. 실행 중인 버전의 상태 엔드포인트를 확인하세요.
전송 및 도달 가능성
동일한 경로가 두 가지 전송을 통해 제공됩니다.
| 운송 | 주소 | 기본값 |
|---|---|---|
| 로컬 유닉스 소켓 | ~/.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가 없습니다. Tailscale의 WireGuard 터널은 암호화 + ID 경계입니다. HTTP 수신기는 tailnet IP에 바인딩된 일반 HTTP입니다.
- 요청 본문이 다음으로 제한됩니다. 64KiB.
인증 및 신뢰 모델 - 먼저 읽어보세요
HTTP API에는 애플리케이션 수준 인증이 없습니다. 전달자 토큰 없음, API 키 없음, 요청별 서명 없음, IP 허용 목록 없음. 신뢰 경계는 다음과 같습니다. Tailscale tailnet: 데몬의 호스트에 연결할 수 있는 모든 호스트
100.x.x.x:7474 는 모든 엔드포인트를 호출할 수 있습니다 — 목록/생성/종료/이름 바꾸기/키 보내기, 완전한 대화형 터미널에 연결, 메모/대화/사용법을 읽어보세요.
이를 다음으로 보호하세요. Tailscale ACL, 앱 수준 인증이 아닙니다. 데몬의 tailnet IP로 라우팅할 수 있는 모든 항목을 완전히 신뢰할 수 있는 것으로 취급합니다.
요청당 2개의 엔드포인트가 검증되며 부트스트랩 장치 신뢰 + 푸시에만 해당됩니다. API를 보호하기 위한 것이 아닙니다.
POST /v1/pair유효한 일회용 페어링 토큰이 필요합니다. 플러스 구문 분석 가능한 SSH 공개 키. 이를 사용하면 해당 키가 호스트의 키에 설치됩니다.~/.ssh/authorized_keys.POST /v1/pair-token는 Unix 소켓 전용 (tailnet에는 절대 없음) 및 추가로 반환503그렇지 않은 경우listen_tailnet가 켜져 있습니다.
오류 형태
오류는 다음과 같습니다. 일반 텍스트 (text/plain): 단일 라인 메시지, 아님 JSON 봉투. 확인해보세요 상태 코드, 본문을 휴먼 메시지의 문자열로 읽습니다. 성공적인 JSON 응답은 다음과 같습니다. application/json.
| 코드 | 의미 |
|---|---|
200 |
OK (JSON 본체) |
204 |
OK, 본문 없음(kill / send-keys / 장치 등록) |
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 |
웹소켓(대화형 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 (유닉스 전용) |
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. 방출killedSSE 이벤트입니다.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/mosh. 용도 coder/websocket 프레이밍.
- 클라이언트 → 서버, 바이너리 프레임: 원시 stdin 바이트(키 입력).
- 클라이언트 → 서버, 텍스트 프레임: 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 (SSE)
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가 없는 경우).
페어링 및 푸시(모바일)
기본 푸시(APN/iOS, FCM/Android) 및 SSH 키 설치에 대한 선택적 흐름을 통해 장치에서 다음을 수행할 수 있습니다. ssh/mosh 첨부합니다. 아님 tailnet을 통해 읽기/작동 엔드포인트를 사용하는 데 필요합니다.
POST /v1/pair-token(Unix 소켓만 해당) →PairTokenResponse. 일회용 토큰 발행(128비트 16진수, 일회용, 5분 TTL) + accmux://pair?…딥링크.503만약listen_tailnet꺼져 있습니다.POST /v1/pair(PairRequest) →PairResponse. 토큰 사용: 장치의 SSH 공개 키를 설치하고 선택적으로 푸시 토큰 인라인을 등록합니다.401유효하지 않거나 만료된 토큰에 대해. tailnet에서 접근 가능합니다.POST /v1/devices(RegisterDeviceRequest) →204. 이미 페어링된 호스트에 푸시 토큰을 등록/새로 고칩니다. SSH로 식별되는 장치public_key(SHA-256 해시로만 저장됨)provider는apns(기본값) 또는fcm; APN 요구 사항env=development/production, FCM은 비어 있어야 합니다.env.POST /v1/devices/test({"public_key":"…"}) →204. 해당 키에 대한 확인 푸시를 장치에 수행합니다.
두 가지 전환 시 실행을 푸시합니다. 세션이 시작됩니다. needs_input및
active → idle ("에이전트 완료"). 푸시의 세션 ID는 다음과 같습니다.
local/SESSIONNAME. APN과 FCM은 기본적으로 꺼짐 서버 측 구성이 필요합니다. FCM 라우팅이 존재하지만 Android 전송이 아직 연결되지 않았습니다.
종류
복사됨 internal/daemon/protocol.go (JSON 태그가 있는 Go 구조체).
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. - 요청 본문이 다음으로 제한됩니다. 64KiB.
예시
# 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 개선에 참여해 주세요
버그, 이해하기 어려운 설명, 어색한 번역을 발견하셨나요? 작은 개선도 환영합니다. 이슈를 등록하거나 풀 리퀘스트를 보내 주세요.
오류나 오래된 내용을 발견하셨나요? GitHub에서 이 페이지를 편집하세요..