변경 이력
RAWP 1.0.2
1.0.1 대비 변경 사항을 정리한 문서입니다.
수정됨
Document Overview
추가된 줄 6 삭제된 줄 6
1 1
# RAWP 1.0: Remote Agent Wire Protocol 2 2
3
| 항목 | 값 | 4
| ---------------------- | ---------------- | 5
| 상태 | Stable | 6
| 버전 | 1.0 | 7
| 데이터 평면 규격 | **RAWP-DPS 1.0** | 8
| 클라이언트 렌더링 규격 | **RAWP-CRS 1.0** | 3
| 항목 | 값 | 4
| ---------------------- | ------------------ | 5
| 상태 | Stable | 6
| 버전 | 1.0.2 | 7
| 데이터 평면 규격 | **RAWP-DPS 1.0.1** | 8
| 클라이언트 렌더링 규격 | **RAWP-CRS 1.0.1** | 9 9
10 10
--- 수정됨
1. 개요 (Introduction)
추가된 줄 11 삭제된 줄 8
1 1
## 1. 개요 (Introduction) 2 2
3 3
RAWP 1.0은 중앙 제어 서버(Master Server)가 원격지의 게이트웨이(Local Client)를 안전하게 제어하기 위한 Push 기반의 분산 제어 프로토콜이다. 제어 및 상태 모니터링을 위한 Stateless HTTP 통신과, 실시간 데이터 입출력을 위한 Stateful WebSocket 통신으로 제어 평면(Control Plane)과 데이터 평면(Data Plane)을 분리한다. 4 4
5 5
### 1.1. 요구사항 표기 규약 (Requirements Notation) 6 6
7 7
본 문서의 "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", "OPTIONAL"은 RFC 2119를 따른다. 8 8
9 9
### 1.2. 용어 정의 (Terminology) 10 10
11
| 용어 | 정의 | 12
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | 13
| Master Server | 클라이언트 노드를 관리하고 제어 명령을 하달하는 중앙 서버 (Control Plane). | 14
| Local Client | 마스터 서버의 명령을 수신하는 HTTP 엔드포인트를 소유하며, 로컬 프로세스(Agent)의 실행 및 I/O를 담당하는 에지 게이트웨이 (Execution Plane). | 15
| Agent | 클라이언트 환경에서 실행되는 실제 단위 작업 프로세스 (예: LLM, 스크립트 등). | 16
| Session | 특정 에이전트의 실행부터 종료까지의 라이프사이클 및 I/O 스트림의 논리적 단위. | 17
| Config Scope | 클라이언트 설정의 논리적 분류 단위. 각 스코프는 독립된 엔드포인트, 독립된 `config_version`, 독립된 갱신 주기를 갖는다. | 18
| Session-Pinned Config | 세션 초기화 시점에 바인딩된 설정 스냅샷. 해당 세션의 전체 라이프사이클 동안 불변이다. | 11
| 용어 | 정의 | 12
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 13
| Master Server | 클라이언트 노드를 관리하고 제어 명령을 하달하는 중앙 서버 (Control Plane). | 14
| Local Client | 마스터 서버의 명령을 수신하는 HTTP 엔드포인트를 소유하며, 로컬 프로세스(Agent)의 실행 및 I/O를 담당하는 에지 게이트웨이 (Execution Plane). | 15
| Agent | 클라이언트 환경에서 실행되는 실제 단위 작업 프로세스 (예: LLM, 스크립트 등). | 16
| Agent Session | 특정 에이전트의 실행부터 종료까지의 라이프사이클 및 I/O 스트림의 논리적 단위. `session_id`(UUID v4)로 식별한다. 상태 머신: `INIT → RUNNING → DETACHED → TERMINATED`. 하나의 Agent Session에 복수의 WSS Connection이 바인딩될 수 있다. 본 문서에서 "세션"은 별도 명시가 없는 한 Agent Session을 의미한다. | 17
| WSS Connection | Agent Session에 바인딩된 WebSocket 전송 채널. 티켓(§5.3)을 통해 수립되며, 네트워크 단절 시 소멸하고 재연결(reattach)으로 재수립할 수 있다. 동일 Agent Session에 여러 WSS Connection이 동시에 존재할 수 있다 (예: 마스터 연결 + 로컬 UI 연결, 또는 다중 엣지 연결). | 18
| Config Scope | 클라이언트 설정의 논리적 분류 단위. 각 스코프는 독립된 엔드포인트, 독립된 `config_version`, 독립된 갱신 주기를 갖는다. | 19
| Session-Pinned Config | 세션 초기화 시점에 바인딩된 설정 스냅샷. 해당 세션의 전체 라이프사이클 동안 불변이다. | 19 20
21
**Agent Session과 WSS Connection의 관계**: Agent Session은 에이전트의 논리적 생명주기이고, WSS Connection은 그 세션의 데이터를 전달하는 전송 채널이다. Agent Session이 `RUNNING` 상태에서 WSS Connection이 단절되면 Agent Session은 `DETACHED`로 전이하지만 에이전트 프로세스는 계속 실행된다. WSS Connection이 재수립(reattach)되면 Agent Session은 `RUNNING`으로 복귀한다. Agent Session이 `TERMINATED`되면 모든 WSS Connection이 종료된다. 22
20 23
### 1.3. 호환성 및 파싱 규약 (Forward Compatibility) 21 24
22 25
- **필드 무시 원칙 (Postel's Law)**: 클라이언트와 서버의 JSON 파서는 스펙에 정의되지 않은 알 수 없는 키(Unknown Key)를 수신하더라도 파싱 에러를 발생시키지 않고 조용히 무시해야 한다 (MUST). 23 26
- **API 단종 예고**: 서버는 향후 지원이 중단될 API 응답에 `Warning: 299 - "Deprecated API"` HTTP 헤더를 포함할 수 있으며, 수신자는 이를 감지 시 시스템 로그에 기록해야 한다 (MUST). 24 27
25 28
--- 수정됨
2. Discovery 및 엔드포인트 라우팅
추가된 줄 0 삭제된 줄 2
1 1
## 2. Discovery 및 엔드포인트 라우팅 2 2
3 3
### 2.1. API 버전 라우팅 4 4
5 5
모든 HTTP 및 WSS URI는 반드시 버전을 명시해야 한다 (예: `/v1/`). 페어링 시점부터 적용되며, 버전이 누락되거나 불일치할 경우 수신 측은 `404 Not Found` 또는 `426 Upgrade Required`를 반환해야 한다 (MUST). 6 6
7 7
### 2.2. Discovery Endpoint (Master Server) 8 8
9 9
클라이언트가 서버의 호환 버전을 동적으로 파악하기 위한 표준 진입점이다. 10 10
11 11
**Endpoint**: `GET /.well-known/rawp-configuration` 12 12
13 13
**Response** (200 OK): 14 14
15 15
```json 16 16
{ 17 17
"supported_versions": ["v1", "v1.1"], 18 18
"data_plane_protocol": "rawp-dps-1.0", 19 19
"endpoints": { 20 20
"pair": "/v1/auth/pair", 21 21
"metrics": "/v1/nodes/metrics" 22 22
} 23 23
} 24 24
``` 25 25
26
> **변경 사항**: RAWP-DPS 1.0 적용 시 `data_plane_protocol` 필드가 추가되었다. 이 필드는 서버가 지원하는 데이터 평면 규격을 명시하며, 클라이언트는 이를 확인하여 호환되는 데이터 평면 규격을 선택해야 한다 (MUST). 필드가 없으면 RAWP-DPS-0.1-Legacy로 간주한다. 27
28 26
--- 수정됨
3. Phase 1: 등록, 페어링 및 키 교환 (Registration & Lifecycle)
추가된 줄 38 삭제된 줄 2
1 1
## 3. Phase 1: 등록, 페어링 및 키 교환 (Registration & Lifecycle) 2 2
3 3
클라이언트와 마스터 서버가 상호 통신을 위한 토큰 페어(Access/Refresh)를 교환하고 검증하는 단계다. 4 4
5 5
### 3.1. 페어링 URL 발급 (Master Server) 6 6
7 7
- **형식**: `https://{master_host}/v1/auth/pair?token={pairing_token}` 8 8
- **제약 조건**: `pairing_token`은 발급 후 15분 이내에 사용되지 않으면 반드시 폐기되어야 한다 (MUST). 9 9
10 10
### 3.2. 등록 주소 검증 규약 (Address Validation) 11 11
12 12
클라이언트가 제출하는 `client_address`에 대한 마스터 서버의 검증 규칙: 13 13
14 14
- **Allowed Schemes**: `https://` 사용 강제 (단, 명시적인 개발 플래그 활성화 시 `http://` 허용). 15 15
- **Loopback Forbidden**: `127.0.0.1`, `localhost` 등 루프백 및 링크 로컬 주소는 등록을 거부해야 한다 (MUST). 16 16
- **Private Network**: `192.168.x.x` 등 사설 IP는 허용된다. 17 17
18 18
### 3.3. 등록 요청 및 상호 키 교환 (Mutual Registration) 19 19
20 20
클라이언트는 등록 시 자신이 마스터 서버를 호출할 때 쓸 토큰을 발급받음과 동시에, 마스터 서버가 자신을 호출할 때 사용할 초기 토큰 페어를 생성하여 전달해야 한다 (MUST). 21 21
22 22
**Endpoint**: `POST /v1/auth/pair` (Master Server 제공) 23 23
24 24
**Request** (Client → Master): 25 25
26 26
```json 27 27
{ 28 28
"pairing_token": "String (필수, 최소 32자 이상)", 29 29
"client_address": "String (필수, 향후 마스터가 호출할 URI)", 30 30
"device_name": "String (필수, 최대 100자)", 31 31
"client_credentials": { 32 32
"access_token": "String (필수, 마스터가 클라이언트를 호출할 때 사용할 토큰)", 33 33
"refresh_token": "String (필수, 클라이언트용 갱신 토큰)", 34 34
"expires_in": "Number (필수, 초 단위)" 35 35
} 36 36
} 37 37
``` 38 38
39 39
**Response** (Master → Client) (200 OK): 40 40
41 41
```json 42 42
{ 43 43
"access_token": "String (필수, 클라이언트가 마스터를 호출할 때 사용할 Bearer 토큰)", 44 44
"refresh_token": "String (필수, 마스터용 장기 갱신 토큰)", 45 45
"expires_in": "Number (필수)", 46 46
"server_credentials": "Object (필수, 서버 서명 및 자격 증명용 JWK 공개키)" 47 47
} 48 48
``` 49 49
50 50
### 3.4. 토큰 갱신 엔드포인트 대칭 구현 (Token Refresh) 51 51
52 52
양측은 타 API 호출 시 `401 Unauthorized`를 받으면, 발급 주체의 갱신 엔드포인트를 호출하여 토큰을 갱신하고 원래 요청을 재시도해야 한다 (MUST). 53 53
54
### 3.4.1. 클라이언트의 마스터 토큰 갱신 (Client → Master) 54
#### 3.4.1. 클라이언트의 마스터 토큰 갱신 (Client → Master) 55 55
56 56
- **Endpoint**: `POST /v1/auth/refresh` (Master Server 제공) 57 57
- **Request**: `{"refresh_token": "String (필수)"}` 58 58
- **Response**: §3.3의 Master → Client 응답 포맷과 동일. 59 59
60
### 3.4.2. 마스터의 클라이언트 토큰 갱신 (Master → Client) 60
#### 3.4.2. 마스터의 클라이언트 토큰 갱신 (Master → Client) 61 61
62 62
- **Endpoint**: `POST /v1/auth/refresh` (Local Client 제공) 63 63
- **Request**: `{"refresh_token": "String (필수)"}` 64 64
- **Response**: 65 65
66 66
```json 67 67
{ 68 68
"access_token": "String (필수)", 69 69
"refresh_token": "String (필수)", 70 70
"expires_in": "Number (필수)" 71 71
} 72 72
``` 73 73
74
#### 3.4.3. 토큰 갱신 상세 규칙 75
76
**Refresh Token Rotation**: 토큰 갱신 응답에는 항상 새로운 `refresh_token`이 포함되어야 한다 (MUST). 갱신에 사용된 기존 `refresh_token`은 즉시 무효화해야 한다 (MUST). 이를 통해 탈취된 토큰의 재사용을 방지한다. 77
78
**Replay 감지**: 이미 사용(무효화)된 `refresh_token`으로 갱신이 시도되면, 토큰 발급 주체는 해당 클라이언트(또는 사용자)에게 발급된 모든 토큰(access + refresh)을 즉시 무효화해야 한다 (MUST). 이는 토큰 탈취 후 공격자와 정당한 사용자가 경쟁적으로 갱신하는 시나리오를 차단한다. 무효화 후 `401 Unauthorized`를 반환한다. 79
80
**갱신 실패 복구**: `refresh_token` 자체가 만료되었거나 무효화되어 갱신이 불가능한 경우(`401 Unauthorized` 수신), 클라이언트는 재페어링(§3.3) 절차를 개시해야 한다 (MUST). 81
82
#### 3.4.4. Edge → Master 토큰 관리 83
84
엣지 노드(§9)가 마스터 서버에 접속할 때 사용하는 사용자 토큰의 갱신 및 폐기 절차이다. 85
86
**토큰 갱신 — Endpoint**: `POST /v1/edge/auth/refresh` (Master Server 제공) 87
88
**Request**: `{"refresh_token": "String (필수)"}` 89
90
**Response (200 OK):** 91
92
```json 93
{ 94
"access_token": "String (필수)", 95
"refresh_token": "String (필수, 새로 발급된 토큰)", 96
"expires_in": "Number (필수, 초 단위)" 97
} 98
``` 99
100
§3.4.3의 Refresh Token Rotation 및 Replay 감지 규칙이 동일하게 적용된다 (MUST). 101
102
**토큰 폐기 (로그아웃) — Endpoint**: `POST /v1/edge/auth/revoke` (Master Server 제공) 103
104
**Request**: `{"refresh_token": "String (필수)"}` 105
106
**Response**: `204 No Content` 107
108
마스터는 해당 `refresh_token`과 연관된 모든 `access_token`을 즉시 무효화해야 한다 (MUST). 폐기 후 해당 토큰으로의 모든 API 호출은 `401 Unauthorized`를 반환한다. 109
74 110
### 3.5. 등록 해제 (Unregister) 75 111
76 112
클라이언트가 자발적으로 네트워크에서 이탈할 때 잔여 리소스를 정리한다. 77 113
78 114
- **Endpoint**: `DELETE /v1/nodes/self` (Master Server 제공) 79 115
- **Headers**: `Authorization: Bearer {client_access_token}` 80 116
- **제약 조건**: 마스터 서버는 해당 클라이언트와 연결된 모든 WSS 세션을 즉시 강제 종료하고 상태를 정리해야 한다 (MUST). 관련된 모든 토큰은 즉시 무효화된다. 81 117
- **Response**: `204 No Content` 82 118
83 119
--- 수정됨
4. Phase 2: 제어 및 모니터링 인터페이스 (Master → Client HTTP)
추가된 줄 99 삭제된 줄 36
1 1
## 4. Phase 2: 제어 및 모니터링 인터페이스 (Master → Client HTTP) 2 2
3 3
마스터 서버가 클라이언트를 호출할 때는 반드시 발급받은 `access_token`을 사용하며, 클라이언트는 서명을 검증해야 한다 (MUST). 4 4
5 5
### 4.1. 클라이언트 설정 동기화 (Client Configuration) 6 6
7 7
클라이언트는 시스템 한계치와 지원 능력을 서버에 동기화한다. 설정은 의미적 성격에 따라 독립된 Config Scope로 분리되며, 각 스코프는 전용 엔드포인트를 통해 전체 교체(Full Replacement) 방식으로 동기화된다. 마스터 서버는 수신한 설정 값으로 요청을 스로틀링하며, 보고되지 않은 기능을 요구해서는 안 된다 (MUST). 8 8
9
### 4.1.1. Config Scope 분리 원칙 9
#### 4.1.1. Config Scope 분리 원칙 10 10
11 11
클라이언트 설정은 다음 두 개의 Config Scope로 분리된다: 12 12
13 13
| Config Scope | 엔드포인트 | 성격 | 변경 트리거 | 14 14
| ------------------- | ----------------------------------- | -------------------------------------------------- | --------------------------------------------------------- | 15 15
| **Resource Limits** | `PUT /v1/nodes/config/limits` | 수량적 운영 제약 — 처리량, 전송 크기, 메모리, 정책 | 시스템 부하 변동, 메모리 압박, 운영 정책 조정 | 16 16
| **Capabilities** | `PUT /v1/nodes/config/capabilities` | 질적 기능 선언 — 클라이언트가 지원하는 기능 목록 | 플러그인/도구 로드·언로드, 모델 교체, 소프트웨어 업데이트 | 17 17
18 18
각 스코프는 독립된 `config_version`을 가지며, 한 스코프의 갱신이 다른 스코프의 버전에 영향을 주지 않는다 (MUST). 클라이언트는 변경이 발생한 스코프의 엔드포인트만 호출하면 되고, 변경되지 않은 스코프를 함께 재전송할 필요가 없다. 19 19
20
### 4.1.2. 초기 동기화 (Initial Synchronization) 20
#### 4.1.2. 초기 동기화 (Initial Synchronization) 21 21
22 22
클라이언트는 페어링 완료(§3.3) 직후 그리고 재기동 직후, **양쪽 Config Scope를 모두** 동기화해야 한다 (MUST). 초기 동기화 시 `config_version`은 `1`로 시작한다. 서버는 양쪽 스코프의 초기 동기화가 완료되기 전까지 해당 클라이언트에 대한 세션 초기화(§5.1) 요청을 보류해야 한다 (MUST). 초기 동기화 미완료 상태에서 세션 초기화가 시도되면 서버는 `503 Service Unavailable`을 반환해야 한다 (MUST). 23 23
24 24
마스터 서버가 특정 스코프에 대한 설정을 보유하지 않은 상태에서 해당 클라이언트의 헬스 체크(§4.2) 또는 에이전트 탐색(§4.3)을 수행하는 것은 허용된다. 설정 동기화 보류 규칙은 세션 초기화에만 적용된다. 25 25
26
### 4.1.3. 런타임 설정 갱신 (Runtime Configuration Update) 26
#### 4.1.3. 런타임 설정 갱신 (Runtime Configuration Update) 27 27
28 28
클라이언트는 런타임 중 설정 변경이 발생할 때마다 해당 스코프의 엔드포인트를 호출하여 서버에 변경 사항을 동기화해야 한다 (MUST). 각 호출은 해당 스코프의 **전체 필드를 포함하는 완전한 스냅샷**이어야 하며, 부분 필드만 포함하는 것은 허용되지 않는다 (MUST NOT). 서버는 수신한 스냅샷으로 해당 스코프의 기존 상태를 전체 교체한다. 29 29
30
### 4.1.4. 설정 버전 관리 및 순서 보장 (Config Versioning) 30
#### 4.1.4. 설정 버전 관리 및 순서 보장 (Config Versioning) 31 31
32 32
모든 Config Scope 요청에는 `config_version` 필드가 포함되어야 한다 (MUST). 이 값은 각 스코프별로 독립적이며, 클라이언트가 해당 스코프의 설정을 변경할 때마다 반드시 1씩 단조 증가시켜야 한다 (MUST). 33 33
34 34
**순서 보장 규칙**: 마스터 서버는 수신한 `config_version`이 현재 보유한 해당 스코프의 버전보다 큰 경우에만 설정을 갱신해야 한다 (MUST). 현재 보유 버전 이하의 값이 수신되면 네트워크 지연에 의한 역전(stale update)으로 간주하고, `409 Conflict`를 반환하여 거부해야 한다 (MUST). 이때 응답 바디에는 서버가 현재 보유 중인 `config_version`을 포함하여 클라이언트가 재동기화할 수 있도록 해야 한다 (MUST). 35 35
36 36
**클라이언트 재기동 시 버전 처리**: 클라이언트가 재기동될 때 이전 `config_version`을 영속적으로 보관하지 못하는 경우를 위해, 다음의 특수 규칙을 적용한다: `config_version`이 `1`인 요청은 서버가 보유한 버전과 무관하게 항상 수락해야 한다 (MUST). 이는 재기동 후 초기 동기화를 보장하기 위한 것이며, 서버는 `config_version: 1` 수신 시 내부 버전 카운터를 `1`로 리셋해야 한다. 단, 활성 세션이 존재하는 상태에서 `config_version: 1`이 수신되면, 서버는 이를 클라이언트 재기동 시그널로 해석하여 시스템 로그에 기록해야 한다 (MUST). 37 37
38
### 4.1.5. Resource Limits 동기화 38
#### 4.1.5. Resource Limits 동기화 39 39
40 40
클라이언트의 수량적 운영 제약을 서버에 동기화한다. 41 41
42 42
**Endpoint**: `PUT /v1/nodes/config/limits` (Master Server 제공) 43 43
44 44
**Request** (Client → Master): 45 45
46 46
```json 47 47
{ 48 48
"config_version": "Number (필수, 이 스코프의 단조 증가 정수, 초기값 1)", 49 49
"effective_at": "String (필수, ISO 8601, 클라이언트가 이 설정을 적용한 시각)", 50 50
"reason": "String (선택, 변경 사유. 예: 'memory_pressure', 'scheduled_maintenance')", 51 51
"limits": { 52 52
"rate_limit": "Number (필수, 초당 처리 가능한 최대 HTTP 요청 수. 양의 정수)", 53 53
"max_frame_size": "Number (필수, WSS 단일 프레임 최대 바이트. 최소 1024)", 54 54
"reattach_window": "Number (필수, WSS 단절 시 DETACHED 상태 유지 시간 초. 0 이상 정수. 0이면 DETACHED 미지원으로, 단절 시 즉시 TERMINATED 전이)", 55 55
"max_buffer_size": "Number (필수, 최대 메모리 버퍼 바이트. 양의 정수. max_frame_size 이상이어야 함)", 56 56
"buffer_overflow_policy": "String (필수, 'RING' 또는 'DROP')" 57 57
} 58 58
} 59 59
``` 60 60
61 61
**서버 검증 규칙 (MUST)**: 62 62
63 63
| 필드 | 검증 조건 | 위반 시 에러 | 64 64
| ------------------------------- | ----------------------------------------------------------------------------------- | --------------------------------- | 65 65
| `config_version` | 양의 정수. 현재 보유 버전보다 큰 값이거나, 정확히 `1` (재기동) | `409 Conflict` (버전 역전) | 66 66
| `effective_at` | 유효한 ISO 8601 형식. 미래 시각 불허 (서버 현재 시각 + 30초 허용 오차 초과 시 거부) | `422` `EFFECTIVE_AT_IN_FUTURE` | 67 67
| `limits.rate_limit` | 양의 정수 (> 0). 소수점 불허 | `422` `INVALID_RATE_LIMIT` | 68 68
| `limits.max_frame_size` | 양의 정수. 최소 `1024` | `422` `FRAME_SIZE_TOO_SMALL` | 69 69
| `limits.reattach_window` | 0 이상 정수 | `422` `INVALID_REATTACH_WINDOW` | 70 70
| `limits.max_buffer_size` | 양의 정수. `max_frame_size` 이상이어야 함 | `422` `BUFFER_SMALLER_THAN_FRAME` | 71 71
| `limits.buffer_overflow_policy` | `"RING"` 또는 `"DROP"` 중 하나 | `422` `INVALID_OVERFLOW_POLICY` | 72 72
| 모든 필드 존재 여부 | `limits` 객체 내 모든 필드가 존재해야 함 (전체 교체) | `400` `CONFIG_FIELD_MISSING` | 73 73
74 74
**Response (200 OK):** 75 75
76 76
```json 77 77
{ 78 78
"config_version": "Number (수신한 버전 에코백)", 79 79
"accepted_at": "String (ISO 8601, 서버가 설정을 수락하고 저장한 시각)", 80 80
"active_sessions_affected": "Number (현재 RUNNING 또는 DETACHED 상태의 세션 수. 이 세션들은 이전 설정으로 계속 동작함. §4.1.8 참조)" 81 81
} 82 82
``` 83 83
84
### 4.1.6. Capabilities 동기화 84
#### 4.1.6. Capabilities 동기화 85 85
86 86
클라이언트가 지원하는 기능 목록을 서버에 동기화한다. 87 87
88 88
**Endpoint**: `PUT /v1/nodes/config/capabilities` (Master Server 제공) 89 89
90 90
**Request** (Client → Master): 91 91
92 92
```json 93 93
{ 94 94
"config_version": "Number (필수, 이 스코프의 단조 증가 정수, 초기값 1)", 95 95
"effective_at": "String (필수, ISO 8601, 클라이언트가 이 설정을 적용한 시각)", 96 96
"reason": "String (선택, 변경 사유. 예: 'plugin_loaded: vision_v2', 'model_replaced: gpt4o')", 97 97
"capabilities": [ 98 98
"String (지원 기능 식별자 배열. 예: 'vision_v1', 'tool_call_v2'. 빈 배열 허용)" 99 99
] 100 100
} 101 101
``` 102 102
103 103
**서버 검증 규칙 (MUST)**: 104 104
105 105
| 필드 | 검증 조건 | 위반 시 에러 | 106 106
| ----------------- | ------------------------------------------------------------------------------------------ | ------------------------------------- | 107 107
| `config_version` | §4.1.5의 `config_version` 검증과 동일 | `409 Conflict` (버전 역전) | 108 108
| `effective_at` | §4.1.5의 `effective_at` 검증과 동일 | `422` `EFFECTIVE_AT_IN_FUTURE` | 109 109
| `capabilities` | JSON 배열이어야 함. 빈 배열 허용 | `422` `INVALID_CAPABILITIES_FORMAT` | 110 110
| `capabilities[*]` | 각 항목은 비어 있지 않은 문자열. 최대 128자. 허용 문자: `a-z`, `A-Z`, `0-9`, `_`, `.`, `-` | `422` `INVALID_CAPABILITY_IDENTIFIER` | 111 111
| 중복 검사 | 배열 내 동일한 값이 2회 이상 출현하면 거부 | `422` `DUPLICATE_CAPABILITY` | 112 112
113 113
**서버의 미인식 Capability 처리**: 서버는 자신이 인식하지 못하는 capability 식별자를 수신하더라도, 형식 검증을 통과하면 거부 없이 저장해야 한다 (MUST). 이는 §1.3의 필드 무시 원칙(Postel's Law)과 일관되며, 클라이언트가 서버보다 먼저 새로운 기능을 탑재하는 시나리오를 허용하기 위함이다. 114 114
115 115
**Response (200 OK):** 116 116
117 117
```json 118 118
{ 119 119
"config_version": "Number (수신한 버전 에코백)", 120 120
"accepted_at": "String (ISO 8601, 서버가 설정을 수락하고 저장한 시각)", 121 121
"active_sessions_affected": "Number (현재 RUNNING 또는 DETACHED 상태의 세션 수. 이 세션들은 이전 capabilities로 계속 동작함. §4.1.8 참조)" 122 122
} 123 123
``` 124 124
125
### 4.1.7. 설정 갱신 에러 응답 (Config Update Errors) 125
#### 4.1.7. 설정 갱신 에러 응답 (Config Update Errors) 126 126
127 127
설정 갱신 요청이 검증에 실패한 경우, 서버는 §7.1의 에러 응답 표준 규격을 확장한 다음 포맷으로 응답해야 한다 (MUST). 128 128
129 129
**검증 실패 (422 Unprocessable Entity):** 130 130
131 131
```json 132 132
{ 133 133
"error_code": "String (에러 코드. 예: 'CONFIG_VALIDATION_FAILED')", 134 134
"message": "String (휴먼 리더블 디버깅 메시지)", 135 135
"field_errors": [ 136 136
{ 137 137
"field": "String (문제가 된 필드의 JSON Path. 예: 'limits.rate_limit', 'capabilities[2]')", 138 138
"constraint": "String (위반된 제약 조건의 식별자. 예: 'POSITIVE_INTEGER', 'MIN_VALUE:1024', 'ENUM:RING|DROP')", 139 139
"reason": "String (휴먼 리더블 설명. 예: 'MUST be a positive integer')" 140 140
} 141 141
] 142 142
} 143 143
``` 144 144
145 145
`field_errors` 배열에는 검증에 실패한 **모든** 필드가 포함되어야 한다 (MUST). 첫 번째 위반에서 중단하고 나머지를 생략해서는 안 된다. 이는 클라이언트가 한 번의 응답으로 모든 위반 사항을 파악하고 수정할 수 있도록 하기 위함이다. 146 146
147 147
**버전 충돌 (409 Conflict):** 148 148
149 149
```json 150 150
{ 151 151
"error_code": "CONFIG_VERSION_CONFLICT", 152 152
"message": "String (휴먼 리더블 디버깅 메시지)", 153 153
"current_config_version": "Number (서버가 현재 보유 중인 해당 스코프의 config_version)" 154 154
} 155 155
``` 156 156
157 157
`current_config_version`을 통해 클라이언트는 자신의 `config_version`을 서버 값 이상으로 재조정한 후 재시도할 수 있다. 158 158
159
### 4.1.8. 설정 적용 범위: Session-Pinned Config 159
#### 4.1.8. 설정 적용 범위: Session-Pinned Config 160 160
161 161
런타임 설정 갱신은 **새로 초기화되는 세션에만 적용**되며, 이미 `RUNNING` 또는 `DETACHED` 상태인 세션에는 소급 적용되지 않는다 (MUST NOT). 이를 Session-Pinned Config라 한다. 162 162
163 163
**바인딩 규칙:** 164 164
165 165
- **신규 세션** (`reattach: false`): 마스터 서버가 `POST /v1/session/init` (§5.1)을 호출하는 시점에 보유한 각 스코프의 최신 설정 스냅샷을 해당 세션에 바인딩한다. 바인딩된 설정은 세션이 `TERMINATED` 상태로 전이될 때까지 불변이다 (MUST). 166 166
- **세션 재연결** (`reattach: true`): 원래 세션에 바인딩된 설정을 그대로 유지한다. 재연결 시점의 최신 설정으로 교체하지 않는다 (MUST NOT). 167 167
168 168
**서버 추적 의무**: 마스터 서버는 각 세션에 바인딩된 `config_version` 쌍(limits 스코프 버전 + capabilities 스코프 버전)을 세션 메타데이터로 보관해야 한다 (MUST). 이 정보는 세션의 행동을 예측하고 디버깅하는 데 필요하다. 169 169
170 170
**활성 세션에 대한 서버의 행동 원칙**: 설정 갱신 후에도 활성 세션은 바인딩된 이전 설정에 따라 동작하므로, 서버는 다음을 준수해야 한다 (MUST): 171 171
172 172
- 활성 세션에 대한 스로틀링은 해당 세션에 바인딩된 `rate_limit` 값을 기준으로 적용한다. 갱신된 값이 아니다. 173 173
- 활성 세션에 대한 기능 요구는 해당 세션에 바인딩된 `capabilities` 목록 내에서만 허용한다. 갱신 후 추가된 capability를 기존 세션에 요구해서는 안 된다. 174 174
- 갱신 후 제거된 capability가 활성 세션에 바인딩되어 있는 경우, 해당 세션은 정상적으로 계속 동작한다. 서버가 해당 세션을 강제 종료하거나 기능을 제한해서는 안 된다 (MUST NOT). 175 175
176
### 4.1.9. 레거시 호환: POST /v1/nodes/config (Deprecated) 177
178
> **본 엔드포인트는 단종 예고(Deprecated) 상태이다.** 새로운 클라이언트 구현은 §4.1.5 및 §4.1.6의 분리된 PUT 엔드포인트를 사용해야 한다 (MUST). 179
180
**Endpoint**: `POST /v1/nodes/config` (Master Server 제공) 181
182
기존 클라이언트와의 하위 호환을 위해 서버는 이 엔드포인트를 계속 수락할 수 있다 (MAY). 수신된 요청은 서버 내부에서 limits와 capabilities 스코프로 분리 저장한다. 이 경우 `config_version`은 양쪽 스코프 모두 `1`로 초기화한다. 183
184
서버는 이 엔드포인트로의 응답에 반드시 `Warning: 299 - "Deprecated: Use PUT /v1/nodes/config/limits and PUT /v1/nodes/config/capabilities"` HTTP 헤더를 포함해야 한다 (MUST). 185
186
**Request** (Client → Master): 176
#### 4.1.9. 레거시 호환: POST /v1/nodes/config (지원 종료) 187 177
188
```json 189
{ 190
"rate_limit": "Number (필수, 초당 처리 가능한 최대 HTTP 요청 수)", 191
"max_frame_size": "Number (필수, WSS 단일 프레임 최대 바이트)", 192
"reattach_window": "Number (필수, WSS 단절 시 대기 시간 초)", 193
"max_buffer_size": "Number (필수, 최대 메모리 버퍼 바이트)", 194
"buffer_overflow_policy": "String (필수, 'RING' 또는 'DROP')", 195
"capabilities": ["String (선택, e.g., 'vision_v1', 'tool_call_v2')"] 196
} 197
``` 178
`POST /v1/nodes/config` 엔드포인트는 지원이 종료되었다. 모든 클라이언트는 §4.1.5 및 §4.1.6의 분리된 PUT 엔드포인트를 사용해야 한다 (MUST). 198 179
199 180
### 4.2. 헬스 체크 (Health Check) 200 181
201 182
**Endpoint**: `GET /v1/health` (Local Client 제공) 202 183
203 184
**Response**: 204 185
205 186
```json 206 187
{ 207 188
"status": "String (필수, 'online', 'busy', 'error')", 208 189
"uptime": "Number (필수, 클라이언트 가동 초)", 209 190
"active_sessions": "Number (선택, 현재 유지 중인 세션 수)" 210 191
} 211 192
``` 212 193
213 194
**상태 전이 규칙**: 활성 세션이 0개면 즉시 `online`. 1개 이상 존재 시 `busy`. 세션이 0이 되면 즉시 `online`으로 복귀해야 한다 (MUST). 214 195
215
### 4.3. 에이전트 탐색 (Agent Discovery) 196
### 4.3. 에이전트 탐색 및 관리 (Agent Discovery & Management) 216 197
217
클라이언트 내 실행 가능한 에이전트 목록을 조회한다. 198
클라이언트 내 실행 가능한 에이전트 목록 조회 및 관리를 제공한다. 218 199
219 200
**Endpoint**: `GET /v1/agents` (Local Client 제공) 220 201
221 202
**Response**: 222 203
223 204
```json 224 205
{ 225 206
"agents": [ 226 207
{ 227
"id": "String (고유 식별자)", 228
"name": "String (세션 식별자로 사용될 이름)", 229
"is_active": "Boolean (에이전트 활성화 상태)" 208
"id": "String (필수, 고유 식별자)", 209
"name": "String (필수, 세션 식별자로 사용될 이름)", 210
"is_active": "Boolean (필수, 에이전트 활성화 상태)", 211
"type": "String (필수, 'builtin' | 'custom')", 212
"adapter_type": "String (필수, 'process' | 'sdk'. RAWP-DPS 1.0.1 §17.1 참조)", 213
"supported_dps_versions": [ 214
"String (필수, 지원하는 DPS 프로토콜 버전. 예: 'rawp-dps-1.0')" 215
], 216
"models": [ 217
{ 218
"id": "String (필수, 모델 식별자)", 219
"name": "String (필수, 표시명)", 220
"is_default": "Boolean (필수)" 221
} 222
], 223
"options": [ 224
{ 225
"name": "String (필수, 옵션 이름)", 226
"type": "String (필수, 'string' | 'boolean' | 'number' | 'enum')", 227
"required": "Boolean (필수)", 228
"default": "Any (선택, 기본값)", 229
"enum_values": ["String (선택, type이 'enum'일 때 허용 값 목록)"], 230
"description": "String (선택, 옵션 설명)" 231
} 232
], 233
"single_turn_process": "Boolean (선택, 기본값 false. RAWP-DPS 1.0.1 §18 참조)", 234
"description": "String (선택, 에이전트 설명)" 230 235
} 231 236
] 232 237
} 233 238
``` 234 239
240
`type: "builtin"`은 클라이언트에 사전 탑재된 에이전트, `type: "custom"`은 사용자가 등록한 커스텀 에이전트를 의미한다. `models` 배열이 비어 있으면 에이전트가 모델 선택을 지원하지 않음을 의미한다. 241
242
#### 4.3.1. 에이전트 상세 조회 243
244
**Endpoint**: `GET /v1/agents/{agent_id}` (Local Client 제공) 245
246
**Response (200 OK)**: §4.3의 `agents` 배열 내 단일 에이전트 객체와 동일한 스키마. 247
248
**에러**: 존재하지 않는 `agent_id` 시 `404 Not Found`. 249
250
#### 4.3.2. 커스텀 에이전트 등록 251
252
**Endpoint**: `POST /v1/agents` (Local Client 제공) 253
254
**Request**: 255
256
```json 257
{ 258
"name": "String (필수, 최대 100자)", 259
"adapter_type": "String (필수, 'process' | 'sdk')", 260
"command": "String (adapter_type이 'process'일 때 필수, 실행 명령어)", 261
"supported_dps_versions": ["String (필수)"], 262
"models": ["Object (선택, §4.3의 모델 스키마)"], 263
"options": ["Object (선택, §4.3의 옵션 스키마)"], 264
"single_turn_process": "Boolean (선택)", 265
"resume_flag": "String (선택, single_turn_process: true일 때. RAWP-DPS 1.0.1 §18.1 참조)", 266
"description": "String (선택)" 267
} 268
``` 269
270
**Response (201 Created)**: 생성된 에이전트 객체 (`type: "custom"`, `is_active: true`). 271
272
**제약 조건**: `name`이 기존 에이전트와 중복되면 `409 Conflict`를 반환한다 (MUST). 273
274
#### 4.3.3. 커스텀 에이전트 수정 275
276
**Endpoint**: `PATCH /v1/agents/{agent_id}` (Local Client 제공) 277
278
**Request**: §4.3.2의 필드 중 변경할 필드만 포함. `type: "builtin"` 에이전트는 수정할 수 없다 (MUST NOT). 시도 시 `403 Forbidden`. 279
280
**Response (200 OK)**: 수정된 에이전트 객체. 281
282
#### 4.3.4. 커스텀 에이전트 삭제 283
284
**Endpoint**: `DELETE /v1/agents/{agent_id}` (Local Client 제공) 285
286
**제약 조건**: `type: "builtin"` 에이전트는 삭제할 수 없다 (MUST NOT). 시도 시 `403 Forbidden`. 해당 에이전트에 활성 세션이 존재하면 `409 Conflict`를 반환한다 (MUST). 287
288
**Response**: `204 No Content`. 289
290
#### 4.3.5. 에이전트 활성화/비활성화 291
292
**Endpoint**: `PATCH /v1/agents/{agent_id}/status` (Local Client 제공) 293
294
**Request**: `{"is_active": "Boolean (필수)"}` 295
296
**Response (200 OK)**: `{"id": "String", "is_active": "Boolean"}` 297
298
비활성화된 에이전트(`is_active: false`)는 `GET /v1/agents` 목록에 포함되지만, 세션 초기화(§5.1)에서 해당 `agent_name` 사용이 거부된다 (MUST). 거부 시 `400 Bad Request` (error_code: `"AGENT_INACTIVE"`). 299
235 300
### 4.4. 파일 탐색 (Directory Browsing) 236 301
237 302
인가된 작업 경로 내의 디렉토리를 조회한다. 238 303
239 304
**Endpoint**: `POST /v1/fs/list` (Local Client 제공) 240 305
241 306
**Request**: `{"target_path": "String (필수, 조회할 절대 경로)"}` 242 307
243
> **변경 사항**: 실시간 퍼지 파일 검색은 RAWP-DPS 1.0 §16의 WSS 이벤트(`control.file.search` / `session.file.candidates`)를 통해 제공된다. 본 엔드포인트는 디렉토리 단위 일괄 조회용으로 유지한다. 244
245 308
--- 수정됨
5. Phase 3: 세션 라이프사이클 및 연결 (Session & I/O)
추가된 줄 48 삭제된 줄 2
1 1
## 5. Phase 3: 세션 라이프사이클 및 연결 (Session & I/O) 2 2
3 3
**세션 생명주기**: `INIT` → `RUNNING` → `DETACHED`(소켓 단절 시) → `TERMINATED` 4 4
5 5
### 5.1. 세션 초기화 및 재연결 (Session Initialization) 6 6
7 7
신규 에이전트 실행 또는 DETACHED 상태 프로세스에 WSS 바인딩을 위한 티켓을 발급한다. 8 8
9 9
**Endpoint**: `POST /v1/session/init` (Local Client 제공) 10 10
11 11
**Request**: 12 12
13 13
```json 14 14
{ 15 15
"session_id": "String (필수, UUID v4)", 16 16
"agent_name": "String (reattach: false 시 필수)", 17 17
"workspace_path": "String (reattach: false 시 필수)", 18 18
"ticket": "String (필수, WSS 연결 인증용 난수 티켓)", 19 19
"reattach": "Boolean (선택, 기본값 false. true 시 기존 구동 중 프로세스 바인딩)", 20 20
"last_sync_timestamp": "String (선택, ISO 8601)", 21 21
"last_message_id": "String (선택, UUID)" 22 22
} 23 23
``` 24 24
25 25
**제약 조건**: `reattach: true`이나 해당 `session_id`가 존재하지 않거나 종료된 경우 `404 Not Found` 반환 (MUST). 26 26
27 27
**Config 바인딩 제약 조건**: 마스터 서버는 `reattach: false`로 세션을 초기화할 때, §4.1.8에 따라 양쪽 Config Scope(limits, capabilities)의 최신 스냅샷을 해당 세션에 바인딩해야 한다 (MUST). 양쪽 스코프의 초기 동기화(§4.1.2)가 완료되지 않은 클라이언트에 대해서는 세션 초기화를 시도해서는 안 된다 (MUST NOT). 28 28
29 29
### 5.2. 세션 명시적 종료 (Session Termination) 30 30
31 31
**Endpoint**: `DELETE /v1/session/{session_id}` (Local Client 제공) 32 32
33 33
**제약 조건**: 클라이언트는 자식 프로세스에 `SIGTERM`을 전송하고, 5초 내 종료되지 않으면 `SIGKILL`로 강제 종료 후 메모리 버퍼 및 소켓을 파기해야 한다 (MUST). 34 34
35
**WSS 알림 의무**: 세션 종료 처리 후 WSS 연결을 닫기 전에, 해당 세션에 바인딩된 모든 WSS Connection에 RAWP-DPS 1.0.1 §7.7의 `session.deleted` 이벤트를 발송해야 한다 (MUST). 발송 순서는 RAWP-DPS 1.0.1 §7.5.3(진행 중 항목 정리) → `session.deleted` → WSS 종료이다. 36
35 37
### 5.3. WebSocket 연결 수립 (WebSocket Upgrade) 36 38
37 39
**Connection URI**: `wss://{client_address}/v1/ws/stream?ticket={ticket}&session_id={session_id}` 38 40
39 41
**인가 절차** (MUST): 40 42
41
1. 클라이언트는 HTTP Upgrade 요청 수신 시 HTTP 헤더의 `Sec-WebSocket-Protocol`을 확인해야 한다. RAWP-DPS 1.0 적용 시 `rawp-dps-1.0`을 우선 수락하고, 미지원 시 `rawp-1.0`으로 폴백한다. 양측 모두 미지원 시 `426` 또는 `400`을 반환해야 한다. 43
1. 클라이언트는 HTTP Upgrade 요청 수신 시 HTTP 헤더의 `Sec-WebSocket-Protocol`을 확인해야 한다. RAWP-DPS 1.0.1 적용 시 `rawp-dps-1.0`을 우선 수락하고, 미지원 시 `rawp-1.0`으로 폴백한다. 양측 모두 미지원 시 `426` 또는 `400`을 반환해야 한다. 42 44
2. Query Parameter의 `ticket`과 `session_id`를 검증하고, 만료되거나 불일치 시 `401 Unauthorized`를 반환 후 연결을 거절한다. 43 45
3. 사용된 티켓은 1회 사용 시 즉시 무효화(Burn)해야 한다. 44 46
45
> **변경 사항**: RAWP-DPS 1.0 적용에 따라 `Sec-WebSocket-Protocol` 협상 절차가 확장되었다. `rawp-dps-1.0`이 우선이며, 레거시 호환을 위해 `rawp-1.0`도 수락 가능하다. 자세한 프로토콜 협상 규칙은 RAWP-DPS 1.0 §1.3을 참조한다. 47
### 5.4. 세션 이름 변경 (Session Rename) 46 48
49
세션에 사용자 친화적 이름을 부여하고, 변경 사항을 모든 참여자에게 전파한다. 50
51
#### 5.4.1. 마스터 → 클라이언트 이름 변경 52
53
**Endpoint**: `PATCH /v1/session/{session_id}` (Local Client 제공) 54
55
**Request**: 56
57
```json 58
{ 59
"name": "String (필수, 새 세션 이름. 최대 200자)" 60
} 61
``` 62
63
**Response (200 OK)**: 64
65
```json 66
{ 67
"session_id": "String (필수)", 68
"name": "String (필수, 변경된 이름)", 69
"updated_at": "String (필수, ISO 8601)" 70
} 71
``` 72
73
**제약 조건**: 존재하지 않거나 `TERMINATED` 상태인 세션에 대한 요청은 `404 Not Found`를 반환한다 (MUST). 74
75
**WSS 알림 의무**: HTTP 응답 후, 클라이언트는 해당 세션에 바인딩된 모든 WSS Connection에 RAWP-DPS 1.0.1 §7.6의 `session.renamed` 이벤트를 발송해야 한다 (MUST). 이는 로컬 UI WSS(§10.6)와 마스터 WSS를 모두 포함한다. 76
77
#### 5.4.2. 엣지 → 마스터 이름 변경 78
79
**Endpoint**: `PATCH /v1/edge/sessions/{session_id}` (Master Server 제공) 80
81
**Request/Response**: §5.4.1과 동일한 스키마. 82
83
마스터는 이름 변경 수신 시 대상 로컬 클라이언트의 `PATCH /v1/session/{session_id}`를 호출하여 전파하고, 해당 세션에 연결된 모든 엣지 WSS에 RAWP-DPS 1.0.1 §7.6의 `session.renamed` 이벤트를 브로드캐스트해야 한다 (MUST). 84
85
#### 5.4.3. 로컬 세션 이름 변경 86
87
**Endpoint**: `PATCH /local/v1/sessions/{session_id}` (Local Client 제공) 88
89
**Request/Response**: §5.4.1과 동일한 스키마. 90
91
HTTP 응답 후, 클라이언트는 해당 세션에 바인딩된 모든 WSS Connection에 `session.renamed` 이벤트를 발송해야 한다 (MUST). 92
47 93
--- 수정됨
6. 데이터 평면 스트리밍 규격 (Data Plane WSS Protocol)
추가된 줄 3 삭제된 줄 5
1 1
## 6. 데이터 평면 스트리밍 규격 (Data Plane WSS Protocol) 2 2
3 3
> **본 절은 별도 규격으로 분리되었다.** 4 4
> 5
> WSS 연결 수립(§5.3) 이후 교환되는 데이터 프레임의 Envelope 구조, 이벤트 타입 및 페이로드 정의는 **RAWP-DPS 1.0** 규격을 따른다. 5
> WSS 연결 수립(§5.3) 이후 교환되는 데이터 프레임의 Envelope 구조, 이벤트 타입 및 페이로드 정의는 **RAWP-DPS 1.0.1** 규격을 따른다. 6 6
> 7
> RAWP-DPS 1.0은 다음을 정의한다: 7
> RAWP-DPS 1.0.1은 다음을 정의한다: 8 8
> 9 9
> - **Envelope 구조**: 버전 필드(`v`), 네임스페이스 기반 이벤트 타입, Turn 추적(`turn_id`), 요청-응답 상관(`parent_id`)을 포함하는 확장된 프레임 구조 (RAWP-DPS §2) 10 10
> - **에이전트 출력** (`agent.*`): 텍스트 스트리밍, 사고(Thinking) 스트리밍, 상호작용 요청, 에러 보고, 파일 전송, 상태 변경 (RAWP-DPS §4) 11 11
> - **도구 호출** (`tool.*`): 도구 호출 요청/결과/실시간 스트리밍, 도구 카탈로그 고지 (RAWP-DPS §5) 12 12
> - **제어 명령** (`control.*`): 프롬프트 전달, 프롬프트 취소, 상호작용 응답, 컨텍스트 압축 요청, 동작 모드 전환 (RAWP-DPS §6) 13 13
> - **세션 이벤트** (`session.*`): 이력 복구, 컨텍스트 압축 보고, 사용량 지표, Turn 라이프사이클, 능력 협상 (RAWP-DPS §7, §12) 14 14
> - **태스크 관리** (RAWP-DPS §8), **계획 문서** (RAWP-DPS §9), **서브에이전트 위임** (RAWP-DPS §10), **구조화 출력** (RAWP-DPS §11) 15 15
> - **파일 참조** (RAWP-DPS §16): 인라인 파일 참조 토큰 포맷, 실시간 퍼지 파일 검색, 토큰 이스케이프 규칙, 어댑터별 변환 규칙 16 16
> 17
> 구현자는 RAWP-DPS 1.0 규격 문서를 참조하여 데이터 평면을 구현해야 한다 (MUST). 18
> 19
> **레거시 호환**: RAWP-DPS-0.1-Legacy만 지원하는 피어와의 통신이 필요한 경우, RAWP-DPS 1.0 §14의 하위 호환성 가이드라인 및 브릿지 변환 규칙을 따른다. 17
> 구현자는 RAWP-DPS 1.0.1 규격 문서를 참조하여 데이터 평면을 구현해야 한다 (MUST). 20 18
21 19
--- 수정됨
7. 보안 및 예외 처리 (Security & Errors)
추가된 줄 21 삭제된 줄 1
1 1
## 7. 보안 및 예외 처리 (Security & Errors) 2 2
3 3
### 7.1. HTTP 에러 응답 표준 규격 4 4
5 5
클라이언트나 서버가 HTTP 오류를 반환할 때, 다음 JSON 포맷을 Body에 포함해야 한다 (MUST). 6 6
7 7
```json 8 8
{ 9 9
"error_code": "String (e.g., TOKEN_EXPIRED, SESSION_NOT_FOUND, INVALID_PATH)", 10 10
"message": "String (휴먼 리더블 디버깅 메시지)" 11 11
} 12 12
``` 13 13
14 14
**상태 코드 매핑 규칙**: 15 15
16 16
| HTTP 상태 코드 | 사유 | 17 17
| --------------------------- | --------------------------------------------------------------------------------------------------------------- | 18 18
| `400 Bad Request` | 필수 파라미터 누락, 잘못된 UUID 포맷. | 19 19
| `401 Unauthorized` | 토큰 만료, 서명 불일치, WSS Upgrade 티켓 무효. | 20 20
| `403 Forbidden` | White-list 없는 명령어, 인가되지 않은 디렉토리 접근. | 21 21
| `404 Not Found` | 존재하지 않는 `session_id` 제어 시도. | 22 22
| `409 Conflict` | 이미 `RUNNING` 중인 세션에 `reattach: false`로 초기화 시도. Config Scope의 `config_version` 역전 감지 (§4.1.4). | 23 23
| `422 Unprocessable Entity` | 설정 갱신 시 필드값 형식, 범위, 상호 정합성 위반 (§4.1.7). | 24 24
| `429 Too Many Requests` | `rate_limit` 초과. | 25 25
| `500 Internal Server Error` | 내부 파일 시스템 오류, 바인딩 충돌 등. | 26 26
| `503 Service Unavailable` | 초기 설정 동기화 미완료 상태에서 세션 초기화 시도 (§4.1.2). | 27 27
28 28
### 7.2. 보안 제약 및 필수 보호 장치 (MUST) 29 29
30 30
- **경로 검증 (Path Normalization)**: 클라이언트는 모든 파일/디렉토리 조회 요청 시 경로 내 `../` 등을 정규화하고 인가된 `workspace_path` 범위 내인지 반드시 검증해야 한다 (Directory Traversal 방어). 31 31
- **명령어 및 에이전트 보호 (Whitelisting)**: 활성화된 `agent_name`에 매핑된 명령어만 실행 가능해야 하며, 임의의 쉘 스크립트나 바이너리를 원격으로 주입받아 실행하는 시도는 차단해야 한다 (RCE 방어). 32 32
- **버퍼 관리 및 OOM 방어**: WSS 단절 중 자식 프로세스를 유지할 때 발생하는 출력은 버퍼에 저장하되, `max_buffer_size`에 도달하면 반드시 사전에 합의된 `buffer_overflow_policy`(RING/DROP)에 따라 강제로 메모리를 관리하여 게이트웨이 다운을 방지해야 한다. 33
- **도구 호출 보안**: RAWP-DPS 1.0 적용 시, `tool.catalog.publish`에서 고지된 도구만 `tool.invoke.request`에 사용 가능하며, 고지되지 않은 도구의 호출 요청은 클라이언트가 거부해야 한다 (MUST). 자세한 도구 보안 규칙은 RAWP-DPS 1.0 §15를 참조한다. 33
- **도구 호출 보안**: RAWP-DPS 1.0.1 적용 시, `tool.catalog.publish`에서 고지된 도구만 `tool.invoke.request`에 사용 가능하며, 고지되지 않은 도구의 호출 요청은 클라이언트가 거부해야 한다 (MUST). 자세한 도구 보안 규칙은 RAWP-DPS 1.0.1 §15를 참조한다. 34 34
35
### 7.3. Rate Limiting 36
37
**적용 대상**: Rate Limiting은 HTTP 엔드포인트에 대한 요청에 적용된다. WSS 프레임은 Rate Limit 대상이 아니다. WSS는 이미 세션당 단일 연결이므로 별도의 프레임 수준 제한을 두지 않는다. 38
39
**`limits.rate_limit` 필드의 의미**: §4.1.5에서 클라이언트가 보고하는 `limits.rate_limit`은 해당 클라이언트가 초당 처리 가능한 최대 HTTP 요청 수를 의미한다. 마스터 서버는 이 값을 초과하는 빈도로 해당 클라이언트에 HTTP 요청을 보내서는 안 된다 (MUST NOT). 40
41
**429 응답 형식**: Rate Limit을 초과한 요청에 대해 `429 Too Many Requests`를 반환할 때, 다음을 포함해야 한다 (MUST): 42
43
```json 44
{ 45
"error_code": "RATE_LIMITED", 46
"message": "String (휴먼 리더블 디버깅 메시지)", 47
"retry_after": "Number (필수, 초 단위, 다음 요청까지 대기해야 할 최소 시간)" 48
} 49
``` 50
51
`Retry-After` HTTP 헤더에도 동일한 값을 초 단위로 포함해야 한다 (MUST). 52
53
**양방향 적용**: 마스터가 클라이언트의 `rate_limit`을 초과하면 클라이언트가 429를 반환하고, 클라이언트가 마스터의 내부 Rate Limit을 초과하면 마스터가 429를 반환한다. 양측 모두 본 절의 응답 형식을 따른다 (MUST). 54
35 55
--- 수정됨
8. 클라이언트 UI/UX 구현 지침 (Client UI Implementer Notes)
추가된 줄 3 삭제된 줄 3
1 1
## 8. 클라이언트 UI/UX 구현 지침 (Client UI Implementer Notes) 2 2
3 3
> **본 절은 별도 규격으로 분리되었다.** 4 4
> 5
> RAWP 프로토콜의 제어 평면 이벤트와 RAWP-DPS 1.0의 데이터 평면 이벤트를 사용자에게 시각적으로 전달하는 클라이언트 렌더링 규격은 **RAWP-CRS 1.0** 규격을 따른다. 5
> RAWP 프로토콜의 제어 평면 이벤트와 RAWP-DPS 1.0.1의 데이터 평면 이벤트를 사용자에게 시각적으로 전달하는 클라이언트 렌더링 규격은 **RAWP-CRS 1.0.1** 규격을 따른다. 6 6
> 7
> RAWP-CRS 1.0은 다음을 정의한다: 7
> RAWP-CRS 1.0.1은 다음을 정의한다: 8 8
> 9 9
> - **메시지 아키텍처**: User(우측 버블), Agent(좌측 버블리스), System(중앙 정렬) 세 행위자의 시각적 정체성 및 레이아웃 규칙 (RAWP-CRS §2) 10 10
> - **유저 메시지**: 채팅 버블 스타일, 슬래시 명령어 자동 완성 UI, 명령어 실행 버블 렌더링, **파일 참조 입력** (RAWP-CRS §3) 11 11
> - **에이전트 메시지**: 버블리스 디자인 원칙, 마크다운 렌더링 컴포넌트 명세, 점진적 텍스트 스트리밍, 사고 과정 시각화, 도구 호출 결과, Code Diff 뷰어, 에이전트 상태 표시, 태스크 목록 (RAWP-CRS §4) 12 12
> - **시스템 메시지**: 생략 금지 원칙, 드롭다운 패턴, 중지 피드백, 에러/경고 심각도별 시각 차등 (RAWP-CRS §5) 13 13
> - **비스트리밍 환경 적응**: Discord, Slack 등 완성형 플랫폼의 Typing Indicator 활용, 버퍼링, 사고 과정 우회 처리 (RAWP-CRS §6) 14 14
> 15
> 구현자는 RAWP-CRS 1.0 규격 문서를 참조하여 클라이언트 UI를 구현해야 한다 (MUST). 15
> 구현자는 RAWP-CRS 1.0.1 규격 문서를 참조하여 클라이언트 UI를 구현해야 한다 (MUST). 16 16
17 17
--- 수정됨
9. Edge Node API (사용자 접점 통신 규약)
추가된 줄 124 삭제된 줄 17
1 1
## 9. Edge Node API (사용자 접점 통신 규약) 2 2
3 3
본 장은 사용자가 원격지에서 UI(웹 대시보드, 모바일 앱, 챗봇 게이트웨이 등)를 통해 마스터 서버(Master Server)에 접속하여 로컬 클라이언트(Local Client)를 제어하기 위한 Northbound API를 정의한다. 4 4
5 5
**아키텍처 정의 (3-Tier):** 6 6
7 7
``` 8 8
[Edge Node (User UI)] ↔ (Edge API) ↔ [Master Server] ↔ (RAWP/Southbound) ↔ [Local Client] 9 9
``` 10 10
11 11
### 9.1. Edge Node 인증 및 권한 (Authentication) 12 12
13
엣지 노드는 마스터 서버의 로컬 클라이언트 인증 방식(Pairing Token)과 분리된 사용자 단위의 인증 체계(예: OIDC 기반 JWT)를 사용해야 한다. 모든 Edge API 요청은 `Authorization: Bearer {user_access_token}` 헤더를 포함해야 한다 (MUST). 13
엣지 노드는 마스터 서버의 로컬 클라이언트 인증 방식(Pairing Token)과 분리된 **OIDC(OpenID Connect) 기반** 사용자 인증 체계를 사용한다. 세부 로그인 방법(지원 프로바이더, 허용 흐름 등)은 마스터 서버가 Discovery 엔드포인트를 통해 공개하고, 엣지 노드가 이를 조회하여 협상한다. 모든 Edge API 요청은 `Authorization: Bearer {access_token}` 헤더를 포함해야 한다 (MUST). 14 14
15
#### 9.1.1. 인증 구성 Discovery 16
17
엣지 노드는 로그인 UI를 구성하기 전에 마스터 서버의 인증 구성을 조회해야 한다 (MUST). 18
19
**Endpoint**: `GET /v1/edge/auth/configuration` (Master Server 제공, 인증 불필요) 20
21
**Response (200 OK)**: 22
23
```json 24
{ 25
"issuer": "String (필수, OIDC Issuer URL. 예: 'https://auth.example.com')", 26
"authorization_endpoint": "String (필수, OIDC Authorization Endpoint URL)", 27
"token_endpoint": "String (필수, OIDC Token Endpoint URL)", 28
"userinfo_endpoint": "String (선택, OIDC UserInfo Endpoint URL)", 29
"end_session_endpoint": "String (선택, OIDC End Session Endpoint URL)", 30
"supported_flows": [ 31
"String (필수, 지원하는 OIDC 인증 흐름 목록. 아래 열거 값)" 32
], 33
"supported_scopes": [ 34
"String (필수, 허용하는 OIDC Scope 목록. 최소 'openid' 포함)" 35
], 36
"client_id": "String (필수, 엣지 노드가 사용할 OIDC Client ID)", 37
"redirect_uris": ["String (필수, 허용되는 Redirect URI 패턴 목록)"] 38
} 39
``` 40
41
**`supported_flows` 열거 값:** 42
43
| 값 | 설명 | 44
| -------------------------------- | ------------------------------------------------------------------------- | 45
| `authorization_code` | Authorization Code Flow (PKCE 필수). 브라우저 기반 엣지 노드의 기본 흐름. | 46
| `authorization_code_with_device` | Device Authorization Grant (RFC 8628). 브라우저가 없는 환경(TV, CLI 등). | 47
48
마스터 서버는 최소 `authorization_code`를 지원해야 한다 (MUST). 49
50
#### 9.1.2. Authorization Code Flow (PKCE) 51
52
브라우저 기반 엣지 노드의 표준 인증 흐름이다. RFC 7636(PKCE)을 필수 적용한다 (MUST). 53
54
**흐름:** 55
56
1. 엣지 노드가 `code_verifier`(최소 43자 랜덤 문자열)를 생성하고, SHA-256 해시하여 `code_challenge`를 만든다. 57
2. 엣지 노드가 사용자를 `authorization_endpoint`로 리디렉트한다: 58
59
``` 60
{authorization_endpoint}? 61
response_type=code 62
&client_id={client_id} 63
&redirect_uri={redirect_uri} 64
&scope=openid {추가_scope} 65
&state={csrf_state} 66
&code_challenge={code_challenge} 67
&code_challenge_method=S256 68
``` 69
70
3. 사용자가 인증을 완료하면, OIDC 프로바이더가 `redirect_uri`로 `code`와 `state`를 반환한다. 71
4. 엣지 노드가 `state`를 검증한 후, 마스터 서버의 토큰 교환 엔드포인트를 호출한다. 72
73
#### 9.1.3. 토큰 교환 74
75
엣지 노드가 OIDC Authorization Code를 마스터 서버의 Access/Refresh Token으로 교환한다. 마스터 서버는 OIDC 프로바이더에 코드를 검증하고, 자체 토큰을 발급한다. 76
77
**Endpoint**: `POST /v1/edge/auth/token` (Master Server 제공) 78
79
**Request**: 80
81
```json 82
{ 83
"grant_type": "String (필수, 'authorization_code')", 84
"code": "String (필수, OIDC Authorization Code)", 85
"redirect_uri": "String (필수, 인증 요청 시 사용한 redirect_uri와 동일해야 함)", 86
"code_verifier": "String (필수, PKCE code_verifier)" 87
} 88
``` 89
90
**Response (200 OK)**: 91
92
```json 93
{ 94
"access_token": "String (필수, 마스터 서버 발급 Bearer 토큰)", 95
"refresh_token": "String (필수, 갱신용 토큰)", 96
"token_type": "String (필수, 'Bearer')", 97
"expires_in": "Number (필수, access_token 만료까지 초)", 98
"user_id": "String (필수, 인증된 사용자 식별자)", 99
"id_token": "String (선택, OIDC ID Token. 마스터가 전달하는 경우)" 100
} 101
``` 102
103
**에러 응답 (401 Unauthorized)**: 104
105
```json 106
{ 107
"error_code": "String (필수, 'INVALID_CODE' | 'CODE_EXPIRED' | 'INVALID_REDIRECT_URI' | 'PKCE_MISMATCH')", 108
"message": "String (필수, 휴먼 리더블 에러 메시지)" 109
} 110
``` 111
112
#### 9.1.4. 토큰 갱신 및 폐기 113
114
**토큰 갱신**: §3.4.4의 `POST /v1/edge/auth/refresh`를 사용한다. 115
116
**토큰 폐기 (로그아웃)**: §3.4.4의 `POST /v1/edge/auth/revoke`를 사용한다. OIDC `end_session_endpoint`가 Discovery에 포함된 경우, 엣지 노드는 마스터 토큰 폐기와 함께 OIDC 세션도 종료해야 한다 (SHOULD). 117
118
§3.4.3의 Refresh Token Rotation 및 Replay 감지 규칙이 Edge 인증에도 동일하게 적용된다 (MUST). 119
15 120
### 9.2. 로컬 머신(Node) 풀 관리 및 상태 모니터링 16 121
17 122
사용자 계정에 바인딩된 로컬 클라이언트 목록과 현재 상태를 조회한다. 18 123
19
### 9.2.1. 연결된 노드 목록 조회 124
#### 9.2.1. 연결된 노드 목록 조회 20 125
21 126
**Endpoint:** `GET /v1/edge/nodes` 22 127
23 128
**Response (200 OK):** 24 129
25 130
```json 26 131
{ 27 132
"nodes": [ 28 133
{ 29 134
"node_id": "String (필수, 클라이언트 식별자)", 30 135
"device_name": "String (필수, 등록 시 제공된 이름)", 31 136
"status": "String (필수, 'online' | 'busy' | 'offline')", 32 137
"last_seen": "String (필수, ISO 8601, 마스터 서버와 마지막 통신 시각)", 33 138
"capabilities": [ 34 139
"String (선택, 로컬 머신이 지원하는 기능 목록. §4.1.6에서 동기화된 최신 값 반영)" 35 140
], 36 141
"active_sessions_count": "Number (필수)", 37 142
"config_versions": { 38 143
"limits": "Number (선택, 현재 보유 중인 Resource Limits config_version)", 39 144
"capabilities": "Number (선택, 현재 보유 중인 Capabilities config_version)" 40 145
} 41 146
} 42 147
] 43 148
} 44 149
``` 45 150
46
> **변경 사항**: `capabilities` 필드는 §4.1.6을 통해 동기화된 최신 값을 반영한다. `config_versions` 객체가 추가되어 각 Config Scope의 현재 버전을 엣지 노드에서 조회할 수 있다. 47
48
### 9.2.2. 로컬 머신별 사용량 및 지표 (Metrics) 151
#### 9.2.2. 로컬 머신별 사용량 및 지표 (Metrics) 49 152
50 153
해당 노드에서 발생한 세션들의 누적/실시간 사용량을 조회한다. 51 154
52 155
**Endpoint:** `GET /v1/edge/nodes/{node_id}/metrics` 53 156
54 157
**Query Parameters:** `?period=today` (선택, `'today'`, `'week'`, `'month'`) 55 158
56 159
**Response (200 OK):** 57 160
58 161
```json 59 162
{ 60 163
"node_id": "String (필수)", 61 164
"period": "String (필수)", 62 165
"aggregated_usage": { 63 166
"total_input_tokens": "Number", 64 167
"total_output_tokens": "Number", 65 168
"estimated_cost": "Number (USD 기준)", 66 169
"total_tool_invocations": "Number" 67 170
}, 68 171
"health": { 69 172
"uptime_seconds": "Number", 70 173
"last_error": "String (선택, 최근 발생한 치명적 에러)" 71 174
} 72 175
} 73 176
``` 74 177
75
### 9.2.3. 노드 설정 조회 (Node Configuration Lookup) 178
#### 9.2.3. 노드 설정 조회 (Node Configuration Lookup) 76 179
77 180
엣지 노드가 특정 로컬 머신의 현재 설정 상태를 조회한다. 78 181
79 182
**Endpoint:** `GET /v1/edge/nodes/{node_id}/config` 80 183
81 184
**Response (200 OK):** 82 185
83 186
```json 84 187
{ 85 188
"node_id": "String (필수)", 86 189
"limits": { 87 190
"config_version": "Number (필수)", 88 191
"effective_at": "String (필수, ISO 8601)", 89 192
"limits": { 90 193
"rate_limit": "Number", 91 194
"max_frame_size": "Number", 92 195
"reattach_window": "Number", 93 196
"max_buffer_size": "Number", 94 197
"buffer_overflow_policy": "String" 95 198
} 96 199
}, 97 200
"capabilities": { 98 201
"config_version": "Number (필수)", 99 202
"effective_at": "String (필수, ISO 8601)", 100 203
"capabilities": ["String"] 101 204
} 102 205
} 103 206
``` 104 207
105 208
이 엔드포인트는 Pull 방식으로 최신 설정을 확인하기 위한 것이다. 실시간 Push 알림은 §9.4.4를 참조한다. 106 209
107 210
### 9.3. 엣지 세션(Edge Session) 라이프사이클 관리 108 211
109 212
엣지 노드는 마스터 서버를 통해 특정 로컬 머신에 새로운 세션을 생성하거나 기존 세션을 조회할 수 있다. 110 213
111
### 9.3.1. 원격 세션 생성 (Edge → Master) 214
#### 9.3.1. 원격 세션 생성 (Edge → Master) 112 215
113 216
사용자가 특정 로컬 머신을 선택하여 대화를 시작할 때 호출한다. 114 217
115 218
**Endpoint:** `POST /v1/edge/nodes/{node_id}/sessions` 116 219
117 220
**Request:** 118 221
119 222
```json 120 223
{ 121 224
"agent_name": "String (필수, 실행할 에이전트)", 122 225
"workspace_path": "String (선택, 작업 디렉토리)" 123 226
} 124 227
``` 125 228
126 229
**Response (201 Created):** 127 230
128 231
```json 129 232
{ 130 233
"session_id": "String (필수, UUID v4)", 131 234
"edge_ws_ticket": "String (필수, 엣지용 WSS 연결 티켓. 1회용)", 132 235
"status": "String (필수, 'INIT' | 'RUNNING')", 133 236
"pinned_config_versions": { 134 237
"limits": "Number (필수, 이 세션에 바인딩된 Resource Limits config_version)", 135 238
"capabilities": "Number (필수, 이 세션에 바인딩된 Capabilities config_version)" 136 239
} 137 240
} 138 241
``` 139 242
140
> **변경 사항**: `pinned_config_versions` 객체가 추가되어, 이 세션에 바인딩된 설정 버전을 엣지 노드가 확인할 수 있다. §4.1.8의 Session-Pinned Config 참조. 141
142 243
**동작 원리:** 이 요청을 받은 마스터 서버는 즉시 대상 로컬 머신의 `POST /v1/session/init` (본 스펙 §5.1)을 호출하여 로컬 세션을 확보한 뒤 응답해야 한다. 143 244
144
### 9.3.2. 활성 세션 목록 조회 245
#### 9.3.2. 활성 세션 목록 조회 145 246
146 247
사용자 계정에 귀속된 모든 세션의 현재 상태를 조회한다. 147 248
148 249
**Endpoint:** `GET /v1/edge/sessions` 149 250
150 251
**Query Parameters:** 151 252
152 253
| 파라미터 | 타입 | 필수 | 설명 | 153 254
| --------- | ------ | ---- | ----------------------------------------------------------------------------------------------- | 154 255
| `status` | String | 선택 | 세션 상태 필터. `'INIT'`, `'RUNNING'`, `'DETACHED'` 중 하나. 미지정 시 모든 상태의 세션을 반환. | 155 256
| `node_id` | String | 선택 | 특정 노드에 귀속된 세션만 필터링. | 156 257
157 258
**Response (200 OK):** 158 259
159 260
```json 160 261
{ 161 262
"sessions": [ 162 263
{ 163 264
"session_id": "String (필수, UUID v4)", 164 265
"node_id": "String (필수, 세션이 실행 중인 로컬 클라이언트 식별자)", 165 266
"agent_name": "String (필수, 실행 중인 에이전트 이름)", 166 267
"status": "String (필수, 'INIT' | 'RUNNING' | 'DETACHED')", 167 268
"created_at": "String (필수, ISO 8601, 세션 생성 시각)", 168 269
"last_activity_at": "String (필수, ISO 8601, 마지막 이벤트 수신 시각)", 169 270
"pinned_config_versions": { 170 271
"limits": "Number (필수, 이 세션에 바인딩된 Resource Limits config_version)", 171 272
"capabilities": "Number (필수, 이 세션에 바인딩된 Capabilities config_version)" 172 273
} 173 274
} 174 275
] 175 276
} 176 277
``` 177 278
178 279
`pinned_config_versions`를 통해 엣지 노드는 각 세션이 어떤 시점의 설정으로 동작 중인지를 목록 수준에서 확인할 수 있다. 노드의 현재 `config_versions`(§9.2.1)와 비교하면, 세션이 최신 설정인지 이전 설정인지를 즉시 판별할 수 있다. `TERMINATED` 상태의 세션은 이미 종료되었으므로 목록에 포함하지 않는다 (MUST NOT). 종료된 세션의 이력 조회가 필요한 경우 별도의 세션 이력 API를 통해 제공한다. 179 280
180 281
### 9.4. 엣지 데이터 평면 (Edge WebSocket 통신) 181 282
182 283
엣지 노드는 발급받은 티켓을 사용하여 마스터 서버와 WSS를 연결하며, 마스터 서버는 이 연결을 대상 로컬 머신과 투명하게 중계(Transparent Proxy)한다. 183 284
184 285
**Connection URI:** `wss://{master_host}/v1/edge/ws/stream?ticket={edge_ws_ticket}&session_id={session_id}` 185 286
186
### 9.4.1. 프로토콜 투명성 (Protocol Transparency) 287
#### 9.4.1. 프로토콜 투명성 (Protocol Transparency) 187 288
188
엣지 노드와 마스터 서버 간의 WSS 통신은 RAWP-DPS 1.0 규격을 완전히 동일하게 사용한다 (MUST). 289
엣지 노드와 마스터 서버 간의 WSS 통신은 RAWP-DPS 1.0.1 규격을 완전히 동일하게 사용한다 (MUST). 189 290
190 291
엣지 노드는 본 스펙 §6의 `control.*` 이벤트(사용자 프롬프트, 인터랙션 응답 등)를 마스터 서버로 발송하며, 마스터는 이를 로컬 머신으로 포워딩한다. 191 292
192 293
엣지 노드는 로컬 머신이 발송한 `agent.*`, `tool.*`, `session.*` 이벤트를 마스터 서버를 통해 수신하여 사용자 UI에 렌더링한다. 193 294
194
### 9.4.2. 마스터 서버의 중계 및 개입 규칙 295
#### 9.4.2. 마스터 서버의 중계 및 개입 규칙 195 296
196 297
마스터 서버는 단순한 파이프 역할을 넘어 다음의 제어 책임을 가진다: 197 298
198 299
**라우팅**: 엣지 노드의 발신 프레임을 올바른 로컬 머신의 소켓으로 전달. 199 300
200 301
**사용량 집계**: 중계되는 `session.usage` 이벤트를 인터셉트하여 데이터베이스에 누적 저장 (§9.2.2 지표 제공용). 201 302
202 303
**파일 검색 중계**: `control.file.search`, `control.file.search.cancel`, `session.file.candidates` 이벤트는 중계 대상에 포함된다. `session.file.candidates`는 `session.usage`와 달리 인터셉트 없이 단순 포워딩 대상이다. 203 304
204 305
**보안 필터링**: 엣지 노드가 허가되지 않은 `control.prompt.request` 내장 도구(e.g., 로컬 시스템 파괴성 명령어)를 전송하려 할 경우, 마스터 서버 단에서 프레임을 드롭하고 엣지 노드에 `session.error`를 반환해야 한다 (MUST). 205 306
206
### 9.4.3. 다중 접점 동기화 (Multi-device Synchronization) 307
#### 9.4.3. 다중 접점 동기화 (Multi-device Synchronization) 207 308
208
동일한 `session_id`에 대해 여러 엣지 노드(예: 모바일 앱과 데스크톱 브라우저 동시 접속)가 연결을 요청할 경우, 마스터 서버는 수신되는 RAWP-DPS 1.0 프레임을 모든 연결된 엣지 소켓으로 브로드캐스트(Fan-out)하여 UI 상태를 동기화해야 한다 (SHOULD). 단, `control.*` 이벤트 발송 시 동시성 충돌을 막기 위해 `message_id` 기반 멱등성 처리를 수행해야 한다. 309
동일한 `session_id`에 대해 여러 엣지 노드(예: 모바일 앱과 데스크톱 브라우저 동시 접속)가 연결을 요청할 경우, 마스터 서버는 RAWP-DPS 1.0.1 §2.4의 브로드캐스트 원칙과 동일한 정책을 적용해야 한다 (MUST). 209 310
210
### 9.4.4. 노드 설정 변경 Push 알림 (Node Config Change Notification) 311
- 로컬 클라이언트로부터 수신한 모든 DPS 프레임(`agent.*`, `tool.*`, `session.*`)을 해당 세션에 연결된 모든 엣지 WSS로 브로드캐스트해야 한다 (MUST). 312
- 엣지 노드가 발송한 `control.*` 프레임(예: `control.prompt.request`)도 동일 세션에 연결된 다른 모든 엣지 WSS로 중계해야 한다 (MUST). 이를 통해 엣지 A가 전송한 프롬프트를 엣지 B도 수신하여 표시할 수 있다. 313
- 중계 시 `message_id` 기반 멱등성 처리를 수행하여 발신 엣지에 자신의 프레임이 되돌아오는 것을 방지해야 한다 (MUST). 211 314
315
**동시 프롬프트 충돌 처리**: 동일 세션에 대해 여러 엣지 노드가 동시에 `control.prompt.request`를 발송한 경우, 마스터 서버는 `message_id` 타임스탬프 기준 선착순(first-come)으로 하나만 수락하고, 후발 요청에 대해서는 RAWP-DPS 1.0.1 §7.4.1의 `session.error` (error_code: `"CONCURRENT_PROMPT"`, fatal: `false`)를 반환해야 한다 (MUST). 316
317
#### 9.4.4. 노드 설정 변경 Push 알림 (Node Config Change Notification) 318
212 319
로컬 클라이언트가 §4.1.5 또는 §4.1.6을 통해 설정을 갱신하면, 마스터 서버는 해당 노드에 연결된 모든 활성 엣지 WSS 소켓에 설정 변경 알림 프레임을 전송해야 한다 (MUST). 이 알림을 통해 엣지 노드는 UI에 반영할 설정 변경(예: capabilities 변경으로 인한 기능 목록 갱신)을 실시간으로 인지할 수 있다. 213 320
214 321
**알림 프레임 구조**: 215 322
216
이 알림은 RAWP-DPS 1.0의 Envelope 구조(RAWP-DPS §2)를 따르되, 세션 단위가 아닌 노드 단위의 이벤트이므로 `session_id`와 `turn_id`를 포함하지 않는다. 마스터 서버가 자체적으로 생성하여 발송하는 프레임이다. 323
이 알림은 RAWP-DPS 1.0.1의 Envelope 구조(RAWP-DPS §2)를 따르되, 세션 단위가 아닌 노드 단위의 이벤트이므로 `session_id`와 `turn_id`를 포함하지 않는다. 마스터 서버가 자체적으로 생성하여 발송하는 프레임이다. 217 324
218 325
```json 219 326
{ 220 327
"v": "rawp-dps-1.0", 221 328
"type": "node.config.changed", 222 329
"message_id": "String (필수, UUID v4)", 223 330
"timestamp": "String (필수, ISO 8601)", 224 331
"payload": { 225 332
"node_id": "String (필수, 설정이 변경된 노드의 식별자)", 226 333
"changed_scope": "String (필수, 'limits' | 'capabilities')", 227 334
"config_version": "Number (필수, 갱신된 스코프의 새 config_version)", 228 335
"reason": "String (선택, 클라이언트가 갱신 요청 시 전달한 reason 값 전파)" 229 336
} 230 337
} 231 338
``` 232 339
233 340
**엣지 노드의 처리 규칙**: 234 341
235 342
- `node.config.changed` 수신 시, 엣지 노드는 `changed_scope`에 따라 관련된 로컬 캐시를 무효화해야 한다 (MUST). 236 343
- 상세 설정 값이 필요하면 §9.2.3의 `GET /v1/edge/nodes/{node_id}/config`을 Pull 호출하여 최신 값을 획득한다. 알림 프레임 자체에는 변경된 설정의 전체 값을 포함하지 않는다. 이는 알림 프레임의 크기를 최소화하고, 엣지 노드가 필요한 시점에만 상세 데이터를 가져오도록 하기 위함이다. 237 344
- `changed_scope: "capabilities"` 수신 시, 엣지 UI가 기능 목록이나 에이전트 지원 기능 표시를 동적으로 갱신하는 경우, Pull 호출 후 UI를 갱신해야 한다 (SHOULD). 238 345
- 활성 세션에는 §4.1.8에 따라 이전 설정이 유지되므로, 현재 진행 중인 세션의 동작을 변경할 필요는 없다. 239 346
240 347
**전송 범위**: 마스터 서버는 해당 노드(`node_id`)에 연결된 모든 엣지 WSS 소켓에 알림을 브로드캐스트한다. 특정 세션의 WSS가 아닌, 해당 노드와 관련된 모든 엣지 연결이 대상이다. 세션 WSS가 아직 연결되지 않았더라도, 해당 노드에 대해 다른 세션의 WSS가 활성 상태이면 해당 소켓을 통해 알림이 전달된다. 241 348
242 349
--- 수정됨
부록: 관련 규격 문서
추가된 줄 5 삭제된 줄 5
1 1
## 부록: 관련 규격 문서 2 2
3
| 문서 | 설명 | 4
| ----------------------- | ----------------------------------------------------- | 5
| **RAWP-DPS 1.0** | 데이터 평면 스트리밍 규격 (현행). 본 문서 §6이 참조. | 6
| **RAWP-CRS 1.0** | 클라이언트 렌더링 규격 (현행). 본 문서 §8이 참조. | 7
| **RAWP-DPS-0.1-Legacy** | 데이터 평면 스트리밍 규격 (단종 예고). 레거시 호환용. | 3
| 문서 | 설명 | 4
| ----------------------- | ---------------------------------------------------- | 5
| **RAWP-DPS 1.0.1** | 데이터 평면 스트리밍 규격 (현행). 본 문서 §6이 참조. | 6
| **RAWP-CRS 1.0.1** | 클라이언트 렌더링 규격 (현행). 본 문서 §8이 참조. | 7
| **RAWP-DPS-0.1-Legacy** | 데이터 평면 스트리밍 규격 (지원 종료). | 추가됨
10. 로컬 세션 관리 (Local Session Management)
추가된 줄 136 삭제된 줄 0
1
## 10. 로컬 세션 관리 (Local Session Management) 2
3
본 장은 사용자가 데스크톱 앱에서 마스터 서버 없이 직접 에이전트와 대화하기 위한 로컬 세션 프로토콜을 정의한다. 로컬 세션은 §5의 원격 세션과 동일한 세션 상태 머신(`INIT → RUNNING → DETACHED → TERMINATED`)과 RAWP-DPS 1.0.1 프로토콜을 공유하되, 인증과 보안 모델이 다르다. 4
5
### 10.1. 로컬 세션 엔드포인트 6
7
로컬 세션 전용 엔드포인트는 `/local/v1/` 접두사를 사용하여 원격(마스터 발) 엔드포인트(`/v1/`)와 네임스페이스를 분리한다. 8
9
#### 10.1.1. 로컬 세션 생성 10
11
**Endpoint**: `POST /local/v1/sessions` (Local Client 제공) 12
13
**Request**: 14
15
```json 16
{ 17
"agent_name": "String (필수, 실행할 에이전트 이름. §4.3의 에이전트 목록 참조)", 18
"workspace_path": "String (필수, 작업 디렉토리 절대 경로)", 19
"session_name": "String (선택, 세션 표시명. 생략 시 agent_name + 생성 시각으로 자동 생성)" 20
} 21
``` 22
23
**Response (201 Created)**: 24
25
```json 26
{ 27
"session_id": "String (필수, UUID v4)", 28
"ws_ticket": "String (필수, 1회용 WSS 연결 티켓)", 29
"status": "String (필수, 'INIT')" 30
} 31
``` 32
33
WSS 연결: `ws://127.0.0.1:{port}/v1/ws/stream?ticket={ws_ticket}&session_id={session_id}` 34
35
#### 10.1.2. 로컬 세션 재개 36
37
**Endpoint**: `POST /local/v1/sessions/{session_id}/resume` (Local Client 제공) 38
39
**Request**: 40
41
```json 42
{ 43
"last_sync_timestamp": "String (선택, ISO 8601)", 44
"last_message_id": "String (선택, UUID)" 45
} 46
``` 47
48
**Response (200 OK)**: 49
50
```json 51
{ 52
"session_id": "String (필수)", 53
"ws_ticket": "String (필수, 1회용 WSS 연결 티켓)", 54
"status": "String (필수, 'RUNNING')" 55
} 56
``` 57
58
**제약 조건**: 존재하지 않거나 `TERMINATED` 상태인 세션에 대한 요청은 `404 Not Found`를 반환한다 (MUST). 59
60
#### 10.1.3. 로컬 세션 종료 61
62
**Endpoint**: `DELETE /local/v1/sessions/{session_id}` (Local Client 제공) 63
64
**제약 조건**: §5.2와 동일한 종료 절차를 따른다. `session.deleted` WSS 알림 의무(§5.2)도 동일하게 적용된다. 65
66
**Response**: `204 No Content`. 67
68
#### 10.1.4. 로컬 세션 목록 조회 69
70
**Endpoint**: `GET /local/v1/sessions` (Local Client 제공) 71
72
**Query Parameters**: 73
74
| 파라미터 | 타입 | 필수 | 설명 | 75
| -------- | ------ | ---- | --------------------------------------------------------------------------- | 76
| `status` | String | 선택 | `'INIT'`, `'RUNNING'`, `'DETACHED'` 중 하나. 미지정 시 모든 활성 세션 반환. | 77
| `origin` | String | 선택 | `'local'` 또는 `'remote'`. §10.3 참조. 미지정 시 모든 origin의 세션 반환. | 78
79
**Response (200 OK)**: 80
81
```json 82
{ 83
"sessions": [ 84
{ 85
"session_id": "String (필수)", 86
"agent_name": "String (필수)", 87
"workspace_path": "String (필수)", 88
"session_name": "String (필수)", 89
"status": "String (필수, 'INIT' | 'RUNNING' | 'DETACHED')", 90
"origin": "String (필수, §10.3 참조)", 91
"created_at": "String (필수, ISO 8601)", 92
"last_activity_at": "String (필수, ISO 8601)" 93
} 94
] 95
} 96
``` 97
98
### 10.2. 로컬 인증 면제 99
100
- 클라이언트의 HTTP/WSS 서버가 루프백 주소(`127.0.0.1` 또는 `::1`)에 바인딩된 경우, `/local/v1/` 접두 엔드포인트에 대해 `Authorization` 헤더 검증을 면제해야 한다 (MUST). 101
- 클라이언트가 외부 네트워크 주소에 바인딩된 경우, `/local/v1/` 엔드포인트를 노출해서는 안 된다 (MUST NOT). 이는 인증 없는 외부 접근을 차단하기 위함이다. 102
103
### 10.3. 세션 식별 (Session Origin) 104
105
모든 세션은 `origin` 속성을 가진다: 106
107
| `origin` 값 | 설명 | 108
| ---------------------- | ------------------------------------------------------------------------------- | 109
| `"local"` | §10.1.1을 통해 로컬에서 생성된 세션 | 110
| `"remote:{server_id}"` | §5.1을 통해 마스터 서버가 생성한 원격 세션. `server_id`는 마스터의 고유 식별자. | 111
112
`origin`은 세션 생성 시 결정되며 변경할 수 없다 (MUST NOT). 113
114
### 10.4. 로컬 세션 보안 모델 115
116
- **경로 검증 면제**: 로컬 세션(`origin: "local"`)은 §7.2의 경로 검증(`workspace_path` 범위 제한)을 적용하지 않는다. 사용자가 직접 조작하는 환경이므로 파일시스템 접근 범위를 제한하지 않는다. 117
- **도구 승인**: 로컬 세션에서의 도구 승인 요청은 RAWP-DPS 1.0.1 §4.3.1의 `agent.interaction.request`를 통해 사용자에게 직접 표시된다. 마스터를 경유하지 않는다. 118
119
### 10.5. 로컬-원격 세션 공존 120
121
- 동일 에이전트에 대해 로컬 세션과 원격 세션이 동시에 존재할 수 있다 (MUST 지원). 122
- 각 세션은 독립된 에이전트 프로세스 또는 SDK 인스턴스를 가진다. 세션 간 에이전트 상태를 공유하지 않는다 (MUST NOT). 123
124
### 10.6. 원격 세션 로컬 감지 125
126
마스터가 `POST /v1/session/init`(§5.1)로 원격 세션을 생성하면, 클라이언트는 해당 세션을 `origin: "remote:{server_id}"`로 등록한다. 이 세션은 `GET /local/v1/sessions`(§10.1.4) 응답에 포함되어야 한다 (MUST). 127
128
로컬 UI가 원격 세션에 연결할 때는 동일 클라이언트의 로컬 WSS 엔드포인트를 사용한다: 129
130
``` 131
ws://127.0.0.1:{port}/v1/ws/stream?ticket={local_ticket}&session_id={session_id} 132
``` 133
134
이때 `local_ticket`은 `/local/v1/sessions/{session_id}/resume`(§10.1.2)을 통해 발급받는다. 로컬 UI WSS 연결과 마스터 WSS 연결은 독립적이며, 에이전트의 DPS 이벤트는 양쪽 소켓 모두에 전달되어야 한다 (MUST). 135
136
--- 추가됨
11. 게이트웨이 생명주기 (Gateway Lifecycle)
추가된 줄 44 삭제된 줄 0
1
## 11. 게이트웨이 생명주기 (Gateway Lifecycle) 2
3
본 장은 로컬 클라이언트의 HTTP/WSS 서버(게이트웨이)의 시작, 중지, 재시작에 대한 프로토콜 계약을 정의한다. 4
5
### 11.1. 게이트웨이 상태 머신 6
7
``` 8
STOPPED → STARTING → RUNNING → STOPPING → STOPPED 9
↑ 10
RESTARTING ─┘ 11
``` 12
13
| 상태 | 설명 | 14
| ------------ | ----------------------------------------------------- | 15
| `STOPPED` | HTTP/WSS 서버 미기동. 모든 포트 미바인딩. | 16
| `STARTING` | 서버 초기화 중. 포트 바인딩 시도. | 17
| `RUNNING` | 서버 가동 중. 요청 수신 가능. | 18
| `STOPPING` | 서버 종료 중. 신규 연결 거부, 기존 세션 정리. | 19
| `RESTARTING` | `STOPPING` → `STARTING` 연쇄 전이. 설정 변경 적용 등. | 20
21
### 11.2. 상태별 동작 규칙 22
23
**STARTING**: 신규 세션 초기화(§5.1) 및 로컬 세션 생성(§10.1.1) 요청을 수락하지 않는다 (MUST NOT). 헬스체크(§4.2)에는 응답할 수 있다 (MAY). 24
25
**STOPPING**: 신규 연결을 거부하고, 모든 활성 세션을 정리한다. 활성 세션에 대해 RAWP-DPS 1.0.1 §7.5.3의 종료 절차를 수행한 후, WSS 연결을 종료해야 한다 (MUST). 26
27
**RESTARTING**: `STOPPING` 완료 후 `STARTING`으로 전이한다. 재시작 전후로 세션은 보존되지 않는다. 28
29
### 11.3. 헬스체크 확장 30
31
§4.2의 `GET /v1/health` 응답에 게이트웨이 상태 필드를 추가한다: 32
33
```json 34
{ 35
"status": "String (필수, 'online', 'busy', 'error')", 36
"uptime": "Number (필수, 클라이언트 가동 초)", 37
"active_sessions": "Number (선택, 현재 유지 중인 세션 수)", 38
"gateway_state": "String (필수, 'STOPPED' | 'STARTING' | 'RUNNING' | 'STOPPING' | 'RESTARTING')" 39
} 40
``` 41
42
`gateway_state`가 `RUNNING`이 아닌 상태에서 세션 초기화(§5.1)가 시도되면 `503 Service Unavailable`을 반환해야 한다 (MUST). 43
44
---