로그, 감사, 닥터, 페어링, 스킨 페이지
실시간 로그 스트림, 체인 검증 기반 감사 추적, 진단, 기기 페어링, UI 스킨·테마 시스템.
이 페이지에서는 대시보드의 검사 및 운영 화면을 다룹니다. 문제가 발생했거나 배포 환경을 브랜딩하고 페어링하려 할 때 찾게 되는 화면들입니다. Inspection 섹션에는 실시간 Logs 스트림, 변조 방지 Audit 추적, 온디맨드 Doctor 진단이 포함되어 있습니다. Operations 섹션에는 Device Pairing과 UI Skins가 있습니다. 두 가지 페이지 공통 시스템도 여기서 동작합니다: 앱 전역 에이전트 이벤트 SSE 스트림을 수신하는 승인 토스터와 언어 전환기를 포함한 테마 / 스킨 런타임입니다.
Logs는 데몬을 실시간으로 모니터링할 때, Audit은 변조 여부를 증명할 때, Doctor는 잘못된 설정을 진단할 때, Pairing은 새 브라우저나 전화를 인증할 때, Skins는 대시보드 테마를 변경할 때 사용합니다. 이 페이지의 모든 기능은 대시보드의 bearer-token 인증 뒤에 있습니다. 브라우저를 먼저 페어링하려면 대시보드 실행을 참고하세요.
Logs 페이지 (/logs)
섹션 제목: “Logs 페이지 (/logs)”Logs 페이지는 Server-Sent Events를 통해 데몬에서 전달되는 실시간 이벤트 스트림입니다. 에이전트가 실행되면 색상이 지정된 이벤트 카드가 하단에 나타나며 자동 스크롤됩니다. 버퍼는 최근 500개 항목으로 제한되며, 새 항목이 도착하면 오래된 카드는 삭제됩니다.
다음 SSE 엔드포인트를 구독합니다:
GET /api/daemon/logsAuthorization: Bearer rk_<token>Accept: text/event-stream이는 라이브 테일 방식으로, 연결 시 과거 로그는 재전송되지 않으므로 페이지를 열기 전에 발생한 이벤트는 볼 수 없습니다. 기반 SSEClient는 스트림이 끊기면 자동으로 재연결합니다.
이벤트 유형
섹션 제목: “이벤트 유형”각 이벤트는 type에 따라 색상 코드가 지정된 카드로 렌더링됩니다. 인식되는 이벤트 유형은 다음과 같습니다:
| 이벤트 유형 | 의미 |
|---|---|
error | 런타임에서 발생한 오류 |
warn / warning | 경고 |
tool_call / tool_call_start / tool_result | 에이전트 도구 활동 |
llm_request | 외부 모델 요청 |
agent_start / agent_end | 에이전트 턴 경계 |
message / chat | 채팅 트래픽 |
log | 일반 로그 라인 |
log_unavailable | 게이트웨이가 로그 파일을 읽을 수 없음 |
log_unavailable 이벤트는 데몬은 실행 중이지만 자체 로그 파일을 테일링할 수 없다는 의미입니다. 에이전트가 유휴 상태라고 단정하기 전에 파일 권한과 로그 경로를 확인하세요.
컨트롤
섹션 제목: “컨트롤”- 일시 정지 / 재개 — 카드가 스크롤되지 않도록 스트림을 고정하여 내용을 읽을 수 있습니다. 새 이벤트는 계속 수신되며, 재개 시 따라잡을 수 있습니다.
- 자동 스크롤 — 패널은 기본적으로 최신 항목을 따릅니다. 패널 내부를 클릭하면 자동 스크롤이 비활성화되어 이전 카드를 검사할 수 있으며, 맨 아래로 다시 스크롤하면 재활성화됩니다.
- 유형 필터 — 이벤트 유형 칩을 토글하여 카테고리 전체를 숨기거나 표시할 수 있습니다. 예를 들어
tool_call노이즈를 숨기고error와warn에 집중할 수 있습니다.

감사 추적 페이지 (/audit)
섹션 제목: “감사 추적 페이지 (/audit)”Audit 페이지는 Revka의 변조 방지 Merkle 해시 체인 감사 로그 뷰어입니다. 보안 관련 모든 이벤트는 추가 전용 JSONL 로그에 기록되며, 각 항목의 entry_hash는 SHA-256(prev_hash || canonical_content)로 계산되고 단조 증가하는 sequence를 포함합니다. 항목을 수정하거나 삭제하면 이후의 모든 해시가 깨지며, 이것이 바로 체인 검증기가 감지하는 내용입니다.
두 개의 엔드포인트를 사용합니다:
| 작업 | Method + path | 인증 |
|---|---|---|
| 이벤트 목록 조회 | GET /api/audit?limit=100&event_type=<type> | Bearer |
| 체인 무결성 검증 | GET /api/audit/verify | Bearer |
GET /api/audit은 limit(기본값 50, 최대 500), 선택적 event_type 필터, 선택적 RFC 3339 형식 since 타임스탬프를 받습니다. 각 이벤트의 형태는 다음과 같습니다:
{ "timestamp": "2026-06-18T10:00:00Z", "event_id": "<uuid>", "event_type": "command_execution", "actor": { "channel": "telegram", "user_id": "123", "username": "@alice" }, "action": { "command": "ls -la", "risk_level": "low", "approved": false, "allowed": true }, "result": { "success": true, "exit_code": 0, "duration_ms": 15 }, "security": { "policy_violation": false, "rate_limit_remaining": 19, "sandbox_backend": "firejail" }, "sequence": 42, "prev_hash": "<64 hex>", "entry_hash": "<64 hex>", "signature": "<64 hex>"}이벤트 유형
섹션 제목: “이벤트 유형”이벤트 유형 드롭다운으로 스트림을 필터링할 수 있습니다. 지원되는 유형은 다음과 같습니다:
command_execution, file_access, config_change, auth_success, auth_failure, policy_violation, security_event.
security.policy_violation이 true인 이벤트에는 정책 위반 배지가 표시되어 차단되거나 위험한 작업을 한눈에 확인할 수 있습니다.
체인 검증
섹션 제목: “체인 검증”Verify Chain을 클릭하면 무결성 검사를 실행합니다. 결과는 방패 아이콘과 함께 인라인으로 렌더링됩니다: 검증 성공 시 녹색, 실패 시 빨간색으로 표시되며, 전체 entry_count와 체인이 깨진 경우 오류 메시지가 함께 표시됩니다.
GET /api/audit/verifyAuthorization: Bearer rk_<token>{ "verified": true, "entry_count": 1284 }이 검사는 대시보드 개요의 Risk Rail 감사 배지에서도 사용되며, 60초마다, 그리고 창이 포커스를 받을 때마다 /api/audit/verify를 다시 폴링합니다. 검증 실패는 디스크 상의 로그가 변조되었음을 의미하며, 대시보드가 발생시킬 수 있는 가장 중요한 보안 경보입니다.
Doctor 페이지 (/doctor)
섹션 제목: “Doctor 페이지 (/doctor)”Doctor 페이지는 Revka의 내장 진단 스위트를 온디맨드로 실행하며, 서브시스템 카테고리별로 그룹화된 각 검사 결과와 함께 통과/경고/실패 요약을 보고합니다. revka doctor CLI와 동일한 기능을 제공합니다.
Run Diagnostics를 클릭하면 실행됩니다. 페이지는 빈 본문을 POST하고 결과 목록을 받습니다:
POST /api/doctorAuthorization: Bearer rk_<token>Content-Type: application/json
{}{ "results": [ { "category": "network", "message": "Provider reachable", "severity": "ok" }, { "category": "memory", "message": "Kumiho round-trip slow", "severity": "warn" }, { "category": "agent", "message": "MCP server not connected", "severity": "error" } ], "summary": { "ok": 8, "warnings": 1, "errors": 1 }}각 결과는 category, 사람이 읽을 수 있는 message, 그리고 ok, warn, error 중 하나인 severity를 포함합니다. 요약 패널에서 각 카운트를 집계합니다. 검사 항목은 일반적으로 API 키/프로바이더 연결성, 메모리 백엔드, MCP 연결, 네트워크, 파일 시스템 등 여러 서브시스템에 걸쳐 있습니다.
기기 페어링 페이지 (/pairing)
섹션 제목: “기기 페어링 페이지 (/pairing)”Pairing 페이지는 게이트웨이 호출이 허가된 기기(브라우저, 전화, 사이드카)를 관리합니다. 모든 /api/* 요청은 페어링을 통해 획득한 Authorization: Bearer rk_<token> 토큰을 전달합니다. 이 페이지에서 새 기기용 코드를 발급하고, QR 코드를 표시하고, 코드를 교체하고, 더 이상 신뢰하지 않는 기기를 취소할 수 있습니다.
사용하는 엔드포인트는 다음과 같습니다:
| 작업 | Method + path | 인증 |
|---|---|---|
| 페어링된 기기 목록 조회 | GET /api/devices | Bearer |
| 새 페어링 코드 생성 | POST /api/pairing/initiate | Bearer |
| 기기 취소 | DELETE /api/devices/{id} | Bearer |
| 현재 시작 코드 읽기 | GET /pair/code | 없음 |
POST /api/pairing/initiate는 새 기기가 자체 토큰으로 교환할 수 있는 신규 코드를 반환합니다:
{ "pairing_code": "123456" }기기 목록(GET /api/devices)은 항목당 DeviceInfo를 반환합니다 — id, name, device_type, hardware, paired_at, last_seen, ip_address — 페어링된 클라이언트를 구분하고 올바른 기기를 취소할 수 있습니다. DELETE /api/devices/{id}로 취소하면 해당 기기의 bearer 토큰이 즉시 무효화됩니다.
새 기기 페어링
섹션 제목: “새 기기 페어링”- Pair New Device를 클릭하면
/api/pairing/initiate가 호출되고 6자리 코드와 QR 코드가 표시됩니다. - 새 기기에서 QR을 스캔하거나(또는 대시보드 로그인 화면에 코드를 직접 입력) 연결합니다.
- 새 기기는 선택적 기기 메타데이터와 함께 코드를
POST /api/pair에 전송하고 자체 영구 토큰을 받습니다. - 새 기기가 목록에 나타납니다. 이후 쓰레기통 아이콘으로 기기를 취소할 수 있습니다.
QR 페이로드 형식
섹션 제목: “QR 페이로드 형식”QR 코드는 모바일 클라이언트가 호스트와 코드를 자동으로 채울 수 있도록 JSON 문자열을 인코딩합니다:
{ "v": 1, "type": "revka-pair", "host": "<origin><basePath>", "code": "<6-digit>" }host는 게이트웨이 오리진에 역방향 프록시 베이스 경로를 포함한 값이므로, 스캔하는 클라이언트가 POST /api/pair를 보낼 위치를 정확히 알 수 있습니다. 코드를 교체하면 즉시 새 값이 생성되고 이전 값은 무효화됩니다.
승인 토스터 & 에이전트 이벤트
섹션 제목: “승인 토스터 & 에이전트 이벤트”두 가지 앱 전역 시스템이 어느 라우트를 보고 있든 상관없이 모든 대시보드 페이지에서 동작합니다.
에이전트 이벤트 (전역 SSE 컨텍스트)
섹션 제목: “에이전트 이벤트 (전역 SSE 컨텍스트)”시작 시 대시보드는 게이트웨이의 브로드캐스트 이벤트 스트림에 애플리케이션 수준 SSE 구독을 열고 모든 컴포넌트와 공유합니다:
GET /api/eventsAuthorization: Bearer rk_<token>Accept: text/event-stream이벤트는 { "type": "...", "payload": { ... }, "timestamp": "..." } 봉투 형식을 사용하며 channel_event, human_approval_request, 관찰 가능성 메트릭을 포함합니다. 이 스트림은 아래 승인 토스터와 같은 실시간 업데이트의 근간이 됩니다. 컴포넌트들은 각자 연결을 열지 않고 공유 컨텍스트를 구독합니다.
승인 토스터
섹션 제목: “승인 토스터”워크플로 실행 단계가 approval_required 상태에 진입하면, 게이트웨이는 /api/events에 human_approval_request를 브로드캐스트합니다. 대시보드의 대기 중 승인 컨텍스트가 이를 수집하고 현재 어느 페이지에 있든 Approve / Reject 버튼이 있는 토스트를 표시합니다. 토스트에서 작업하면 게이트가 인라인으로 처리됩니다:
POST /api/workflows/runs/{run_id}/approveAuthorization: Bearer rk_<token>Content-Type: application/json
{ "approved": true, "feedback": "looks good" }거부하려면 "approved": false를 전송합니다. 선택적 feedback 문자열은 실행으로 다시 전달됩니다. 토스터는 페이지 전역에서 작동하므로, Logs, Doctor, Skins 페이지에 있어도 백그라운드 워크플로에서 발생한 승인 요청이 표시됩니다. 대기 중인 승인 수를 나타내는 배지 카운트도 표시됩니다. 동일한 승인/거부 흐름은 워크플로, 에디터 및 실행 페이지에서도 사용 가능하며, 게이트 라이프사이클은 실행, 승인 및 체크포인트에서 설명합니다.
UI Skins 페이지 (/skins)
섹션 제목: “UI Skins 페이지 (/skins)”Skins 페이지에서는 대시보드를 재테마화하는 UI 스킨 패키지를 탐색하고, 활성화하고, 업로드하고, 삭제할 수 있습니다. 스킨은 라이트/다크 모드별로 정의된 CSS 변수 토큰 재정의와 명명된 이미지 에셋 슬롯을 포함하는 revka-skin.json 매니페스트가 담긴 ZIP 파일입니다. 두 개의 내장 스킨 — revka(기본 중립-흑연 엔터프라이즈 팔레트)와 matrix(녹색 콘솔 스타일) — 은 항상 존재하며 삭제할 수 없습니다.
다음 라우트를 사용합니다:
| 작업 | Method + path | 인증 |
|---|---|---|
| 설치된 스킨 목록 조회 | GET /api/skins | Bearer |
| 스킨 ZIP 가져오기 | POST /api/skins/import | Bearer |
| 스킨 삭제 | DELETE /api/skins/{id} | Bearer |
| 스킨 에셋 제공 | GET /api/skins/{id}/assets/{path} | Bearer |
GET /api/skins는 스킨 요약(id, name, version, builtin, 파싱된 manifest)을 반환합니다. 내장 스킨은 "builtin": true를 가지며 삭제 시 아무 작업도 하지 않습니다. 각 스킨 카드는 최대 4개의 색상 칩이 있는 스와치로 라이트/다크 모드 미리보기를 보여줍니다. Activate를 클릭하면 스킨이 적용됩니다. ZIP 파일을 페이지에 드래그 앤 드롭하거나 업로드 버튼을 사용하여 설치하고, 쓰레기통 아이콘으로 삭제합니다.
가져오기 제한 및 유효성 검사
섹션 제목: “가져오기 제한 및 유효성 검사”가져오기(POST /api/skins/import)는 원시 ZIP 본문을 받아 엄격한 제한을 적용합니다:
| 제한 | 값 |
|---|---|
| ZIP 본문 | 25 MiB |
| 요청 타임아웃 | 120초 |
| 압축 해제 총 크기 | 50 MiB |
| 파일 수 | 128 |
| 허용 확장자 | .json, .png, .jpg, .jpeg, .webp |
가져오기 도구는 다음과 같은 패키지를 거부합니다: 루트 revka-skin.json 누락 또는 중복, 절대 경로, .. 경로 탐색, 스킨 루트를 벗어나는 경로 구분자, 심볼릭 링크, 중복 항목, 지원하지 않는 확장자, 알 수 없는 모드 또는 에셋 슬롯, 예약된 ID revka/matrix, 유효하지 않은 ID, ZIP에 없는 매니페스트 에셋 참조, JavaScript/CSS/HTML/SVG/원격 URL 콘텐츠. 가져온 스킨은 {workspace_dir}/skins/{skin_id}/ 아래에 저장됩니다.
UI 스킨 참조
섹션 제목: “UI 스킨 참조”revka-skin.json 매니페스트
섹션 제목: “revka-skin.json 매니페스트”매니페스트는 id, 표시 name, version, schemaVersion(반드시 1이어야 함), 그리고 light, dark 또는 둘 다를 가진 modes 객체를 선언합니다. 각 모드는 tokens(CSS 변수 재정의), assets(슬롯 → 상대 경로), 선택적 preview 이미지를 정의할 수 있습니다.
revka-skin.json ← ZIP 루트의 매니페스트assets/ logo.png avatar.webp hero.jpg{ "schemaVersion": 1, "id": "rabbit_garden", "name": "Rabbit Garden", "version": "1.0.0", "modes": { "light": { "tokens": { "--revka-bg-base": "#f7fbf2", "--revka-bg-surface": "#ffffff", "--revka-text-primary": "#142018", "--revka-signal-live": "#4e9f5b", "--revka-radius-md": "14px" }, "assets": { "brandLogo": "assets/logo.png", "operatorAvatar": "assets/avatar.webp", "dashboardHero": "assets/hero.jpg" }, "preview": "assets/hero.jpg" }, "dark": { "tokens": { "--revka-bg-base": "#090607", "--revka-text-primary": "#f6ecef" } } }}| 필드 | 목적 |
|---|---|
schemaVersion | 반드시 1이어야 함 |
id | 저장 ID; 소문자, 숫자, -, _만 허용 |
name | 표시 이름 |
version | 스킨 패키지 버전 |
modes | light, dark 또는 둘 다를 포함하는 객체 |
토큰 계약
섹션 제목: “토큰 계약”작성 가능한 토큰은 반드시 표준 --revka-* 네임스페이스를 사용해야 합니다. --pc-* 호환성 토큰은 프런트엔드 브리지에서 생성되며, 직접 작성해서는 안 됩니다. 허용 값은 색상(hex, rgb(), rgba(), hsl(), hsla())과 반지름/간격/폰트 크기 관련 토큰에 대한 제한된 CSS 길이(px, rem, em, %)입니다. v1에서 거부되는 것: url(...), var(...), expression(...), JavaScript형 URL, 그림자 토큰, 배경 이미지 토큰, 임의 CSS.
| 토큰 | 사용처 |
|---|---|
--revka-bg-base | 페이지 배경 |
--revka-bg-shell | 사이드바 / 셸 표면 |
--revka-bg-surface | 카드 및 포함된 표면 |
--revka-bg-panel | 반투명 패널 배경 |
--revka-border-soft / --revka-border-strong | 낮은 강조 / 활성 테두리 |
--revka-text-primary / --revka-text-secondary / --revka-text-muted | 텍스트 강조 수준 |
--revka-signal-live / --revka-signal-network | 기본 / 보조 액센트 |
--revka-radius-sm / --revka-radius-md / --revka-radius-lg | 모서리 반지름 |
에셋 슬롯
섹션 제목: “에셋 슬롯”에셋 값은 assets/ 아래의 상대 경로입니다(원격 URL, 절대 경로, 경로 탐색, SVG는 거부됩니다). 전체 슬롯 목록:
| 슬롯 | UI 사용처 |
|---|---|
brandLogo | 사이드바 / 헤더 브랜드 이미지 |
operatorAvatar | 운영자 / 에이전트 아바타 표면 |
dashboardHero | 대시보드 배너 이미지 |
dashboardShowcase | 대형 대시보드 시각 패널 (캐릭터 / 씬 / 제품 아트) |
dashboardAccent | 소형 대시보드 쇼케이스 액센트 소품 또는 엠블럼 |
shellTexture | 미묘한 셸 배경 텍스처 |
panelDecoration | 미묘한 패널 모서리 장식 |
pageBackdrop | 셸 뒤의 전체 애플리케이션 배경 |
sidebarBackdrop | 사이드바 배경 아트 또는 텍스처 |
headerBackdrop | 상단 헤더 커맨드 밴드 배경 아트 |
graphBackdrop | 워크플로 / 팀 그래프 캔버스 배경 |
metricDecoration | 개요 메트릭 카드 장식 |
runCardDecoration | 선택된 실행 / 실행 요약 장식 |
stepCardDecoration | 선택된 단계 / 선택된 노드 장식 |
timelineDecoration | 우선순위 타임라인 장식 |
riskRailDecoration | 리스크 레일 카드 장식 |
agentRailDecoration | 에이전트 레일 카드 장식 |
commandBandDecoration | 커맨드 밴드 카드 장식 |
recentRunsDecoration | 최근 실행 레일 장식 |
statusRunningBadge | 실행 중 상태 배지 아트 |
statusSuccessBadge | 완료 / 성공 상태 배지 아트 |
statusFailedBadge | 실패 상태 배지 아트 |
statusPendingBadge | 대기 중 / 차단됨 상태 배지 아트 |
statusSkippedBadge | 건너뜀 상태 배지 아트 |
테마 / 스킨 시스템
섹션 제목: “테마 / 스킨 시스템”Skins 페이지 아래에는 ThemeContext를 통해 노출되는 CSS 변수 기반 테마 런타임이 있습니다. 모든 컴포넌트에 다크/라이트 모드 토글과 스킨 관리 기능을 제공합니다:
resolvedTheme— 현재 적용된 라이트/다크 모드.toggleMode()— 라이트와 다크 간 전환.setSkin(id)— 설치된 스킨(내장 또는 가져온) 활성화.deleteSkin(id)— 가져온 스킨 삭제.getSkinAsset(slotName)— 슬롯(예:brandLogo)을 활성 스킨과 모드에 맞는 제공 URL로 해석하며, 슬롯이 설정되지 않은 경우 내장 에셋으로 폴백.
활성 스킨과 모드는 localStorage에 저장되어 다음 로드 시 재적용되므로, 브랜딩된 배포 환경은 페이지 새로고침 후에도 브랜딩을 유지합니다. 내장 라이트/다크 테마는 업로드된 스킨 패키지와 무관하게 항상 존재하며, 업로드된 스킨은 그 위에 토큰 및 에셋 재정의를 레이어로 추가합니다.
언어 전환기 (i18n)
섹션 제목: “언어 전환기 (i18n)”대시보드는 완전히 국제화되어 있습니다. 모든 UI 문자열은 useT() 훅을 통해 처리됩니다 — 일반 문자열에는 t(key), 보간이 필요한 경우에는 tpl(key, vars) — 지원 로케일은 i18n 모듈에 정의되어 있습니다. 레이아웃 헤더의 언어 전환기는 런타임에 활성 로케일을 변경하며, 선택 사항은 localStorage에 저장되어 페이지 새로고침 후에도 유지됩니다. 로케일 전환은 에이전트 출력이나 저장된 데이터에 영향을 미치지 않으며, 대시보드 크롬만 다시 렌더링됩니다.