매니저 HTTP API
매니저 HTTP API
편집기, Ember, ash, CLI가 모두 같은 API를 씁니다. 계약은 docs/api/manager.openapi.yaml이고, 매니저가 정확히 그 경로만 내는지 테스트가 확인합니다.
두 모드
| 모드 | 리슨 주소 | 수명 | 토큰 |
|---|---|---|---|
| 임시(기본) | 127.0.0.1, 임의 포트 | 요청과 스트림이 없는 상태가 --idle-timeout초(기본 120초) 이어지면 끝남 | 토큰 하나를 managers/<pid>.json(0600)에 씀 |
전용(--dedicated) | 지정한 호스트와 포트 | 멈출 때까지 실행 | 마스터 토큰과 공유 토큰, manager.db에 해시로 저장 |
바깥에서의 접근은 항상 전용 매니저를 거칩니다. 커널은 루프백에만 열립니다.
인증
모든 요청은 Authorization: Bearer <token>을 붙입니다. 이벤트 스트림과 공유 링크만
?token=도 받습니다. 브라우저의 EventSource는 헤더를 붙일 수 없기 때문입니다.
GET /health는 토큰이 필요 없습니다.
공유 토큰은 커널 하나에 묶입니다. 다른 커널을 가리키면 403이 아니라 404로 답해서,
어떤 커널이 있는지 드러내지 않습니다.
경로
| 경로 | 내용 |
|---|---|
GET /health | 생존 확인. 토큰 없이 열리는 유일한 경로입니다. |
GET /api/manager | 모드, 버전, 가동 시간, 호출한 쪽의 권한. |
GET /api/kernels, POST /api/kernels | 떠 있는 커널 목록, path의 커널 시작 또는 붙기(떠 있었으면 200, 새로 띄우면 201). |
GET, DELETE /api/kernels/{kernel_id} | 상태, 종료. 죽이는 방법은 ?force=true뿐입니다. |
POST /api/kernels/{kernel_id}/interrupt | 실행 중인 셀 인터럽트. |
POST /api/kernels/{kernel_id}/restart | soft 또는 hard 재시작. |
GET /api/kernels/{kernel_id}/namespace | 사용자 변수. |
GET, POST /api/kernels/{kernel_id}/runs | 실행 기록 목록, 실행 시작(mode, cells 또는 cell_ids, params, source, on_busy). |
GET, DELETE /api/kernels/{kernel_id}/runs/{run_ref} | 실행 기록 하나(latest 또는 ID), 대기 중인 실행 취소. |
GET /api/kernels/{kernel_id}/runs/{run_ref}/wait | 실행이 끝날 때까지 롱폴링. 스트림을 붙잡을 수 없는 클라이언트용입니다. |
GET /api/kernels/{kernel_id}/events | Server-Sent Events. Last-Event-ID나 ?since=로 이어 받습니다. |
GET /api/kernels/{kernel_id}/document, GET /api/documents | 최신 출력을 셀에 맵핑한 파일, 커널 없이 아무 파일이나 파싱. |
/api/kernels/{kernel_id}/cells…, …/presence | 협업 편집: 셀 생성·수정·이동·잠금, 포커스와 커서. |
/api/kernels/{kernel_id}/shares… | 공유 토큰(전용 매니저). |
출력은 nbformat 4 출력 스키마를 그대로 씁니다. 그래서 Jupyter 출력을 읽는 도구는 이것도 읽습니다. 떠 있는
매니저는 /docs/에서 API 문서 페이지도 냅니다.
예시
# 파일의 커널을 띄우거나 붙습니다. 떠 있었으면 200, 새로 띄웠으면 201.
curl -s -X POST "$URL/api/kernels" -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"path": "/home/me/experiments/train.py"}'
# 1번과 3번 셀을 실행. 바쁘면 409 대신 대기열에 넣음.
curl -s -X POST "$URL/api/kernels/$KID/runs" -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"mode": "cells", "cells": [1, 3], "params": {"lr": 0.02}, "on_busy": "queue"}'
# 이벤트 따라가기. EventSource는 헤더를 못 붙이므로 SSE는 ?token=도 받음.
curl -N "$URL/api/kernels/$KID/events?token=$TOKEN"
# 멈춤은 인터럽트입니다.
curl -s -X POST "$URL/api/kernels/$KID/interrupt" -H "Authorization: Bearer $TOKEN"
커널이 바쁠 때 들어온 두 번째 실행은 요청에 "on_busy": "queue"가 없으면 현재 실행 정보와 함께
409를 받습니다.
이벤트
GET /api/kernels/{kernel_id}/events는 Server-Sent Events 스트림입니다. 실행 시작과 끝, 셀
시작과 끝, 출력, 문서 변경이 옵니다. SSE id는 커널의 이벤트 번호입니다. 다시 붙을 때
Last-Event-ID를 주면, 커널의 재전송 버퍼에 남아 있는 동안은 놓친 이벤트를 받습니다.
버전 없음
경로에는 버전이 없습니다(INTENT D16). API는 더하기만 합니다. 필드, 선택 요청 필드, 경로, 오류 코드를 더할
뿐 이름을 바꾸거나 지우지 않습니다. 대체된 필드는 deprecated 표시와 함께 남습니다. 클라이언트는
모르는 필드를 무시합니다.
바뀌는 것
오래 열리는 스트림(이벤트, 실행 대기 롱폴링)은 매니저가 소켓째 커널에 넘기도록 바뀝니다. 그래서 매니저가 끝나도 스트림이 이어집니다 변경 중 #47. 2025 인증을 되살리는 중입니다. 비밀번호와 마스터 토큰은 매니저마다 하나(/api/auth)이고, 접근 토큰은 커널이 직접 들고 있어서 어느 매니저로든 통합니다 변경 중 #48. 권한은 viewer 등급 대신 능력 다섯(read, history, execute, edit, manage)의 집합이 되고, 협업은 2025 WebSocket 동기화로 옮깁니다 변경 중 #49. 매니저와 커널 사이는 같은 OS 계정만 통과하게 됩니다 변경 중 #55. runs, interrupt 같은 짧은 REST 호출의 모양은 그대로입니다.