update project files and proto definitions

This commit is contained in:
toki 2026-05-11 07:36:50 +09:00
parent d764adb390
commit 150e9fda2f
17 changed files with 383 additions and 160 deletions

0
.codex Normal file
View file

283
README.md
View file

@ -1,54 +1,257 @@
# IOP — Inference Operations Platform # IOP
IOP는 단일 서비스가 아닌 플랫폼이다. 여러 앱이 협력하여 AI 모델 추론 워크로드를 처리한다. IOP(Inference Operations Platform)는 단순한 모델 라우터나 OpenAI API proxy가 아니다.
## 앱 구성 IOP는 **Control Plane - Edge - Node** 계층 구조를 기반으로, 여러 로컬 모델 런타임과 CLI Agent 실행 환경을 통합 관리하는 실행 오케스트레이션 플랫폼을 지향한다. 모델 서빙, CLI Agent 실행, shell/git/docker/code workspace 작업, node maintenance 작업을 같은 실행 파이프라인에서 다룰 수 있도록 만드는 것이 핵심 방향이다.
| 앱 | 책임 | 현재 프로젝트는 완성된 운영 시스템이 아니라 스켈레톤 단계다. 이 README는 현재 구현의 세부 사용법보다, 프로젝트가 향하는 구조와 경계를 명확히 설명한다.
## Overview
IOP의 실행 대상은 크게 두 가지다.
- **Model Serving**
- Ollama
- vLLM
- MLX
- 그 외 로컬/원격 모델 런타임
- **Agent / Automation Execution**
- CLI Agent
- Shell
- Git
- Docker
- Code workspace 작업
- NomadCode 계열 자동화 작업
IOP는 model serving만 담당하는 시스템이 아니다. CLI Agent 실행과 node maintenance도 adapter 기반 실행으로 보고, Edge와 Node를 통해 실행 요청, 스트림, 상태, 결과를 관리하는 방향으로 설계한다.
## Core Concept
IOP의 중심 개념은 `adapter + target` 기반 실행이다.
- `adapter`는 실행 방식을 나타낸다.
- `target`은 해당 adapter 안에서 실행할 구체 대상을 나타낸다.
- `execution`은 adapter와 target을 해석해 실제 Node에서 수행되는 단위다.
예시는 다음과 같다.
```text
adapter = ollama
target = qwen3.6
adapter = vllm
target = gemma4
adapter = cli
target = cline-dgx
adapter = cli
target = codex-local
```
외부 OpenAI API 호환 계층에서는 호환성을 위해 `model` 필드가 남을 수 있다. 그러나 내부 실행 개념에서는 모델 이름만으로 전체 실행을 설명하지 않고, `adapter`, `target`, `execution`, `node adapter`, `adapter execution` 같은 용어를 우선한다.
## Architecture
IOP는 Control Plane이 Edge를 통해 시스템을 제어하고, Edge가 자신의 로컬 실행 그룹을 운영하는 구조를 지향한다.
```text
Control Plane
├─ Edge Group A
│ ├─ Node 1
│ ├─ Node 2
│ └─ Node 3
└─ Edge Group B
├─ Node 4
└─ Node 5
```
Control Plane은 Node에 직접 연결하지 않는다. 전체 시스템 제어는 Edge를 통해 이뤄지고, Edge는 자신이 관리하는 Node와 로컬 런타임 상태의 원본을 가진다.
핵심 문장은 다음과 같다.
> Control Plane은 Edge를 통해 시스템을 제어하고, Edge는 자신의 로컬 런타임 상태를 소유하고 운영한다.
### Control Plane
Control Plane은 자체 서버와 프론트 페이지를 가진 중앙 관리 계층이다.
주요 책임은 다음과 같다.
- 여러 Edge 연결 관리
- Edge 상태 조회
- Edge 설정 변경
- Edge에 명령 전달
- Edge 이벤트 수신
- 전체 시스템 관찰
- Runtime 영역과 Automation 영역을 나눠 보여주는 운영 화면 제공
Control Plane과 Edge는 소켓 기반 연결을 사용하고, 이벤트, 상태, 명령 결과를 실시간으로 주고받는 구조를 지향한다.
Control Plane은 전통적인 Kubernetes식 중앙 스케줄러가 아니다. 다음 책임은 Control Plane에 두지 않는다.
- Node 직접 연결
- Node 직접 스케줄링
- Edge 내부 DB 대체
- 모든 런타임 상태의 단일 원본화
- 매 요청마다 Node 할당 판단
### Edge
Edge는 단순 API gateway가 아니라 백엔드 전용 실행 그룹 컨트롤러다.
하나의 Edge는 여러 Node를 관리하며, 특정 디바이스 그룹, 로컬 모델 그룹, 자동화 실행 그룹을 하나로 묶는 단위가 된다. Edge는 모델 서빙과 CLI Agent 실행을 모두 처리할 수 있어야 한다.
Edge의 핵심 역할은 다음과 같다.
- Node registry
- Node configuration
- Adapter/Profile configuration
- Runtime routing
- Job assignment
- Stream relay
- Session handling
- Local runtime state
- Execution history aggregation
- Event aggregation
Edge는 자신의 데이터를 자체적으로 가진다. Control Plane은 Edge의 데이터를 조회하고 조정하지만, 런타임 데이터의 원본은 Edge다.
### Node
Node는 실제 실행자다.
Node는 모델 런타임, CLI Agent, 도구 실행을 담당한다. Node는 Control Plane이 아니라 Edge에 연결되며, Edge가 전달한 실행 요청을 adapter execution으로 수행하고 이벤트와 결과를 되돌려준다.
Node는 가능한 한 단순한 실행 단위로 유지한다. 정책, 전체 시스템 조정, 다중 Edge 운영 판단을 Node에 밀어 넣지 않고, 전달받은 실행을 안정적으로 수행하는 데 집중한다.
## Execution Model
### Adapter and Target
IOP 내부 실행은 model 중심이 아니라 adapter 중심으로 정리한다.
- `adapter`: `mock`, `ollama`, `vllm`, `cli` 같은 실행 구현
- `target`: adapter 안에서 선택되는 모델, profile, agent, toolchain
- `execution`: 특정 adapter와 target으로 수행되는 단일 실행
- `node adapter`: Node 안에 등록되어 실제 실행을 담당하는 adapter
- `adapter execution`: Node adapter가 수행하는 실행 단위
현재 코드에는 `RunRequest`, `ExecutionSpec`, `RuntimeEvent`, `NodeCommandRequest`처럼 이 방향을 담기 위한 타입들이 있다. 세부 계약과 schema는 작업을 진행하면서 점진적으로 정리한다.
### Runtime Domain
Runtime Domain은 모델 서빙 중심 실행 영역이다.
- OpenAI API 호환 요청
- Ollama, vLLM, MLX 같은 모델 런타임
- 모델 라우팅
- 추론 요청 처리
- 모델 런타임 adapter 확장
이 영역에서도 내부적으로는 `adapter + target` 개념을 사용한다. 예를 들어 OpenAI 호환 요청의 `model` 값은 내부에서 특정 adapter와 target으로 해석될 수 있다.
### Automation Domain
Automation Domain은 CLI Agent와 도구 실행 중심 영역이다.
- CLI Agent 실행
- Shell, Git, Docker 작업
- code workspace 작업
- Plane 작업과 유지보수 작업
- Claude CLI, Gemini CLI, Codex CLI, OpenCode, Cline 같은 실행 대상
NomadCode는 IOP 안에 완전히 흡수된 제품이 아니라, IOP Automation Domain 위에서 동작할 수 있는 대표 사용처로 본다.
```text
IOP Core
└─ 공통 실행 오케스트레이션 계층
NomadCode
└─ IOP Automation Domain을 활용하는 개발 업무 자동화 도메인
```
## Current Status
현재 go-iop는 스켈레톤 단계다.
- Edge-Node 소켓 기반 구조를 우선 검증 중이다.
- Node 등록, 설정 전달, 실행 요청, 스트리밍 이벤트 흐름이 점진적으로 정리되고 있다.
- CLI adapter 쪽 구현이 먼저 진행되고 있다.
- 현재 실행 이력은 Node local SQLite store에서 검증 중이다. Edge 단위 이력 집계와 로컬 실행 그룹 상태 소유권은 로드맵에 따라 정리한다.
- `mock` adapter와 dummy/TODO 구현은 개발 단계에서 정상적인 구성이다.
- Ollama/vLLM 등 모델 runtime adapter는 단계적으로 확장한다.
- Control Plane은 향후 여러 Edge 관리와 프론트 페이지 제공을 위해 추가된다.
현재 앱 구성은 다음과 같다.
| 경로 | 현재 의미 |
|---|---| |---|---|
| `apps/node` | 디바이스별 node agent. 런타임 라우팅, 추론 어댑터 실행, edge 연결을 담당한다. | | `apps/node` | Edge에 연결되어 adapter execution을 수행하는 Node agent |
| `apps/edge` | node 등록, 레지스트리, transport, 콘솔 기반 요청 전달을 담당하는 gateway이다. | | `apps/edge` | Node registry, 설정 전달, routing, stream relay를 담당하는 Edge skeleton |
| `apps/control-plane` | 노드 등록, 정책, 스케줄링 제어 영역이다. | | `apps/control-plane` | 향후 여러 Edge를 연결하고 운영 화면을 제공할 중앙 관리 계층 |
| `apps/worker` | 비동기 작업 처리 영역이다. | | `apps/worker` | 향후 비동기 작업 처리 가능성을 위한 placeholder |
| `packages` | 설정, 인증, 정책, 작업, 관측성, 버전 등 공통 패키지 |
| `proto` | 앱 간 메시지 계약 원본과 생성물 |
| `configs` | 현재 개발용 설정 예시 |
## 내부 통신 ## Roadmap
- **프로토콜**: TCP + Protobuf + mTLS ### Phase 1. Edge-Node execution skeleton
- **메시지 계약**: `proto/iop/*.proto`
- **생성물**: `proto/gen/iop/*.pb.go`
- gRPC **사용 금지**
- WebSocket transport 기본화 **금지**
## 외부 API - Edge-Node 소켓 연결
- Node adapter execution
- CLI adapter 검증
- `adapter + target` 개념 정리
외부 OpenAI-compatible API는 `apps/edge`의 책임 영역으로 둔다. ### Phase 2. Runtime and Automation unification
## 기본 명령 예시 - 모델 서빙 adapter 확장
- CLI Agent 실행 안정화
- Runtime / Automation 실행 흐름 공통화
- Node local execution history와 Edge event aggregation 경계 정리
```bash ### Phase 3. Control Plane
# 의존성 설치
go mod tidy
# node CLI 빌드 예시 - 자체 서버와 프론트 페이지 제공
go build -o bin/node ./apps/node/cmd/node - 여러 Edge 연결
- Edge 상태 조회
- Edge 설정 변경
- Edge 명령 전달
- 이벤트 수신
# node CLI 실행 예시 ### Phase 4. Multi-Edge operations
./bin/node version
./bin/node config print
./bin/node serve
```
## 모노레포 구조 - 여러 Edge group 관리
- Edge별 runtime/automation 상태 표시
- Edge 단위 실행 이력과 상태 집계
- Edge 단위 작업 실행과 제어
``` ### Phase 5. Policy, History, Audit
iop/
apps/ - 권한, 정책, 이력, 감사 기능 확장
node/ - 상세 설계는 후속 작업에서 결정
edge/
control-plane/ ## Development Notes
worker/
packages/ # 공통 패키지 - 기존 구조를 우선하며, 세부 구현은 각 작업의 domain rule과 현재 코드 경계를 먼저 확인한 뒤 진행한다.
proto/ # Protobuf 스펙과 생성물 - 내부 통신은 TCP/protobuf 기반 소켓 흐름을 우선한다.
configs/ # 앱별 설정 파일 - gRPC 도입, WebSocket 기본 transport 전환, actor/FSM/plugin framework 도입은 현재 단계의 기본 방향이 아니다.
docs/ # 아키텍처와 운영 문서 - OpenAI API 호환 계층은 외부 호환을 위한 표면이며, 내부 실행 모델 전체를 대표하지 않는다.
``` - 앱별 README에는 현재 수동 테스트나 구현 세부가 더 많이 남아 있을 수 있다. 루트 README는 전체 방향과 경계를 설명하는 문서로 유지한다.
## Non-Goals for Current Stage
이번 단계에서는 다음 내용을 루트 README에서 상세 설계로 확정하지 않는다.
- 상세 DB schema
- 상세 protobuf 설계
- 상세 event schema
- 상세 permission model
- 상세 policy engine 설계
- 상세 audit log 구조
- Edge federation 세부 설계
- mTLS 세부 구현 계획
- Control Plane UI 화면별 상세 기획
- Plane 연동 상세 workflow
- NomadCode 상세 제품 설계

View file

@ -2,7 +2,7 @@
## 목적 / 책임 ## 목적 / 책임
외부 API gateway로 발전할 앱 영역이며, 현재는 node 연결을 수락하고 token 기반 등록을 검증한 뒤 중앙 관리 설정을 내려주는 TCP/protobuf 서버 책임을 가진다. 여러 Node를 하나의 로컬 실행 그룹으로 묶는 백엔드 실행 그룹 컨트롤러 영역이다. 현재는 node 연결을 수락하고 token 기반 등록을 검증한 뒤 adapter/runtime 설정을 내려주며, console 기반 실행 요청과 run event relay를 검증한다.
## 포함 경로 ## 포함 경로
@ -15,7 +15,7 @@
## 제외 경로 ## 제외 경로
- `apps/node/` — 실제 모델 실행과 adapter 관리 - `apps/node/` — 실제 모델 실행과 adapter 관리
- `apps/control-plane/`노드 등록/정책/스케줄링 예정 영역 - `apps/control-plane/`여러 Edge 연결 관리와 운영 화면 제공 예정 영역
- `apps/worker/` — 비동기 작업 처리 예정 영역 - `apps/worker/` — 비동기 작업 처리 예정 영역
- `packages/` — 공통 설정/관측성/인증 패키지 - `packages/` — 공통 설정/관측성/인증 패키지
- `proto/` — 메시지 계약 원본과 생성물 - `proto/` — 메시지 계약 원본과 생성물
@ -34,21 +34,24 @@
## 유지할 패턴 ## 유지할 패턴
- edge는 사전 등록된 node 정의를 검증하고 연결 상태를 registry에 반영하는 역할을 먼저 안정화한다. - edge는 사전 등록된 node 정의를 검증하고 연결 상태를 registry에 반영하는 역할을 먼저 안정화한다.
- 외부 OpenAI-compatible HTTP API는 node/transport 안정화 이후 확장한다. - edge는 단순 gateway가 아니라 Node registry, adapter/profile configuration, routing, stream relay의 소유자다.
- 외부 OpenAI-compatible HTTP API는 node/transport 안정화 이후 확장하며, 내부 실행 모델 전체를 대표하지 않는다.
- node가 연결 직후 RegisterRequest를 보내고 edge가 RegisterResponse로 중앙 설정을 응답하는 흐름을 유지한다. - node가 연결 직후 RegisterRequest를 보내고 edge가 RegisterResponse로 중앙 설정을 응답하는 흐름을 유지한다.
- Registry는 동시성 안전성을 유지하고 외부로 내부 map을 노출하지 않는다. - Registry는 동시성 안전성을 유지하고 외부로 내부 map을 노출하지 않는다.
- NodeStore는 설정 파일에서 한 번 seed되며 런타임 중 변경하지 않는다; token 중복·빈 token은 LoadFromConfig에서 즉시 거부한다. - NodeStore는 설정 파일에서 한 번 seed되며 런타임 중 변경하지 않는다; token 중복·빈 token은 LoadFromConfig에서 즉시 거부한다.
- adapter config 변환(mapper)은 `node.BuildConfigPayload()`에서만 수행하고 transport 레이어에 변환 로직을 두지 않는다. - adapter config 변환(mapper)은 `node.BuildConfigPayload()`에서만 수행하고 transport 레이어에 변환 로직을 두지 않는다.
- 내부 실행 요청은 `adapter + target`으로 표현한다. 외부 API 호환 경계의 `model` 표현을 edge 내부 책임 전체로 확장하지 않는다.
## 다른 도메인과의 경계 ## 다른 도메인과의 경계
- **node**: edge는 node를 실행하지 않는다. edge는 사전 등록 정보와 연결 registry를 기반으로 요청을 보낼 대상만 선택한다. - **node**: edge는 node 내부 adapter 직접 실행하지 않는다. edge는 사전 등록 정보와 연결 registry를 기반으로 요청을 보낼 대상과 실행 설정을 관리한다.
- **platform-common**: edge 설정, metrics, protobuf 타입은 platform-common 계약을 따른다. - **platform-common**: edge 설정, metrics, protobuf 타입은 platform-common 계약을 따른다.
- **control-plane 후보**: 장기적으로 노드 등록/정책/스케줄링이 control-plane으로 분리될 수 있으므로 edge에 해당 책임을 굳히기 전에 경계를 재검토한다. - **control-plane 후보**: control-plane은 Edge를 통해 시스템을 제어한다. Node 직접 연결/직접 스케줄링을 control-plane 책임으로 굳히지 않는다.
## 금지 사항 ## 금지 사항
- edge를 구현하면서 node adapter 실행 로직을 복제하지 않는다. - edge를 구현하면서 node adapter 실행 로직을 복제하지 않는다.
- OpenAI-compatible HTTP API를 추가할 때 내부 TCP/protobuf 경계를 우회하지 않는다. - OpenAI-compatible HTTP API를 추가할 때 내부 TCP/protobuf 경계를 우회하지 않는다.
- Control Plane 구현 전이라도 Node를 Control Plane에 직접 연결하는 경로를 edge 도메인에 추가하지 않는다.
- gRPC 또는 WebSocket을 기본 내부 transport로 바꾸지 않는다. - gRPC 또는 WebSocket을 기본 내부 transport로 바꾸지 않는다.
- `proto/gen/iop/*.pb.go` 생성 파일을 직접 수정하지 않는다. - `proto/gen/iop/*.pb.go` 생성 파일을 직접 수정하지 않는다.

View file

@ -2,7 +2,7 @@
## 목적 / 책임 ## 목적 / 책임
디바이스당 1개 실행되는 IOP 노드 에이전트 영역이다. edge에서 들어온 실행 요청을 runtime 요청으로 변환하고, 라우팅된 어댑터를 실행하며, 실행 이벤트와 이력을 관리한다. Edge에 연결되어 실제 adapter execution을 수행하는 IOP 노드 에이전트 영역이다. edge에서 들어온 실행 요청을 runtime 요청으로 변환하고, 라우팅된 어댑터를 실행하며, 실행 이벤트와 현재 단계의 로컬 실행 이력을 관리한다.
## 포함 경로 ## 포함 경로
@ -17,15 +17,15 @@
## 제외 경로 ## 제외 경로
- `apps/edge/`node를 받아들이는 gateway 서버 영역 - `apps/edge/`Node를 관리하는 실행 그룹 컨트롤러 영역
- `apps/control-plane/`노드 등록/정책/스케줄링 예정 영역 - `apps/control-plane/`여러 Edge 연결 관리와 운영 화면 제공 예정 영역
- `apps/worker/` — 비동기 작업 처리 예정 영역 - `apps/worker/` — 비동기 작업 처리 예정 영역
- `packages/` — 여러 앱이 공유하는 공통 패키지 - `packages/` — 여러 앱이 공유하는 공통 패키지
- `proto/` — 앱 간 메시지 계약 - `proto/` — 앱 간 메시지 계약
## 주요 구성 요소 ## 주요 구성 요소
- `runtime.Adapter`모델/CLI 실행 어댑터 계약 - `runtime.Adapter`adapter target 실행 계약
- `runtime.Router` — 실행 요청을 구체적인 `ExecutionSpec`으로 변환하는 계약 - `runtime.Router` — 실행 요청을 구체적인 `ExecutionSpec`으로 변환하는 계약
- `node.Node``transport.Handler` 구현체이자 실행 파이프라인 조정자 - `node.Node``transport.Handler` 구현체이자 실행 파이프라인 조정자
- `node.runManager` — run ID 기준 `runHandle`(cancel, done) 등록/해제/취소 관리; `node.Node` 내부에서만 사용 - `node.runManager` — run ID 기준 `runHandle`(cancel, done) 등록/해제/취소 관리; `node.Node` 내부에서만 사용
@ -41,6 +41,7 @@
- `runtime` 패키지에는 도메인 타입과 인터페이스를 두고 구체 구현 의존성을 넣지 않는다. - `runtime` 패키지에는 도메인 타입과 인터페이스를 두고 구체 구현 의존성을 넣지 않는다.
- transport/proto 타입은 `node.Node` 경계에서 runtime 타입으로 변환한다. - transport/proto 타입은 `node.Node` 경계에서 runtime 타입으로 변환한다.
- 내부 실행 식별자는 `adapter + target`을 사용한다. 외부 OpenAI-compatible API나 legacy placeholder를 제외하고 `model`을 내부 실행 대표 용어로 되돌리지 않는다.
- 어댑터 추가 시 `runtime.Adapter`를 구현하고 `adapters.BuildFromPayload()` 또는 bootstrap registry에 등록한다. - 어댑터 추가 시 `runtime.Adapter`를 구현하고 `adapters.BuildFromPayload()` 또는 bootstrap registry에 등록한다.
- 실행 취소는 run ID 기준으로 `runManager`에 등록하고 실행 종료 시 반드시 `deregister`로 해제한다. - 실행 취소는 run ID 기준으로 `runManager`에 등록하고 실행 종료 시 반드시 `deregister`로 해제한다.
- `adapters.Registry`의 start/stop은 bootstrap lifecycle에서만 호출하고 개별 adapter에서 직접 호출하지 않는다. - `adapters.Registry`의 start/stop은 bootstrap lifecycle에서만 호출하고 개별 adapter에서 직접 호출하지 않는다.
@ -49,9 +50,9 @@
## 다른 도메인과의 경계 ## 다른 도메인과의 경계
- **edge**: edge는 node 연결 등록과 중앙 설정 전달, 라우팅 진입을 담당한다. node는 edge가 보낸 실행 요청을 처리하고 이벤트를 돌려준다. - **edge**: edge는 node 연결 등록, adapter/runtime 설정 전달, 라우팅 진입, stream relay를 담당한다. node는 edge가 보낸 실행 요청을 처리하고 이벤트를 돌려준다.
- **platform-common**: node는 `packages/config`, `packages/observability`, `proto/gen/iop` 등을 사용하지만 공통 타입/설정 자체의 소유자는 platform-common이다. - **platform-common**: node는 `packages/config`, `packages/observability`, `proto/gen/iop` 등을 사용하지만 공통 타입/설정 자체의 소유자는 platform-common이다.
- **control-plane 후보**: 정책/스케줄링의 시스템 단위 결정은 control-plane에서 다루고, node는 전달받은 실행 단위 수행에 집중한다. - **control-plane 후보**: control-plane은 Node가 아니라 Edge를 통해 시스템을 제어한다. node는 control-plane 직접 연결/직접 스케줄링을 전제로 하지 않는다.
## 금지 사항 ## 금지 사항

View file

@ -2,7 +2,7 @@
## 목적 / 책임 ## 목적 / 책임
여러 앱이 공유하는 설정, 인증, 정책, 메타데이터, 작업 상태, 관측성, 버전, protobuf 계약을 관리한다. 앱별 구현보다 안정적인 공통 계약과 작은 유틸리티를 제공하는 영역이다. 여러 앱이 공유하는 설정, 인증, 정책, 메타데이터, 작업 상태, 관측성, 버전, protobuf 계약을 관리한다. 앱별 구현보다 안정적인 공통 계약과 작은 유틸리티를 제공하며, 내부 실행 계약은 `adapter + target` 방향을 우선한다.
## 포함 경로 ## 포함 경로
@ -20,7 +20,7 @@
## 제외 경로 ## 제외 경로
- `apps/node/` — node 실행 파이프라인과 adapter 관리 - `apps/node/` — node 실행 파이프라인과 adapter 관리
- `apps/edge/`gateway 서버와 node registry - `apps/edge/`실행 그룹 컨트롤러와 node registry
- `apps/control-plane/` — control-plane 앱 구현 예정 영역 - `apps/control-plane/` — control-plane 앱 구현 예정 영역
- `apps/worker/` — worker 앱 구현 예정 영역 - `apps/worker/` — worker 앱 구현 예정 영역
@ -30,7 +30,7 @@
- `auth.LoadServerTLS` / `auth.LoadClientTLS` — mTLS TLS config 생성 - `auth.LoadServerTLS` / `auth.LoadClientTLS` — mTLS TLS config 생성
- `observability.NewLogger` / `observability.ServeMetrics` — 공통 로깅/메트릭 - `observability.NewLogger` / `observability.ServeMetrics` — 공통 로깅/메트릭
- `policy.Engine` — 정책 적용/검증 계약 - `policy.Engine` — 정책 적용/검증 계약
- `jobs.Job` — 비동기 작업 상태 모델 - `jobs.Job` — 비동기 작업 상태 placeholder; 내부 실행 대상은 `target`으로 표현
- `proto/iop/*.proto` — 앱 간 메시지 원본 계약 - `proto/iop/*.proto` — 앱 간 메시지 원본 계약
## 유지할 패턴 ## 유지할 패턴
@ -40,16 +40,18 @@
- protobuf 계약 변경은 `proto/iop/*.proto`에서 시작하고 `make proto`로 생성물을 갱신한다. - protobuf 계약 변경은 `proto/iop/*.proto`에서 시작하고 `make proto`로 생성물을 갱신한다.
- 생성 파일(`proto/gen/iop/*.pb.go`)은 사람이 직접 편집하지 않는다. - 생성 파일(`proto/gen/iop/*.pb.go`)은 사람이 직접 편집하지 않는다.
- 공통 패키지는 작고 명확한 계약을 유지하고 앱별 정책을 과도하게 끌어올리지 않는다. - 공통 패키지는 작고 명확한 계약을 유지하고 앱별 정책을 과도하게 끌어올리지 않는다.
- `RunRequest`, `ExecutionSpec`, `NodeCommandRequest`, job/history 계열 계약을 변경할 때 내부 실행 용어는 `target`을 우선하고, `model`은 외부 호환 경계인지 확인한다.
## 다른 도메인과의 경계 ## 다른 도메인과의 경계
- **node**: node가 필요로 하는 설정/타입/계약을 제공하지만 실행 파이프라인의 소유자는 node이다. - **node**: node가 필요로 하는 설정/타입/계약을 제공하지만 실행 파이프라인의 소유자는 node이다.
- **edge**: edge가 필요로 하는 설정/관측성/protobuf 계약을 제공하지만 gateway 동작의 소유자는 edge이다. - **edge**: edge가 필요로 하는 설정/관측성/protobuf 계약을 제공하지만 실행 그룹 제어와 node registry 동작의 소유자는 edge이다.
- **control-plane/worker 후보**: 두 앱이 구현되면 필요한 공통 타입만 이 영역으로 승격하고 앱 내부 책임은 각 도메인에 둔다. - **control-plane/worker 후보**: 두 앱이 구현되면 필요한 공통 타입만 이 영역으로 승격하고 앱 내부 책임은 각 도메인에 둔다.
## 금지 사항 ## 금지 사항
- `packages`에서 `apps/*/internal` 패키지를 import하지 않는다. - `packages`에서 `apps/*/internal` 패키지를 import하지 않는다.
- 앱 하나만을 위한 임시 타입을 충분한 근거 없이 공통 패키지로 승격하지 않는다. - 앱 하나만을 위한 임시 타입을 충분한 근거 없이 공통 패키지로 승격하지 않는다.
- 내부 실행 계약을 확장하면서 `model` 중심 명명을 되살리지 않는다. 외부 API 호환이 필요한 경우 경계와 변환 위치를 명시한다.
- protobuf 생성물을 직접 수정하지 않는다. - protobuf 생성물을 직접 수정하지 않는다.
- 설정 파일만 바꾸고 `packages/config`의 로딩/default와 불일치하게 두지 않는다. - 설정 파일만 바꾸고 `packages/config`의 로딩/default와 불일치하게 두지 않는다.

View file

@ -7,15 +7,16 @@
## 프로젝트 개요 ## 프로젝트 개요
- IOP(Inference Operations Platform)는 분산 AI 모델 추론 워크로드를 처리하는 Go 모노레포이다. - IOP(Inference Operations Platform)는 Control Plane - Edge - Node 계층 구조를 기반으로 모델 서빙과 CLI Agent/Automation 실행을 함께 다루는 Go 실행 오케스트레이션 모노레포이다.
- 현재 1차 구현 중심은 `apps/node`이며, `apps/edge`는 node 등록/레지스트리/transport가 일부 구현되어 있다. - 내부 실행 개념은 model 중심이 아니라 `adapter + target` 중심으로 정리한다. 외부 OpenAI-compatible 경계나 외부 CLI 인자에서는 호환성을 위해 `model` 표현이 남을 수 있다.
- 현재 1차 구현 중심은 `apps/node``apps/edge`의 Edge-Node 실행 스켈레톤이며, CLI adapter와 node 등록/레지스트리/transport가 우선 검증되고 있다.
- `apps/control-plane``apps/worker`는 README와 CLI placeholder 수준이므로, 본격 구현 전 별도 domain rule을 만들거나 갱신한다. - `apps/control-plane``apps/worker`는 README와 CLI placeholder 수준이므로, 본격 구현 전 별도 domain rule을 만들거나 갱신한다.
## 주요 구조 ## 주요 구조
- `apps/node/`디바이스별 노드 에이전트. 런타임 라우팅, 어댑터 실행, edge 연결, 실행 이력 저장을 담당한다. - `apps/node/`Edge에 연결되는 실행자. 런타임 라우팅, adapter execution, CLI/model runtime 실행, 현재 단계의 로컬 실행 이력 저장을 담당한다.
- `apps/edge/`node 연결을 받아 token 기반 등록을 처리하고 중앙 설정을 내려주는 gateway 서버 영역이다. - `apps/edge/`여러 Node를 묶는 백엔드 실행 그룹 컨트롤러. token 기반 등록, node registry, node 설정 전달, routing, stream relay를 담당한다.
- `apps/control-plane/`노드 등록, 정책, 스케줄링 예정 영역이다. 현재 placeholder이다. - `apps/control-plane/`향후 여러 Edge를 연결하고 상태 조회/설정 변경/명령 전달/이벤트 수신/프론트 페이지 제공을 담당할 중앙 관리 계층이다. 현재 placeholder이다.
- `apps/worker/` — 비동기 작업 처리 예정 영역이다. 현재 placeholder이다. - `apps/worker/` — 비동기 작업 처리 예정 영역이다. 현재 placeholder이다.
- `packages/` — 설정, 인증, 정책, 메타데이터, 작업, 관측성, 버전 등 공통 패키지이다. - `packages/` — 설정, 인증, 정책, 메타데이터, 작업, 관측성, 버전 등 공통 패키지이다.
- `proto/iop/` — IOP 메시지 계약 원본이다. - `proto/iop/` — IOP 메시지 계약 원본이다.
@ -39,11 +40,13 @@
- 기존 hexagonal 구조를 유지한다. 특히 `apps/node/internal/runtime` 인터페이스를 중심에 두고 transport/adapters/store는 바깥쪽 구현으로 둔다. - 기존 hexagonal 구조를 유지한다. 특히 `apps/node/internal/runtime` 인터페이스를 중심에 두고 transport/adapters/store는 바깥쪽 구현으로 둔다.
- 새 node 어댑터는 `runtime.Adapter`를 구현하고 `apps/node/internal/bootstrap/module.go`에서 registry에 등록한다. - 새 node 어댑터는 `runtime.Adapter`를 구현하고 `apps/node/internal/bootstrap/module.go`에서 registry에 등록한다.
- 내부 실행 요청과 상태 저장에서는 `adapter`, `target`, `execution` 용어를 우선한다. `model`은 외부 API 호환이나 legacy placeholder일 때만 허용한다.
- Control Plane은 Node를 직접 연결/스케줄링하지 않고 Edge를 통해 시스템을 제어한다. Edge는 자신의 로컬 런타임 상태와 Node registry를 소유한다.
- 내부 통신은 TCP 기반 protobuf 메시지 흐름을 우선한다. gRPC 도입, WebSocket 기본 transport 전환, actor/FSM/plugin framework 도입은 금지한다. - 내부 통신은 TCP 기반 protobuf 메시지 흐름을 우선한다. gRPC 도입, WebSocket 기본 transport 전환, actor/FSM/plugin framework 도입은 금지한다.
- protobuf 계약 변경 시 `proto/iop/*.proto`를 먼저 수정하고 `make proto``proto/gen/iop/*.pb.go`를 갱신한다. 생성 파일은 직접 수정하지 않는다. - protobuf 계약 변경 시 `proto/iop/*.proto`를 먼저 수정하고 `make proto``proto/gen/iop/*.pb.go`를 갱신한다. 생성 파일은 직접 수정하지 않는다.
- 앱 설정 구조 변경 시 `packages/config`의 struct/default와 `configs/*.yaml` 예시를 함께 확인한다. - 앱 설정 구조 변경 시 `packages/config`의 struct/default와 `configs/*.yaml` 예시를 함께 확인한다.
- 테스트는 변경 범위에 맞춰 `go test ./...` 또는 대상 패키지 테스트를 실행한다. - 테스트는 변경 범위에 맞춰 `go test ./...` 또는 대상 패키지 테스트를 실행한다.
- `packages/protocol`은 README에 언급되어 있지만 현재 디렉터리가 없으므로, 생성이 필요하면 실제 책임 경계를 먼저 확정한다. - 상세 DB schema, event schema, permission/policy/audit model, federation, mTLS 구현 세부, Control Plane UI 세부 기획은 각 작업에서 별도로 결정한다.
## 도메인 매핑 ## 도메인 매핑
@ -57,7 +60,7 @@
## 도메인 후보 ## 도메인 후보
- `control-plane`: `apps/control-plane/**`가 placeholder를 넘어 노드 등록, 정책, 스케줄링을 구현하기 시작할 때 생성한다. - `control-plane`: `apps/control-plane/**`가 placeholder를 넘어 여러 Edge 연결 관리, Edge 상태 조회, Edge 설정 변경, Edge 명령 전달, 이벤트 수신, 프론트 페이지 제공을 구현하기 시작할 때 생성한다.
- `worker`: `apps/worker/**`가 placeholder를 넘어 작업 큐 소비/재시도/결과 저장을 구현하기 시작할 때 생성한다. - `worker`: `apps/worker/**`가 placeholder를 넘어 작업 큐 소비/재시도/결과 저장을 구현하기 시작할 때 생성한다.
## 스킬 라우팅 ## 스킬 라우팅

View file

@ -1,12 +1,17 @@
# control-plane — Control Plane # control-plane — Control Plane
노드 등록, 정책 적용, 작업 스케줄링을 담당한다. 여러 Edge를 연결하고 관찰하며, Edge 설정 변경과 명령 전달을 담당할 중앙 관리 계층이다.
**현재 상태: Placeholder** **현재 상태: Placeholder**
현재 Control Plane은 구현 전 단계다. Node를 직접 등록하거나 직접 스케줄링하는 계층으로 확정하지 않는다. Control Plane은 Edge를 통해 시스템을 제어하고, Edge가 자신의 로컬 런타임 상태와 Node registry를 운영하는 방향을 따른다.
## 계획된 기능 ## 계획된 기능
- 노드 등록 및 헬스체크 - 자체 서버와 프론트 페이지 제공
- 정책 DSL 평가 - 여러 Edge 연결 관리
- 작업 스케줄링 (라운드로빈 / 최소 부하) - Edge 상태 조회
- 노드 드레이닝 / 업그레이드 조율 - Edge 설정 변경
- Edge 명령 전달
- Edge 이벤트 수신
- Runtime / Automation 영역을 나눠 보여주는 운영 화면

View file

@ -17,11 +17,12 @@ func main() {
func rootCmd() *cobra.Command { func rootCmd() *cobra.Command {
root := &cobra.Command{ root := &cobra.Command{
Use: "control-plane", Use: "control-plane",
Short: "IOP Control Plane — node registry, policy, scheduling (placeholder)", Short: "IOP Control Plane — multi-edge management placeholder",
Long: `control-plane manages node registration, enforces policy, Long: `control-plane will manage Edge connections, observe Edge state,
and schedules jobs to nodes. adjust Edge configuration, deliver commands to Edge, and receive Edge events.
This component is a placeholder. Implementation is planned after the node is stable.`, This component is a placeholder. It should control the system through Edge,
not by connecting to or scheduling Node directly.`,
} }
root.AddCommand(&cobra.Command{ root.AddCommand(&cobra.Command{
Use: "serve", Use: "serve",

View file

@ -2,92 +2,81 @@
## 개요 ## 개요
IOP(Inference Operations Platform)는 분산 AI 모델 추론을 위한 플랫폼이다. IOP(Inference Operations Platform)는 모델 서빙과 CLI Agent/Automation 실행을 함께 다루는 실행 오케스트레이션 플랫폼을 지향한다. 단순한 OpenAI-compatible proxy나 모델 라우터가 아니라, Control Plane - Edge - Node 계층으로 로컬 실행 그룹을 관리하는 구조를 목표로 한다.
단일 서비스가 아니라 협력하는 여러 앱으로 구성된다.
``` 현재 구현은 Edge-Node 실행 스켈레톤을 검증하는 단계다. Control Plane과 worker는 placeholder이며, 세부 계약은 후속 작업에서 정리한다.
Client (OpenAI SDK)
┌─────────┐
│ edge │ OpenAI-compatible HTTP API
└────┬────┘
│ IOP TCP + Protobuf + mTLS
┌──────────────────┐
│ control-plane │ 노드 등록 / 정책 / 스케줄링
└────────┬─────────┘
┌─────┴──────┐
▼ ▼
┌──────┐ ┌──────┐ (N개의 node)
│ node │ │ node │
└──┬───┘ └──┬───┘
│ │
┌──┴──┐ ┌──┴──┐
│mock │ │ollama│ 어댑터
│cli │ │vllm │
└─────┘ └──────┘
┌────────┐ ## 계층 구조
│ worker │ 비동기 작업 처리
└────────┘ ```text
Control Plane
├─ Edge Group A
│ ├─ Node 1
│ │ ├─ adapter: cli
│ │ └─ adapter: ollama
│ └─ Node 2
│ └─ adapter: vllm
└─ Edge Group B
└─ Node 3
└─ adapter: cli
``` ```
## 내부 통신 프로토콜 Control Plane은 Node에 직접 연결하지 않는다. Control Plane은 Edge 연결을 관리하고, Edge 상태를 조회하고, Edge 설정을 조정하고, Edge에 명령을 전달하는 중앙 관리 계층이다.
### 와이어 포맷 Edge는 여러 Node를 묶는 실행 그룹 컨트롤러다. Edge는 Node registry, adapter/profile configuration, routing, stream relay, session handling, event aggregation을 담당한다.
``` Node는 실제 실행자다. Node는 Edge에 연결되어 adapter execution을 수행하고, RuntimeEvent를 Edge로 돌려준다.
Frame: [4 bytes big-endian uint32: length] [payload]
Payload: JSON-encoded Envelope ## 현재 Edge-Node 흐름
Envelope.Payload: JSON-encoded inner message
현재 검증 중인 기본 흐름은 다음과 같다.
```text
Edge
└─ accepts Node connection
└─ validates token
└─ sends NodeConfigPayload
└─ relays RunRequest / NodeCommandRequest
Node
└─ receives adapter/runtime config
└─ builds adapter registry
└─ resolves adapter + target
└─ executes adapter
└─ emits RuntimeEvent
``` ```
**TODO**: Payload 인코딩을 protobuf로 교체 (proto/iop/runtime.proto 참고) 현재 실행 이력은 Node의 local SQLite store에서 검증 중이다. Edge 단위 이력 집계와 로컬 실행 그룹 상태 소유권은 로드맵에 따라 정리한다.
### 메시지 타입 ## 내부 실행 모델
| Type | 방향 | 설명 | 내부 실행은 `adapter + target`으로 표현한다.
|------|------|------|
| `run.request` | client → node | 추론 실행 요청 |
| `run.event` | node → client | 스트리밍 이벤트 (start/delta/complete/error) |
| `heartbeat` | 양방향 | 연결 유지 (30초 간격) |
| `cancel.request` | client → node | 실행 중단 요청 |
| `register.request` | node → edge | 사전 발급 token 기반 등록 요청 |
| `register.response` | edge → node | 등록 수락 여부와 중앙 관리 node 설정 응답 |
| `error` | node → client | 에러 응답 |
### mTLS - `adapter`: 실행 방식. 예: `mock`, `ollama`, `vllm`, `cli`
- `target`: adapter 안의 실행 대상. 예: 모델 이름, CLI profile, agent/toolchain
- `execution`: 특정 adapter와 target으로 수행되는 단일 실행
```yaml 외부 OpenAI-compatible API에서는 호환성을 위해 `model` 필드가 남을 수 있다. 그러나 내부 실행 계약에서는 `target`을 우선한다.
tls:
enabled: true
cert: certs/node.crt
key: certs/node.key
ca: certs/ca.crt
```
`tls.enabled: true`로 설정하면 RequireAndVerifyClientCert (mTLS) 모드로 동작.
## apps/node 내부 구조 ## apps/node 내부 구조
``` ```text
Hexagonal Architecture Hexagonal Architecture
┌───────────────────────────────┐ ┌───────────────────────────────┐
│ node.Node │ (application core) │ node.Node │ application core
│ │ │ │
TCP │ transport.Handler interface │ TCP │ transport.Handler interface │
─────────► OnRunRequest() │ ─────────► OnRunRequest() │
client │ OnCancel() │ edge │ OnCancel() │
│ OnCommandRequest() │
└───────┬───────────────────────┘ └───────┬───────────────────────┘
router.Resolve() ExecutionSpec router.Resolve() -> ExecutionSpec
adapter.Execute() RuntimeEvent stream adapter.Execute() -> RuntimeEvent stream
store.CompleteRun() SQLite store.CompleteRun() -> SQLite
``` ```
### 어댑터 패턴 ### 어댑터 패턴
@ -100,10 +89,14 @@ type Adapter interface {
} }
``` ```
새 어댑터를 추가하려면: 새 어댑터를 추가할 때는 `runtime.Adapter`를 구현하고 registry에 등록한다. CLI Agent, 모델 런타임, 도구 실행은 모두 같은 adapter execution 경로로 다루는 방향을 유지한다.
1. `apps/node/internal/adapters/<name>/` 패키지 생성
2. `runtime.Adapter` 인터페이스 구현 ## 통신 방향
3. `bootstrap/module.go`에서 Registry에 등록
- Edge-Node 내부 통신은 TCP + protobuf 기반 소켓 흐름을 우선한다.
- Node는 Edge에 연결한다.
- Control Plane은 향후 Edge와 소켓 기반 연결을 맺는다.
- Control Plane이 Node에 직접 연결하거나 매 요청마다 Node를 직접 스케줄링하는 구조는 목표가 아니다.
## 라이브러리 선택 ## 라이브러리 선택
@ -113,9 +106,9 @@ type Adapter interface {
| `go.uber.org/zap` | 구조화 로깅 | | `go.uber.org/zap` | 구조화 로깅 |
| `github.com/spf13/cobra` | CLI | | `github.com/spf13/cobra` | CLI |
| `github.com/spf13/viper` | 설정 파일 | | `github.com/spf13/viper` | 설정 파일 |
| `google.golang.org/protobuf` | Protobuf (spec용) | | `google.golang.org/protobuf` | Protobuf |
| `github.com/prometheus/client_golang` | 메트릭 | | `github.com/prometheus/client_golang` | 메트릭 |
| `modernc.org/sqlite` | 실행 이력 저장 | | `modernc.org/sqlite` | 현재 Node local 실행 이력 저장 |
## 금지 사항 ## 금지 사항

View file

@ -18,7 +18,7 @@ type Job struct {
RunID string `json:"run_id"` RunID string `json:"run_id"`
NodeID string `json:"node_id"` NodeID string `json:"node_id"`
Adapter string `json:"adapter"` Adapter string `json:"adapter"`
Model string `json:"model"` Target string `json:"target"`
Status Status `json:"status"` Status Status `json:"status"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
StartedAt *time.Time `json:"started_at,omitempty"` StartedAt *time.Time `json:"started_at,omitempty"`

View file

@ -8,9 +8,9 @@ type Spec map[string]any
// Engine applies and validates policies on execution specs. // Engine applies and validates policies on execution specs.
type Engine interface { type Engine interface {
// Apply enriches or modifies spec fields based on policy rules. // Apply enriches or modifies spec fields based on policy rules.
Apply(ctx context.Context, adapter, model string, policy Spec) (Spec, error) Apply(ctx context.Context, adapter, target string, policy Spec) (Spec, error)
// Validate returns an error if the policy rejects the request. // Validate returns an error if the policy rejects the request.
Validate(ctx context.Context, adapter, model string, policy Spec) error Validate(ctx context.Context, adapter, target string, policy Spec) error
} }
// Passthrough is a no-op policy engine used when no policy is configured. // Passthrough is a no-op policy engine used when no policy is configured.

View file

@ -21,7 +21,7 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// PolicyRule defines a routing/resource constraint. // PolicyRule is a placeholder for future edge-level routing/resource constraints.
type PolicyRule struct { type PolicyRule struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
@ -82,7 +82,8 @@ func (x *PolicyRule) GetParams() map[string]string {
return nil return nil
} }
// ScheduleRequest asks the control-plane to route a job to a node. // ScheduleRequest is a legacy placeholder for future Control Plane orchestration.
// It must not be treated as an active contract for direct Node scheduling.
type ScheduleRequest struct { type ScheduleRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
JobId string `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` JobId string `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"`
@ -151,7 +152,8 @@ func (x *ScheduleRequest) GetMetadata() map[string]string {
return nil return nil
} }
// ScheduleResponse contains the selected node address. // ScheduleResponse is a legacy placeholder. Future scheduling should be
// reworked around Edge-owned runtime state instead of bypassing Edge.
type ScheduleResponse struct { type ScheduleResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"`

View file

@ -85,7 +85,7 @@ type Job struct {
RunId string `protobuf:"bytes,2,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"` RunId string `protobuf:"bytes,2,opt,name=run_id,json=runId,proto3" json:"run_id,omitempty"`
NodeId string `protobuf:"bytes,3,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` NodeId string `protobuf:"bytes,3,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"`
Adapter string `protobuf:"bytes,4,opt,name=adapter,proto3" json:"adapter,omitempty"` Adapter string `protobuf:"bytes,4,opt,name=adapter,proto3" json:"adapter,omitempty"`
Model string `protobuf:"bytes,5,opt,name=model,proto3" json:"model,omitempty"` Target string `protobuf:"bytes,5,opt,name=target,proto3" json:"target,omitempty"`
Status JobStatus `protobuf:"varint,6,opt,name=status,proto3,enum=iop.JobStatus" json:"status,omitempty"` Status JobStatus `protobuf:"varint,6,opt,name=status,proto3,enum=iop.JobStatus" json:"status,omitempty"`
CreatedAt int64 `protobuf:"varint,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` CreatedAt int64 `protobuf:"varint,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
StartedAt int64 `protobuf:"varint,8,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` StartedAt int64 `protobuf:"varint,8,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"`
@ -154,9 +154,9 @@ func (x *Job) GetAdapter() string {
return "" return ""
} }
func (x *Job) GetModel() string { func (x *Job) GetTarget() string {
if x != nil { if x != nil {
return x.Model return x.Target
} }
return "" return ""
} }
@ -319,13 +319,13 @@ var File_proto_iop_job_proto protoreflect.FileDescriptor
const file_proto_iop_job_proto_rawDesc = "" + const file_proto_iop_job_proto_rawDesc = "" +
"\n" + "\n" +
"\x13proto/iop/job.proto\x12\x03iop\"\x8a\x03\n" + "\x13proto/iop/job.proto\x12\x03iop\"\x8c\x03\n" +
"\x03Job\x12\x15\n" + "\x03Job\x12\x15\n" +
"\x06job_id\x18\x01 \x01(\tR\x05jobId\x12\x15\n" + "\x06job_id\x18\x01 \x01(\tR\x05jobId\x12\x15\n" +
"\x06run_id\x18\x02 \x01(\tR\x05runId\x12\x17\n" + "\x06run_id\x18\x02 \x01(\tR\x05runId\x12\x17\n" +
"\anode_id\x18\x03 \x01(\tR\x06nodeId\x12\x18\n" + "\anode_id\x18\x03 \x01(\tR\x06nodeId\x12\x18\n" +
"\aadapter\x18\x04 \x01(\tR\aadapter\x12\x14\n" + "\aadapter\x18\x04 \x01(\tR\aadapter\x12\x16\n" +
"\x05model\x18\x05 \x01(\tR\x05model\x12&\n" + "\x06target\x18\x05 \x01(\tR\x06target\x12&\n" +
"\x06status\x18\x06 \x01(\x0e2\x0e.iop.JobStatusR\x06status\x12\x1d\n" + "\x06status\x18\x06 \x01(\x0e2\x0e.iop.JobStatusR\x06status\x12\x1d\n" +
"\n" + "\n" +
"created_at\x18\a \x01(\x03R\tcreatedAt\x12\x1d\n" + "created_at\x18\a \x01(\x03R\tcreatedAt\x12\x1d\n" +

View file

@ -73,7 +73,9 @@ func (NodeStatus) EnumDescriptor() ([]byte, []int) {
return file_proto_iop_node_proto_rawDescGZIP(), []int{0} return file_proto_iop_node_proto_rawDescGZIP(), []int{0}
} }
// NodeInfo describes a registered node. // NodeInfo is a legacy placeholder for node snapshots.
// Current Edge-Node registration uses RegisterRequest/RegisterResponse in
// runtime.proto, and Control Plane should observe nodes through Edge.
type NodeInfo struct { type NodeInfo struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"`
@ -158,7 +160,8 @@ func (x *NodeInfo) GetLabels() map[string]string {
return nil return nil
} }
// NodeRegisterRequest is sent to the control-plane on startup. // NodeRegisterRequest is a legacy placeholder and is not the active startup
// contract. Nodes connect to Edge, not directly to Control Plane.
type NodeRegisterRequest struct { type NodeRegisterRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Info *NodeInfo `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"` Info *NodeInfo `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"`
@ -203,7 +206,8 @@ func (x *NodeRegisterRequest) GetInfo() *NodeInfo {
return nil return nil
} }
// NodeRegisterResponse is returned by the control-plane. // NodeRegisterResponse is a legacy placeholder retained until the
// Control Plane contracts are redesigned around Edge.
type NodeRegisterResponse struct { type NodeRegisterResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Accepted bool `protobuf:"varint,1,opt,name=accepted,proto3" json:"accepted,omitempty"` Accepted bool `protobuf:"varint,1,opt,name=accepted,proto3" json:"accepted,omitempty"`

View file

@ -4,14 +4,15 @@ package iop;
option go_package = "iop/proto/gen/iop"; option go_package = "iop/proto/gen/iop";
// PolicyRule defines a routing/resource constraint. // PolicyRule is a placeholder for future edge-level routing/resource constraints.
message PolicyRule { message PolicyRule {
string name = 1; string name = 1;
string expression = 2; string expression = 2;
map<string, string> params = 3; map<string, string> params = 3;
} }
// ScheduleRequest asks the control-plane to route a job to a node. // ScheduleRequest is a legacy placeholder for future Control Plane orchestration.
// It must not be treated as an active contract for direct Node scheduling.
message ScheduleRequest { message ScheduleRequest {
string job_id = 1; string job_id = 1;
string target = 2; string target = 2;
@ -19,7 +20,8 @@ message ScheduleRequest {
map<string, string> metadata = 4; map<string, string> metadata = 4;
} }
// ScheduleResponse contains the selected node address. // ScheduleResponse is a legacy placeholder. Future scheduling should be
// reworked around Edge-owned runtime state instead of bypassing Edge.
message ScheduleResponse { message ScheduleResponse {
string node_id = 1; string node_id = 1;
string address = 2; string address = 2;

View file

@ -18,7 +18,7 @@ message Job {
string run_id = 2; string run_id = 2;
string node_id = 3; string node_id = 3;
string adapter = 4; string adapter = 4;
string model = 5; string target = 5;
JobStatus status = 6; JobStatus status = 6;
int64 created_at = 7; int64 created_at = 7;
int64 started_at = 8; int64 started_at = 8;

View file

@ -4,7 +4,9 @@ package iop;
option go_package = "iop/proto/gen/iop"; option go_package = "iop/proto/gen/iop";
// NodeInfo describes a registered node. // NodeInfo is a legacy placeholder for node snapshots.
// Current Edge-Node registration uses RegisterRequest/RegisterResponse in
// runtime.proto, and Control Plane should observe nodes through Edge.
message NodeInfo { message NodeInfo {
string node_id = 1; string node_id = 1;
string name = 2; string name = 2;
@ -21,12 +23,14 @@ enum NodeStatus {
NODE_STATUS_DRAINING = 3; NODE_STATUS_DRAINING = 3;
} }
// NodeRegisterRequest is sent to the control-plane on startup. // NodeRegisterRequest is a legacy placeholder and is not the active startup
// contract. Nodes connect to Edge, not directly to Control Plane.
message NodeRegisterRequest { message NodeRegisterRequest {
NodeInfo info = 1; NodeInfo info = 1;
} }
// NodeRegisterResponse is returned by the control-plane. // NodeRegisterResponse is a legacy placeholder retained until the
// Control Plane contracts are redesigned around Edge.
message NodeRegisterResponse { message NodeRegisterResponse {
bool accepted = 1; bool accepted = 1;
string token = 2; string token = 2;