Admin REST API¶
Continuum Router는 바이너리가 admin 기능으로 빌드된 경우 /admin 아래에 관리 API를 마운트합니다. 기본 기능 세트에는 admin이 포함됩니다.
Admin API는 신뢰할 수 있는 관리 네트워크용입니다. 인증은 기본적으로 활성화되지 않습니다. localhost 밖에 노출하기 전에 admin.auth를 설정하세요.
인증¶
Bearer 토큰¶
bearer_token은 16자 이상이어야 합니다. allowed_ips는 선택 사항이며 개별 IPv4/IPv6 주소와 CIDR 범위를 받습니다.
HTTP Basic¶
IP 허용 목록¶
allowed_ips는 method: bearer 또는 method: basic과 함께 사용할 수도 있습니다. Unix 소켓 연결에는 피어 IP가 없으므로 IP 검사를 건너뜁니다.
API 키¶
# Authorization: Bearer와 X-API-Key를 모두 허용합니다
curl -H "Authorization: Bearer $ADMIN_API_KEY" http://127.0.0.1:8080/admin/health
curl -H "X-API-Key: $ADMIN_API_KEY" http://127.0.0.1:8080/admin/health
method: api_key는 제시된 키를 일반 /v1 인증과 Admin 키 관리(/admin/api-keys)가 사용하는 것과 동일한 API 키 저장소로 검증합니다. 키는 활성 상태이고 만료되지 않았으며 required_scope(기본값 admin)를 가져야 합니다. 저장소를 공유하므로 키를 비활성화, 회전, 만료, 삭제하면 재시작 없이 즉시 Admin API 인증에서도 차단됩니다. 키가 없거나 잘못되면 401, 필요한 스코프가 없는 유효한 키나 화이트리스트에 없는 IP의 요청은 403을 반환합니다. 계층적 보안을 위해 allowed_ips를 method: api_key와 함께 사용할 수 있습니다.
Admin 인증 설정은 Admin 라우터를 구성할 때 캡처됩니다. admin.auth 자체(예: method나 required_scope 변경)를 바꾼 뒤에는 라우터를 재시작하세요. 개별 키 변경은 공유 저장소를 통해 즉시 반영됩니다.
감사 로그¶
admin:
audit:
enabled: true
log_level: info
include_headers: false
include_body: false
excluded_headers:
- authorization
- x-api-key
- cookie
감사 로그는 기본적으로 활성화됩니다. 요청에 비밀 값이나 사용자 데이터가 포함될 수 있다면 본문 로깅을 사용하지 마세요.
엔드포인트 목록¶
아래 경로는 모두 라우터 원점을 기준으로 합니다.
기능 및 상태¶
| 메서드 | 경로 | 용도 |
|---|---|---|
GET | /admin/capabilities | 컴파일 시 기능과 런타임 활성 서브시스템 조회 |
GET | /admin/health | 라우터 서비스와 백엔드 상태 요약 |
GET | /admin/models | Admin 인증으로 집계 모델 카탈로그 조회 |
POST | /admin/models/refresh | 집계 모델 카탈로그 갱신 |
백엔드 관리¶
| 메서드 | 경로 | 용도 |
|---|---|---|
GET | /admin/backends | 백엔드와 상태 정보 목록 |
POST | /admin/backends | 백엔드 생성 |
GET | /admin/backends/{name} | 백엔드 하나 조회 |
PUT | /admin/backends/{name} | 백엔드 하나 교체 |
DELETE | /admin/backends/{name} | 백엔드 삭제 |
PUT | /admin/backends/{name}/weight | 백엔드 가중치 변경 |
PUT | /admin/backends/{name}/models | 노출 모델 목록 변경 |
POST | /admin/backends/probe | 등록하지 않은 임시 백엔드 후보 검증 및 모델 검색 |
POST | /admin/backends/{name}/models/discover | 선택 모델 목록을 바꾸지 않고 백엔드 하나의 실시간 모델 카탈로그 검색 |
POST | /admin/backends/{name}/check | 상태 머신을 변경하지 않는 즉시 연결 검사 |
백엔드 변경은 설정 업데이트 채널로 새 설정 스냅샷을 발행합니다. 모든 필드가 즉시 적용되는 것은 아닙니다. /admin/config/hot-reload-status를 확인하고 requires_restart 섹션은 재시작하세요.
런타임 백엔드 영속성은 선택 기능입니다. 루트 backends_persistence_file 키를 설정한 뒤 라우터를 재시작하면 POST /admin/backends(WebUI 생성 포함)로 만든 백엔드를 소유자 전용 YAML 사이드카에 저장합니다. 이 키가 없으면 해당 백엔드는 메모리에만 남고 재시작 시 사라집니다. 파일 선언 또는 Continuum Hub 관리 백엔드는 사이드카에 기록하지 않습니다. 같은 이름이 충돌하면 파일 선언이 우선하며, 일반 파일 핫 리로드에서는 가려지지 않은 모든 런타임 백엔드를 유지합니다. Hub overlay 모드는 로컬 런타임 백엔드를 보존하지만, 채택된 authoritative 스냅샷은 사이드카 레코드를 삭제하지 않은 채 유효 풀에서 숨길 수 있습니다. 핫 리로드는 파일을 런타임 백엔드와 결합한 결과를 검증하며, 그 조합이 유효하지 않으면 이전에 발행한 설정을 그대로 유지합니다. 예를 들어 리로드된 tracing.headers 이름을 이미 런타임 백엔드의 request_extensions.headers가 사용하고 있는 경우입니다.
GET /admin/backends는 runtime_persistence.enabled와 각 백엔드의 durability 값(configured, memory, persistent)을 보고합니다. 단일 백엔드 조회와 변경 응답도 durability를 제공합니다. 복원할 수 있도록 사이드카의 자격 증명은 의도적으로 마스킹하지 않으므로 주 설정 파일과 같은 수준으로 경로를 보호하세요. Unix에서는 모드 0600으로 파일을 만들고 원자적으로 교체합니다. 원본 설정 파일에는 되쓰지 않습니다.
POST /admin/backends/{name}/models/discover는 읽기 전용입니다. 설정된 백엔드 하나를 정확한 이름으로 찾고, 일반 집계가 쓰는 것과 같은 백엔드별 모델 fetcher를 재사용하며, 설정의 backends[].models 허용 목록을 적용하기 전의 실시간 카탈로그를 반환합니다. 관리 UI가 사용 가능한 모델 선택지를 채워야 하지만 라우팅에 쓰는 선택 모델 목록은 그대로 유지해야 할 때 사용하세요.
curl -X POST http://127.0.0.1:8080/admin/backends/provider-openai/models/discover \
-H "Authorization: Bearer $ADMIN_TOKEN"
{
"backend": "provider-openai",
"object": "list",
"data": [
{
"id": "gpt-5.5",
"object": "model",
"created": 1760000000,
"owned_by": "openai"
}
]
}
검색 응답은 설정을 쓰거나, 집계 모델 캐시를 무효화하거나, 백엔드 헬스와 서킷 브레이커 상태를 바꾸지 않습니다. Codex OAuth 백엔드는 일반 Codex 검색 경로와 동일하게 로드된 OAuth 전략, 토큰 갱신, chatgpt-account-id 처리, 계정 플랜 필터를 사용합니다. Codex가 아닌 OAuth 백엔드, 네이티브 Anthropic, Bedrock은 라우터가 조회할 공급자 네이티브 카탈로그가 없으므로 501과 error.code: "model_discovery_unsupported"를 반환합니다. 알 수 없는 백엔드 이름은 404와 error.code: "backend_not_found"를 반환합니다. 업스트림 실패는 backend_authentication_failed, backend_discovery_timeout, backend_discovery_network_error, backend_discovery_parse_error, backend_discovery_http_error, backend_discovery_response_too_large 중 하나로 구분됩니다. 응답 본문에는 액세스 토큰, 리프레시 토큰, 토큰 저장소 경로, 원본 인증 헤더가 들어가지 않습니다.
POST /admin/backends/probe는 백엔드 생성과 같은 후보 필드와 operations: ["health", "models"]를 받습니다. TCP와 Unix 소켓을 포함한 모든 Admin 전송에서 Admin 인증을 거치며, GET /admin/capabilities는 이 기능을 transient_backend_probe_v1로 광고합니다. 후보는 메모리에서만 검증되고 활성 설정, 설정 기록, 백엔드 풀, 헬스 상태, 서킷 브레이커, 집계 캐시, 환경 파일, 토큰 저장소에 추가되지 않습니다.
응답은 health와 catalog를 분리합니다. health.credential_status는 valid, invalid, unknown, not_required 중 하나입니다. 자격 증명이 전달되었더라도 인증이 필요 없는 헬스 엔드포인트가 정상이라고 해서 자격 증명을 증명하지 않으므로 unknown을 반환합니다. 임시 OAuth 후보는 수명 주기로 관리되는 인증 전략 없이 정상 상태를 만들어 내지 않고 health.status: "unknown"과 backend_probe_health_unsupported를 반환합니다. catalog.source는 live, curated, configured, unsupported 중 하나입니다. OpenAI 호환, Gemini, Ollama, vLLM, llama.cpp, MLX, LM Studio처럼 모델 목록 API가 있는 후보는 등록된 백엔드 검색과 같은 URL 구성, 인증 헤더, 응답 크기 제한, 파싱, 정규화, 모델 개수 제한을 사용합니다. Anthropic은 라우터 내장 카탈로그나 명시적인 models/model_configs를 반환할 수 있습니다. Bedrock과 OAuth 후보도 개수 제한이 적용된 이 설정 항목을 반환할 수 있으며, 항목이 없으면 타입이 지정된 unsupported 카탈로그를 반환합니다.
프로브 구현은 항상 컴파일되는 src/backend_probe/ 모듈에 있으며, Router 자신의 네트워크에서 Hub가 작성한 후보 프로브를 실행하는 outbound Continuum Hub 백엔드 task 실행기(backend_tasks_v1, 이슈 #1262)와 공유합니다. 두 호출자가 같은 admission 한도를 지나므로 Hub task와 Admin 요청은 두 개가 아니라 하나의 프로세스 전역 동시성·레이트 상한을 나눠 쓰며, 지원 백엔드 타입, 엔드포인트 규칙, 미지원 auth 동작, 응답 크기 제한, 자격 증명 분류가 두 표면 사이에서 어긋날 수 없습니다. Admin 요청과 응답 형태는 달라지지 않았습니다. Hub 경로는 Router에 inbound 엔드포인트를 얻지 않으며, 이 응답을 그대로 전달하지 않고 프로토콜의 타입드 bounded 형태로 매핑합니다.
GET /admin/capabilities는 capabilities 배열에 위의 transient_backend_probe_v1과 함께 backend_preference_header_v1도 담아 보고합니다. 이 토큰은 x-backend 요청 헤더를 가리키며(x-backend로 백엔드 지정하기 참고), 컴파일 타임 기능이나 현재 설정과 상관없이 이 토큰을 담은 모든 라우터 빌드에서 나타납니다. 의미는 하나뿐입니다. 라우터가 서로 바꿔 쓸 수 있는 백엔드 후보 중에서 고르는 모든 자리에서 헤더를 존중한다는 뜻이며, 해당 절에 적힌 예외는 그대로 적용됩니다. 엔드포인트별 목록은 담지 않으므로 클라이언트는 이 토큰만으로 특정 엔드포인트가 헤더를 존중하는지까지는 알 수 없습니다. 이 토큰을 읽는 클라이언트는 x-backend를 보낼지 판단할 때 버전 문자열이 필요 없습니다. 토큰이 있으면 보내고 없으면 보내지 않으면 됩니다. 토큰이 없는 오래된 라우터에 헤더를 보내도 무방한데, 그런 라우터는 헤더를 그냥 무시하기 때문입니다. 추론용 API 키만 가진 클라이언트는 /admin/capabilities에 접근할 수 없지만, 어느 백엔드가 처리했는지 확인하기에 적힌 방법으로 추론 표면에서도 같은 동작을 확인할 수 있습니다.
서킷 브레이커 상태¶
| 메서드 | 경로 | 용도 |
|---|---|---|
GET | /admin/circuit/all | 전체 서킷 상태 목록 |
GET | /admin/circuit/{backend}/status | 백엔드 서킷 상태 조회 |
POST | /admin/circuit/{backend}/open | 서킷 강제 열기 |
POST | /admin/circuit/{backend}/close | 서킷 강제 닫기 |
POST | /admin/circuit/{backend}/reset | 서킷 초기화 |
이 엔드포인트는 일반 LLM 프록시 요청이 구동하고 조회하는 것과 동일한 서킷 브레이커 상태 머신을 제어하므로, 일반 프록시 라우팅이 사용하는 상태를 보고하고 조정합니다(예: open은 백엔드가 회복될 때까지 선택에서 제외합니다).
설정 조회¶
| 메서드 | 경로 | 용도 |
|---|---|---|
GET | /admin/config | 간단한 설정 요약 |
GET | /admin/config/full | 민감 값을 마스킹한 전체 현재 설정 |
GET | /admin/config/sections | Admin Config API 지원 섹션과 재적용 능력 |
GET | /admin/config/schema | 지원 섹션의 JSON 스키마 |
GET | /admin/config/hot-reload-status | 런타임 핫 리로드 상태와 능력 목록 |
GET | /admin/config/{section} | 민감 값을 마스킹한 지원 섹션 하나 |
GET /admin/config/full은 다음 봉투 구조를 반환합니다.
{
"config": {},
"hot_reload": {
"enabled": true,
"capabilities": {}
},
"metadata": {
"retrieved_at": "2026-07-19T00:00:00Z",
"version": 1
}
}
설정하지 않은 섹션은 config에 명시적 null 값으로 남지 않고 아예 빠집니다. 개별 섹션 조회는 이 변경과 무관하게 그대로 동작합니다. GET /admin/config/{section}으로 아직 설정하지 않은 유효한 섹션(예: metrics, fallback)을 조회해도 여전히 404가 아니라 200과 "config": null을 반환합니다.
섹션 API가 받는 이름은 다음과 같습니다.
serverbackendshealth_checksloggingretrytimeoutsrate_limitingcircuit_breakerglobal_promptsrequest_paramsadminfallbackmodel_aliasesfilesapi_keysmetricsrouting
이는 관리용 부분집합이며 주 설정 스키마가 받는 모든 최상위 필드 목록이 아닙니다.
설정 검증 및 변경¶
| 메서드 | 경로 | 용도 |
|---|---|---|
PUT | /admin/config/{section} | 지원 섹션 교체 |
PATCH | /admin/config/{section} | 섹션 객체의 재귀 부분 병합 |
POST | /admin/config/validate | 적용 없이 YAML, JSON, TOML 검증 |
POST | /admin/config/export | 기본적으로 마스킹하여 설정 내보내기 |
POST | /admin/config/import | 전체 설정 검증 및 선택적 발행 |
GET | /admin/config/history | 마스킹된 스냅샷을 포함한 최근 기록 최대 50개 조회 |
POST | /admin/config/rollback/{version} | 기록에 있는 설정 스냅샷 발행 |
POST | /admin/config/apply | 전체 설정 후보를 원자적으로 적용하거나, 후보가 없으면 no-op |
PUT과 PATCH의 JSON 본문은 { "config": ... } 래퍼가 아니라 섹션 값 자체입니다.
curl -X PATCH http://127.0.0.1:8080/admin/config/rate_limiting \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"enabled":true}'
성공한 섹션 변경 응답에는 version, requires_restart, hot_reload_capability가 포함됩니다. API는 메모리 내 스냅샷을 발행하며 원본 YAML/TOML 파일에는 쓰지 않습니다.
검증 본문:
{
"content": "server:\n bind_address: 127.0.0.1:8080\nbackends: []\n",
"format": "yaml",
"sections": []
}
sections가 비어 있으면 content는 완전한 설정이어야 합니다. 섹션 단위 검증에서는 content에 해당 최상위 섹션을 넣고 sections에도 그 이름을 나열합니다.
내보내기 본문:
format은 yaml, json, toml 중 하나입니다. sections가 비어 있으면 전체 설정을 내보냅니다. include_sensitive: true는 평문 비밀 값을 반환하며 보안 감사 로그를 남깁니다.
format: "toml"은 이제 섹션 여러 개를 설정하지 않아도 성공합니다. 그런 섹션은 직렬화된 설정에서 null로 남지 않고 아예 빠지는데 TOML은 애초에 null 값을 표현하지 못합니다. 예전에는 설정하지 않은 섹션이 하나라도 있으면 TOML 내보내기가 전부 500 SERIALIZATION_ERROR로 실패했습니다.
가져오기 본문:
{
"content": "server:\n bind_address: 127.0.0.1:8080\nbackends: []\n",
"format": "yaml",
"apply": false,
"dry_run": true
}
가져오기 본문은 1 MiB, 구조 중첩 깊이는 32로 제한됩니다. dry_run: true는 검증만 합니다. apply: true는 전체 후보 설정을 설정 업데이트 채널로 발행하고 기록 버전을 생성합니다. 재시작 전용 섹션은 여전히 재시작이 필요합니다.
내보내기에서 가져오기로 돌아올 때의 마스킹된 비밀 값¶
Admin API가 반환하는 마스킹 값은 어떤 엔드포인트든 똑같이 고정된 센티널 ***CONTINUUM-MASKED:<힌트>***로 감쌉니다. 힌트는 길이만 드러냅니다. 4자를 넘는 값은 앞 두 글자와 길이를 함께 보여주고(sk...(21 chars)), 4자 이하 값은 길이만 보여주며((4 chars)), 불리언이나 숫자를 마스킹하면 ***CONTINUUM-MASKED:hidden***으로 나옵니다. 예전 라우터 빌드는 4자 이하 값이면 길이조차 밝히지 않고 그대로 ***MASKED***만 돌려줬는데, 이번 변경으로 짧은 비밀 값도 길이는 드러나지만 내용은 여전히 드러나지 않습니다. 이 마스킹 규칙은 GET /admin/config/full, GET /admin/config/{section}, GET /admin/backends, GET /admin/config/history 스냅샷에도 똑같이 적용되며 내보내기에서만 쓰는 형식이 아닙니다.
include_sensitive: true를 넘기지 않으면 내보내기는 모든 자격 증명을 마스킹합니다. 따라서 편집해서 되돌려 보내는 문서에는 실제 비밀 값 대신 이 자리표시자가 들어 있습니다. 가져오기는 자리표시자를 "지금 적용된 값을 그대로 둔다"는 뜻으로 읽어 실행 중인 비밀 값을 다시 채워 넣고, 그대로 둔 경로를 모두 응답에 적습니다.
{
"success": true,
"applied": true,
"version": 4,
"preserved_secret_paths": ["backends[0].api_key", "admin.auth.bearer_token"],
"validation": {
"valid": true,
"warnings": [
{
"section": "backends",
"path": "backends[0].api_key",
"message": "'backends[0].api_key' carried a masked placeholder, so the value currently in effect was kept. The submitted document did not change this secret.",
"code": "MASKED_SECRET_PRESERVED"
}
]
}
}
POST /admin/config/validate도 같은 MASKED_SECRET_PRESERVED 경고를 내보냅니다. WebUI의 저장 전 예행 검증이 커밋 전에 어떤 값이 그대로 남는지 보여 준다는 뜻입니다.
설정 문서를 기록하는 모든 엔드포인트가 같은 복원 과정을 거치므로, 예행 검증이 약속한 결과가 실제 저장 결과와 같습니다. POST /admin/config/import, config 후보를 담은 POST /admin/config/apply, PUT/PATCH /admin/config/{section}이 여기에 해당합니다. 섹션 기록에서 자리표시자를 복원하지 못하면 400 Bad Request로 거부하며, 본문에는 종전과 같이 success: false와 실패한 경로가 path/code에 담깁니다.
자리표시자는 실행 중인 설정에 그 값을 만들어 낸 원본이 아직 있을 때만 복원됩니다. 배열 항목은 위치가 아니라 id나 name으로 대응시키므로 backends 순서를 바꿔도 제대로 복원되지만, 이름을 바꾸면 복원되지 않습니다. guardrails.bypass_api_keys나 rate_limiting.bypass_keys처럼 문자열만 담는 목록은 id도 name도 없어 위치가 유일한 단서입니다. 그래서 항목을 하나 넣거나 빼면 추측하지 않고 거부합니다. 같은 공급자가 발급한 키는 접두사와 길이가 같아서, 한 칸 밀린 위치가 옆 키로 그대로 대응해 버리기 때문입니다. 이런 목록은 include_sensitive: true로 내보낸 문서에서 편집하거나 실제 값을 직접 넣으세요. 대응할 값을 찾지 못하면 옆에 있던 비밀 값을 엉뚱한 필드에 붙이는 대신 가져오기 전체를 거부하고 실패한 경로를 그대로 알려 줍니다.
| 코드 | 의미 |
|---|---|
MASKED_SECRET_PRESERVED | 경고. 이 경로의 실행 중 값을 그대로 두었습니다. 제출한 문서는 이 값을 바꾸지 않았습니다. |
MASKED_SECRET_UNRESOLVED | 오류. 이 경로의 자리표시자가 실행 중인 설정의 어떤 값과도 대응하지 않습니다. 보통 항목이나 섹션의 이름이 바뀌었거나, 다른 문서에서 옮겨 왔거나, 삭제된 경우, 또는 id나 name이 없는 목록에 항목을 넣거나 뺀 경우입니다. 실제 값을 넣거나, 현재 설정을 다시 내보내 그 문서를 편집하세요. |
LEGACY_MASK_PLACEHOLDER | 오류. 센티널이 생기기 전에 쓰던 자리표시자 형태(***MASKED***, xy...(N chars), ${***VAR***})이며 보통 업그레이드 이전 라우터에서 내보낸 문서에서 나타납니다. 이 형태들은 비밀 값 자체에서 파생되어 실제 자격 증명과 구분할 수 없으므로 복원 근거로 쓰지 않습니다. 현재 설정을 다시 내보내 그 문서를 편집하거나 지적된 경로에 실제 비밀 값을 직접 입력하세요. |
MASKED_SECRET_REPORT_TRUNCATED | 요약하는 목록에 따라 경고 또는 오류로, 최대 한 번만 보고합니다. 자리표시자로 가득 찬 문서가 응답을 부풀리지 못하도록 경로별 목록을 500개로 제한하며, 목록에 담기지 않은 채 유지되거나 거부된 경로가 몇 개인지 알려줍니다. |
이미 실행 중인 값과 같은 값은 생김새와 무관하게 그대로 둡니다. 예전 빌드에서 자격 증명이 손상되어 실행 중인 값 자체가 옛 자리표시자 형태인 설정도, 모든 비밀 값을 다시 입력하지 않고 섹션 단위로 계속 편집할 수 있습니다.
자리표시자가 아닌 값은 그대로 반영되므로 자격 증명 교체도 평소대로 됩니다. 자리표시자를 새 비밀 값으로 바꿔 가져오면 됩니다. 환경 변수 참조는 참조인 채로 왕복합니다. ${OPENAI_API_KEY}는 ***CONTINUUM-MASKED:${OPENAI_API_KEY}***로 내보내지고 ${OPENAI_API_KEY}로 복원되며, 해석된 값으로 바뀌지 않습니다.
적용 본문:
POST /admin/config/apply는 전체 config 후보를 전달할 때만 실제로 동작합니다. 섹션 편집(PUT/PATCH), import, rollback은 이미 즉시 발행하므로:
config없음(또는null): 명시적 no-op. 아무것도 발행하지 않고 기록 버전도 생성하지 않으며, 응답은hot_reload_triggered: false와 빈updated_sections를 반환합니다. 반복 호출해도 빈 기록 버전을 만들지 않습니다.config있음,hot_reload: true: 후보를 검증하고 실행 중인 설정과 비교(diff)한 뒤, 차이가 있으면 설정 업데이트 채널로 원자적으로 발행합니다. 변경된 섹션으로 정확히 하나의 기록 버전을 생성합니다.hot_reload_triggered는 발행이 성공한 뒤에만true이고,updated_sections와requires_restart는 실제 diff를 반영합니다. 이 경로에서 후보가 거부되면400 Bad Request를 반환하며 거부 사유는message에 담깁니다.config있음,hot_reload: false: 미리보기 전용. 응답은updated_sections/requires_restartdiff를 반환하지만 아무것도 발행하지 않고 기록 버전도 생성하지 않습니다. 이 모드에서는 후보가 거부되어도200을 유지합니다. 실행이 아니라 어떻게 될지를 물은 요청이기 때문입니다.
실행 중인 설정과 동일한 후보는 no-op으로 처리됩니다.
API 키¶
| 메서드 | 경로 | 용도 |
|---|---|---|
GET, POST | /admin/api-keys | 키 목록 또는 생성 |
GET, PUT, DELETE | /admin/api-keys/{id} | 키 조회, 변경 또는 삭제 |
POST | /admin/api-keys/{id}/rotate | 키 값 회전 |
POST | /admin/api-keys/{id}/enable | 키 활성화 |
POST | /admin/api-keys/{id}/disable | 키 비활성화 |
API 키 값은 생성 또는 회전 시에만 반환됩니다. 즉시 안전한 곳에 저장하세요.
프롬프트 및 통계¶
| 메서드 | 경로 | 용도 |
|---|---|---|
GET | /admin/config/prompts | 프롬프트 파일 목록 |
POST | /admin/config/prompts/reload | 프롬프트 파일 다시 읽기 |
GET, PUT | /admin/config/prompts/{path} | 프롬프트 경로 조회 또는 변경 |
GET | /admin/stats | 전체 통계 스냅샷 |
GET | /admin/stats/series | 전체 시계열 |
GET | /admin/stats/models | 모델별 통계 |
GET | /admin/stats/backends | 백엔드별 통계 |
GET | /admin/stats/api-keys | API 키 통계 |
GET | /admin/stats/api-keys/{id} | 키 하나의 통계 |
GET | /admin/stats/api-keys/{id}/models | 키 하나의 모델 통계 |
GET | /admin/stats/api-keys/{id}/series | 키 하나의 시계열 |
GET | /admin/stats/users | 사용자 통계 |
GET | /admin/stats/users/{user_id} | 사용자 하나의 통계 |
GET | /admin/stats/users/{user_id}/models | 사용자 하나의 모델 통계 |
GET | /admin/stats/users/{user_id}/series | 사용자 하나의 시계열 |
POST | /admin/stats/reset | 메모리 통계 초기화 |
GET | /admin/metrics/history | 영속 메트릭 기록. 영속화가 없으면 404 |
라우팅, 캐시, 가드레일 제어¶
| 메서드 | 경로 | 용도 |
|---|---|---|
GET | /admin/prefix-routing/stats | 접두사 라우팅 통계 |
GET | /admin/response-cache/stats | 응답 캐시 통계 |
POST | /admin/response-cache/invalidate | 응답 캐시 항목 무효화 |
GET | /admin/kv-index/stats | KV 인덱스 통계 |
GET | /admin/kv-index/backends | 백엔드별 KV 인덱스 상태 |
POST | /admin/kv-index/clear | KV 인덱스 비우기 |
GET | /admin/gemini-context-cache/stats | Gemini 컨텍스트 캐시 통계 |
POST | /admin/gemini-context-cache/clear | Gemini 컨텍스트 캐시 상태 비우기 |
GET, PUT | /admin/smart-routing/model-profiles | 모델 프로필 목록 또는 교체 |
GET | /admin/smart-routing/model-profiles/{model} | 모델 프로필 하나 조회 |
GET | /admin/smart-routing/status | 스마트 라우팅 상태 |
GET | /admin/smart-routing/stats | 스마트 라우팅 통계 |
POST | /admin/smart-routing/classify | 확인용 요청 분류 |
POST | /admin/smart-routing/simulate | 라우팅 시뮬레이션 |
GET, PUT | /admin/smart-routing/policies | 정책 조회 또는 교체 |
GET | /admin/smart-routing/load-state | 백엔드 부하 상태 |
GET | /admin/smart-routing/cache/stats | 분류기 캐시 통계 |
POST | /admin/smart-routing/cache/clear | 분류기 캐시 비우기 |
GET, PATCH | /admin/guardrails | 가드레일 정책 조회 또는 부분 변경 |
PUT | /admin/guardrails/providers/{name} | 가드레일 공급자 변경 |
PUT, DELETE | /admin/guardrails/routes/{route} | 경로 오버라이드 설정 또는 제거 |
POST | /admin/guardrails/test | 설정된 가드레일로 텍스트 시험 실행 |
파일, ACP, 컨트롤 플레인¶
| 메서드 | 경로 | 용도 |
|---|---|---|
GET, POST | /admin/files | 파일 목록 또는 업로드. Files 비활성 시 404 |
GET, DELETE | /admin/files/{file_id} | 메타데이터 조회 또는 파일 삭제 |
GET | /admin/files/{file_id}/content | 파일 내용 다운로드 |
GET | /admin/acp/status | ACP 서비스 상태 |
GET | /admin/acp/sessions | 활성 ACP 세션 |
GET | /admin/acp/agent.json | ACP 에이전트 설명자 |
GET | /admin/control-plane/status | 컨트롤 플레인 에이전트 상태. control-plane 빌드에만 경로 존재 |
GET | /admin/control-plane/policy | 라우터가 실행 중인 Hub 정책. 키 해시와 비밀값은 제외. control-plane 빌드에만 경로 존재 |
POST | /admin/control-plane/enroll | 권한이 필요한 라우터 측 Hub 등록. control-plane 빌드에만 경로 존재 |
Hub 정책 조회¶
GET /admin/control-plane/policy는 라우터가 실행 중인 Hub 정책을 보고하며, 다른 엔드포인트와 같은 Admin 인증을 사용합니다. 에이전트가 비활성이면 {"enabled": false}를, control_plane.policy.enabled가 꺼져 있으면 {"enabled": true, "policy_sync_enabled": false}를 반환합니다. 그 밖에는 synced가 엔벨로프 적용 여부를 나타내며, 한 번도 등록하지 않은 라우터는 빈 목록과 함께 synced: false를 보고합니다.
| 필드 | 내용 |
|---|---|
revision, issued_at_ms, last_sync_ms | 적용된 엔벨로프의 커서, Hub가 발행한 시각, 이 라우터가 적용한 시각 |
status | 라우터가 Hub에 보고하는 capability와 활성·거부 리비전 식별자. status.rejected는 거부한 리비전을 오류 코드와 함께 나타냄 |
tiers | Hub가 지정한 티어별 제한과 key_count, revoked_key_count |
keys | total, revoked, unresolved_tier(엔벨로프에 정의되지 않은 티어를 가리키는 키) |
substitution_rules, equivalence_classes, routing_windows | 실제 적용 중인 집합. 엔벨로프에 해당 집합이 없으면 이전 집합을 유지 |
optimization | 조직 최적화 정책. 첫 동기화 전에는 null |
model_budgets | 적용 중인 모델 예산 단위와 풀별 tracked_keys, exhausted_keys, unknown_keys. 강제 적용하지 않으면 null |
키 해시와 프로바이더 자격 증명은 포함하지 않으며, 키는 개수로만 나타납니다. Hub에 연결할 수 없는 동안에도 라우터는 마지막으로 적용한 정책을 계속 강제 적용하므로, 현재 정책인지 마지막으로 알려진 정책인지는 GET /admin/control-plane/status의 heartbeat와 함께 보고 판단합니다.
라우터에서 시작하는 Hub 등록¶
POST /admin/control-plane/enroll은 다른 모든 Admin 엔드포인트와 같은 Admin 인증 및 감사 미들웨어를 사용합니다. JSON 본문에는 필수 token, 선택적 hub_url과 router_name, 선택적 불리언 replace를 전달합니다.
{"token":"single-use-token","hub_url":"https://hub.example.com","router_name":"edge-a","replace":false}
라우터는 HTTPS(개발용 루프백 HTTP는 허용)로 토큰을 교환하고, 기존 소유자 전용 자격 증명 저장소를 통해 Hub가 발급한 tenant_id, router_id, router_credential만 저장한 뒤 state: "pending_restart"를 반환합니다. 등록 토큰은 저장, 응답, 로그에 절대 남지 않으며 WebUI는 성공과 실패 모두에서 입력값을 지웁니다. 새 신원을 활성화하려면 라우터를 재시작합니다.
정상적인 영속 자격 증명이 이미 있으면 replace: true를 명시하지 않는 한 409 ALREADY_ENROLLED를 반환합니다. 실행 중인 에이전트가 거부됨을 관측한 자격 증명은 GET /admin/control-plane/status에서 rejected로 보고되며, 상태 파일을 삭제하거나 replace를 지정하지 않고도 교체할 수 있습니다. 이 엔드포인트는 시작 시 등록과 교환을 직렬화하므로 동시 시도가 서로 덮어쓰지 않습니다.
control_plane 섹션은 시작 시 소유됩니다. GET /admin/config/control_plane으로 계속 읽을 수 있지만 GET /admin/config/sections의 편집 가능 목록에서는 제외되며, PUT/PATCH /admin/config/control_plane은 안내와 함께 409 STARTUP_OWNED_SECTION을 반환합니다. enabled, 정책, 주기, 상태 파일 등 에이전트 설정은 소스 YAML/TOML을 편집하고 재시작하십시오. 기존 설정 파일 토큰 등록 흐름도 계속 지원됩니다.
거부된 설정 변경¶
제출한 값 때문에 거부된 요청이 실행 중 상태를 바꾸려던 요청이었다면 400 Bad Request를 반환합니다. 변경이 아니라 판정을 요청한 경우에는 200을 유지하고 결과를 본문에 담습니다. 기준은 엔드포인트가 아니라 요청의 성격입니다.
| 요청 | 거부 시 상태 코드 |
|---|---|
PUT/PATCH /admin/config/{section} | 400 |
apply: true이고 dry_run: false인 POST /admin/config/import | 400 |
dry_run: true이거나 apply: false인 POST /admin/config/import | 200, 결과는 success와 validation에 |
config 후보가 있고 hot_reload: true인 POST /admin/config/apply | 400 |
후보가 없거나, 후보가 실행 중 설정과 같거나, hot_reload: false인 POST /admin/config/apply | 200, 결과는 success와 message에 |
POST /admin/config/validate | 파싱 가능한 입력이면 언제나 200, 결과는 valid와 errors에 |
응답 본문은 이전과 완전히 동일합니다. "success": false와 설명이 담긴 error/validation.errors가 그대로 있으므로, 이미 본문을 확인하던 클라이언트는 고칠 것이 없습니다. 달라지는 것은 상태 코드를 기준으로 삼는 자동화입니다. curl -f와 대부분의 HTTP 클라이언트 라이브러리가 그렇게 동작하는데, 이제 거부된 설정을 적용된 설정으로 잘못 읽지 않습니다.
두 가지 거부는 의도적으로 200을 유지합니다. "핫 리로드 사용 불가"는 잘못된 요청이 아니라 서버 기능이 없다는 보고이고, 가져오기의 크기와 중첩 깊이 제한은 각각 SIZE_LIMIT_EXCEEDED, NESTING_LIMIT_EXCEEDED 코드를 따로 사용합니다.
상태 코드¶
200: 요청이 처리됨. 판정 엔드포인트는 결과를 여기에 담으므로,POST /admin/config/validate,POST /admin/config/import예행 실행,POST /admin/config/apply미리보기 응답에는"valid": false나"success": false가 있을 수 있습니다.400: 잘못된 요청, 또는 제출된 값 때문에 라우터가 거부한 설정 변경. 본문은 이 엔드포인트들이 이전부터 반환하던 JSON 문서 그대로이며"success": false도 포함되므로, 본문을 읽는 클라이언트는 수정할 필요가 없습니다.401: Bearer/Basic 자격 증명 누락 또는 불일치.403: 허용되지 않은 IP 또는 권한 부족.404: 없는 리소스, 런타임 기능 비활성, 또는 컴파일 기능이 없어 경로가 없음.409: 기존 자격 증명 교체 동의 필요, 라우터 이름 충돌, 또는 시작 시 소유되는 런타임 섹션.422: Hub가 제공된 등록 토큰을 거부함.413: 업로드가 경로 본문 제한을 초과함.502: 아웃바운드 Hub 등록 교환 실패.500: 내부 실패 또는 인증 방식에 필요한 런타임 상태가 없음.