페어링 및 인증
디바이스 페어링 흐름, 베어러 토큰, 디바이스 레지스트리, 토큰 교체, 서비스 토큰, 속도 제한.
Revka 게이트웨이의 모든 /api/* 경로는 베어러 토큰 인증으로 보호되며, 토큰을 발급받는 유일한 방법은 페어링 흐름입니다. 디바이스는 일회용 페어링 코드를 영구 베어러 토큰으로 교환하고, 이후의 모든 요청은 해당 토큰으로 인가되며, 디바이스는 SQLite 기반 레지스트리에 기록되어 목록 조회, 폐기, 교체가 가능합니다. 이 페이지에서는 코드 조회, 레거시 및 향상된 페어링 엔드포인트, 디바이스 레지스트리, 토큰 교체, 내부 서비스 토큰, 그리고 이 모든 것을 보호하는 속도 제한까지 전체 흐름을 설명합니다.
브라우저, 휴대폰, 헤드리스 스크립트를 실행 중인 게이트웨이와 페어링하거나, REST API를 대상으로 클라이언트를 구현하거나, 인증 영역을 강화하려는 경우 이 페이지를 참고하십시오. 동일한 흐름의 CLI 측면(revka gateway get-paircode, revka pair token)은 revka 게이트웨이, 데몬 & 서비스를, 토큰 및 시크릿의 암호화 설계는 시크릿, 페어링 & 디바이스 인증을 참조하십시오.
인증 동작 방식
섹션 제목: “인증 동작 방식”[gateway].require_pairing = true(기본값)로 설정된 경우, 게이트웨이는 최초 시작 시 6자리 일회용 페어링 코드를 출력합니다. 클라이언트가 해당 코드를 페어링 엔드포인트에 POST하면 rk_ 접두사가 붙은 256비트 베어러 토큰을 받습니다. 토큰은 SHA-256 해시로만 저장되며 평문은 한 번만 표시됩니다. 이후의 모든 호출에서는 다음과 같이 전송합니다.
Authorization: Bearer rk_<token>[gateway]require_pairing = true # default — set false to disable auth (local-only use)allow_public_bind = false # must be explicit to bind beyond localhost토큰은 성공 시 설정에 유지되므로(persist_pairing_tokens), 데몬을 재시작해도 재페어링이 필요하지 않습니다. 기존 토큰은 평문(rk_...) 또는 사전 해시된 64자 16진수 형식으로 수락되며, 하위 호환성을 위해 레거시 zc_ 접두사 토큰도 계속 지원됩니다.
페어링 코드 조회
섹션 제목: “페어링 코드 조회”게이트웨이는 대시보드가 QR 코드나 복사 버튼을 렌더링할 수 있도록 인증 없이 현재 유효한 코드를 제공하며, CLI를 위한 로컬호스트 전용 관리자 엔드포인트를 통해서도 조회할 수 있습니다.
| 메서드 | 경로 | 인증 | 목적 |
|---|---|---|---|
GET | /pair/code | 로컬호스트 전용 | 현재 유효한 페어링 코드 반환 |
GET | /admin/paircode | 로컬호스트 전용 | 현재 코드 반환 (revka gateway get-paircode에서 사용) |
POST | /admin/paircode/new | 로컬호스트 전용 | 새 코드 생성 (--new 옵션에서 사용) |
# From the CLI (calls the admin endpoints under the hood)revka gateway get-paircode # show the current coderevka gateway get-paircode --new # rotate to a fresh code코드는 일회용입니다. 클라이언트가 코드로 페어링하면 코드가 소모되며, 새로운 코드를 발급하려면 교체해야 합니다.
코드를 베어러 토큰으로 교환
섹션 제목: “코드를 베어러 토큰으로 교환”페어링 엔드포인트는 두 가지입니다. 둘 다 베어러 토큰을 반환하며, 클라이언트가 헤더를 사용하는지 JSON 본문을 사용하는지에 따라 선택합니다.
레거시: POST /pair (헤더 기반)
섹션 제목: “레거시: POST /pair (헤더 기반)”원래 페어링 경로입니다. X-Pairing-Code 헤더에 코드를 포함하고, 선택적으로 디바이스 메타데이터 헤더를 함께 전송합니다.
POST /pairX-Pairing-Code: 123456X-Revka-Device-Name: My LaptopX-Revka-Device-Type: cliX-Revka-Device-Hardware: MacBook Pro / macOS{ "paired": true, "persisted": true, "token": "rk_<64 hex chars>", "message": "Save this token — use it as Authorization: Bearer <token>"}| 헤더 | 필수 여부 | 의미 |
|---|---|---|
X-Pairing-Code | 필수 | 시작 시 표시되는 일회용 코드 |
X-Revka-Device-Name | 선택 | 대시보드 페어링 페이지에 표시되는 디바이스 레이블 |
X-Revka-Device-Type | 선택 | 디바이스 유형 레이블 (예: cli, mobile) |
X-Revka-Device-Hardware | 선택 | 하드웨어 / 플랫폼 레이블 |
향상된 버전: POST /api/pair (JSON 본문)
섹션 제목: “향상된 버전: POST /api/pair (JSON 본문)”대시보드에서 사용하는 권장 API 경로입니다. JSON 본문으로 코드와 디바이스 메타데이터를 받아 페어링 가드와 디바이스 레지스트리 모두에 기록합니다.
curl -X POST http://127.0.0.1:42617/api/pair \ -H 'Content-Type: application/json' \ -d '{"code": "123456", "device_name": "My Phone", "device_type": "mobile", "hardware": "iOS"}'{ "token": "rk_<64 hex chars>", "persisted": true, "message": "Pairing successful"}| 필드 | 타입 | 필수 여부 | 의미 |
|---|---|---|---|
code | string | 필수 | 일회용 페어링 코드 |
device_name | string | 선택 | 디바이스 레이블 (120자로 잘림) |
device_type | string | 선택 | 디바이스 유형 레이블 (120자로 잘림) |
hardware | string | 선택 | 하드웨어 / 플랫폼 레이블 (120자로 잘림) |
실패 응답:
400 Bad Request— 유효하지 않거나 만료된 코드429 Too Many Requests—Too many attempts. Locked out for Ns(속도 제한 참조)
기존 디바이스에서 페어링 시작
섹션 제목: “기존 디바이스에서 페어링 시작”이미 페어링된 클라이언트는 데몬 재시작 없이 필요에 따라 새 페어링 코드를 발급할 수 있어, 두 번째 디바이스가 페어링할 수 있습니다. 이것이 대시보드의 “새 디바이스 페어링” 흐름입니다.
POST /api/pairing/initiateAuthorization: Bearer rk_<existing-token>응답에는 단기 유효 코드가 포함됩니다(일반적으로 QR 코드로 렌더링됨). 전체 흐름:
-
이미 페어링된 디바이스(예: 대시보드)에서
POST /api/pairing/initiate를 호출하여 코드를 생성합니다. -
코드를 QR로 표시하거나 새 디바이스에 복사합니다.
-
새 디바이스에서
POST /api/pair(또는POST /pair)에 코드를 제출하여 해당 디바이스의 베어러 토큰을 받습니다. -
새 디바이스가 디바이스 레지스트리와 대시보드 페어링 페이지에 표시됩니다.
디바이스 레지스트리, 폐기 & 교체
섹션 제목: “디바이스 레지스트리, 폐기 & 교체”페어링된 디바이스는 <workspace_dir>/devices.db의 SQLite 레지스트리에 유지되어 데몬 재시작 후에도 유지됩니다. API를 통해 관리합니다(모두 베어러 인증 필요):
| 메서드 | 경로 | 인증 | 목적 |
|---|---|---|---|
GET | /api/devices | Bearer | 페어링된 디바이스 목록 조회 |
DELETE | /api/devices/{id} | Bearer | 디바이스 폐기 — 즉시 토큰 무효화 |
POST | /api/devices/{id}/token/rotate | Bearer | 교체: 디바이스가 재페어링할 수 있도록 새 페어링 코드 발급 |
# List paired devicescurl http://127.0.0.1:42617/api/devices \ -H "Authorization: Bearer rk_<token>"
# Revoke a device by its registry UUIDcurl -X DELETE http://127.0.0.1:42617/api/devices/<device-id> \ -H "Authorization: Bearer rk_<token>"각 디바이스 레코드에는 다음 필드가 포함됩니다.
| 필드 | 의미 |
|---|---|
id | 레지스트리의 디바이스 UUID (경로에서 사용) |
name | 페어링 시 제공된 디바이스 이름 |
device_type | 디바이스 유형 레이블 |
hardware | 하드웨어 / 플랫폼 레이블 |
paired_at | 디바이스가 처음 페어링된 시각 |
last_seen | 디바이스의 마지막 인증 요청 시각 |
ip_address | 디바이스에 기록된 소스 IP |
서비스 토큰 (X-Revka-Service-Token)
섹션 제목: “서비스 토큰 (X-Revka-Service-Token)”신뢰할 수 있는 로컬 사이드카(주로 operator-mcp 런타임)는 사용자 베어러 토큰 대신 내부 서비스 토큰으로 인증합니다. 서비스 토큰은 게이트웨이 시작 시 자동으로 생성되어 <state_dir>/service-token에 저장됩니다(POSIX에서 모드 0600). 다음 헤더로 전송합니다.
X-Revka-Service-Token: <service-token>이 헤더는 머신 간 특권 액세스가 필요한 경로에서 Authorization: Bearer와 함께 허용됩니다. 서비스 토큰 전용 주요 엔드포인트:
| 메서드 | 경로 | 서비스 토큰 전용 이유 |
|---|---|---|
POST | /api/cost/usage | 예산 원장을 수정합니다 — 사이드카가 토큰 사용량을 이곳에 보고합니다 |
POST | /api/auth/profiles/{id}/resolve | 단계 실행 시 저장된 워크플로 자격증명을 복호화합니다 |
# A trusted sidecar reports token usage to the cost ledgercurl -X POST http://127.0.0.1:42617/api/cost/usage \ -H "X-Revka-Service-Token: $(cat ~/.revka/service-token)" \ -H 'Content-Type: application/json' \ -d '{"model":"anthropic/claude-sonnet-4","provider":"openrouter","input_tokens":1200,"output_tokens":340}'속도 제한
섹션 제목: “속도 제한”두 개의 독립적인 레이어가 슬라이딩 윈도우 방식으로 클라이언트 IP별로 키를 설정하여 인증 영역을 무차별 대입 공격으로부터 보호합니다.
인증 속도 제한기 (무차별 대입 잠금)
섹션 제목: “인증 속도 제한기 (무차별 대입 잠금)”AuthRateLimiter는 페어링 및 베어러 토큰 엔드포인트를 보호합니다.
| 상수 | 값 | 의미 |
|---|---|---|
MAX_ATTEMPTS | 10 | 윈도우당 허용되는 실패 횟수 |
WINDOW_SECS | 60 | 슬라이딩 윈도우 지속 시간 |
LOCKOUT_SECS | 300 | 한도 초과 후 잠금 시간 (5분) |
잠금 시 엔드포인트는 429 Too Many Requests와 함께 잠금 해제까지 남은 초를 알려주는 Too many attempts. Locked out for Ns 메시지를 반환합니다. 오래된 항목은 메모리 사용량을 제한하기 위해 5분마다 정리됩니다.
게이트웨이 수준 페어링 속도 제한 (설정)
섹션 제목: “게이트웨이 수준 페어링 속도 제한 (설정)”별도로, 게이트웨이는 페어링(및 웹훅) 경로에 분당 슬라이딩 윈도우 제한기를 적용하며, 설정에서 조정할 수 있습니다.
[gateway]pair_rate_limit_per_minute = 10 # 0 disables the pairing rate limitwebhook_rate_limit_per_minute = 60 # 0 disables the webhook rate limitrate_limit_max_keys = 10000 # max tracked IP keys (LRU eviction)trust_forwarded_headers = false # see caution below| 키 | 타입 | 기본값 | 의미 |
|---|---|---|---|
gateway.pair_rate_limit_per_minute | integer | 10 | IP당 분당 페어링 시도 횟수. 0은 비활성화. |
gateway.webhook_rate_limit_per_minute | integer | 60 | IP당 분당 웹훅 요청 횟수. 0은 비활성화. |
gateway.rate_limit_max_keys | integer | 10000 | 축출 전 최대 추적 IP 키 수. |
gateway.trust_forwarded_headers | boolean | false | 클라이언트 키에 X-Forwarded-For / X-Real-IP 사용. |
빠른 시작: 클라이언트 페어링
섹션 제목: “빠른 시작: 클라이언트 페어링”-
게이트웨이를 시작하고 시작 출력에서 페어링 코드를 확인하거나 조회합니다.
Terminal window revka gatewayrevka gateway get-paircode -
코드를 토큰으로 교환합니다.
Terminal window curl -X POST http://127.0.0.1:42617/api/pair \-H 'Content-Type: application/json' \-d '{"code": "123456", "device_name": "My Laptop", "device_type": "cli"}' -
반환된
rk_토큰을 저장합니다 — 한 번만 표시됩니다. -
인증된 엔드포인트를 호출하여 확인합니다.
Terminal window curl http://127.0.0.1:42617/api/status \-H "Authorization: Bearer rk_<token>"
문제 해결
섹션 제목: “문제 해결”- “Too many attempts. Locked out for Ns”. 인증 속도 제한기에 걸렸습니다. 보고된 초만큼 기다리거나,
127.0.0.1(루프백은 제외)에서 페어링하십시오. - 페어링 입력이 코드를 거부합니다. 코드는 일회용입니다.
revka gateway get-paircode --new로 교체한 후 다시 시도하십시오. - 프록시 뒤에 있고 단일 공유 IP로 잠깁니다. 제한기가 실제 클라이언트 IP를 키로 사용하도록
trust_forwarded_headers = true로 설정하십시오. 단, 프록시가 신뢰할 수 있는 경우에만 설정하십시오. - 디바이스 토큰을 분실했습니다.
POST /api/devices/{id}/token/rotate로 새 코드를 발급받거나,DELETE /api/devices/{id}후 재페어링하십시오. - 모든
/api/*호출에서401이 반환됩니다.Authorization: Bearer rk_<token>헤더가 있는지, 토큰이 폐기되지 않았는지 확인하십시오. 사이드카는X-Revka-Service-Token을 사용해야 합니다.