# Milestone: Edge-local Dev Config와 바이너리 중심 실행 루프

## 위치

- Roadmap: `agent-roadmap/ROADMAP.md`
- Phase: `agent-roadmap/phase/control-plane-portal-ops/PHASE.md`

## 목표

Control Plane이 아직 없는 동안 `edge.yaml`을 개발/테스트용 Control Plane source of truth로 사용한다.
로컬/개발 서버 배포와 smoke test는 helper script나 모델용 skill로 흩뜨리지 않고 `iop-edge` command 표면에 모으며, Node 사용자는 `node.yaml`을 만들거나 편집하지 않고 Edge가 제공하는 bootstrap만 실행한다.
Edge/Node 바이너리는 repo checkout 위치에 묶이지 않고, 바이너리와 같은 폴더의 구조화된 config와 artifact만으로 실행 가능해야 한다.
사용자는 `edge.yaml` 구조를 문서에서 추측하지 않고 `iop-edge`가 생성하는 템플릿과 현재 설정/환경 출력으로 확인할 수 있어야 한다.

## 상태

[완료]

## 승격 조건

- 없음

## 구현 잠금

- 상태: 해제
- 결정 필요: 없음

## 범위

- Control Plane 부재 시 사용할 `edge.yaml` 구조를 Edge-local dev/test source of truth로 정리한다.
- `iop-edge`가 바이너리 기준 경로의 `edge.yaml`을 기본 config 후보로 찾고, repo checkout 없이 실행 가능하게 한다.
- Node별 id/alias/token/adapter/runtime 설정은 Edge config의 `nodes[]`에서 관리한다.
- Node 정의와 Node별 adapter/runtime 설정은 사용자가 `edge.yaml`을 직접 편집해 추가하는 것이 아니라 `iop-edge node register <node-id>` 같은 command의 flag/default가 `edge.yaml`을 갱신해 생성한다.
- `iop-edge`는 `edge.yaml` 템플릿 생성과 현재 effective config/environment 출력 command를 제공한다.
- `iop-edge help`와 하위 command help만 보고 config 생성, 환경 확인, Node 등록/부트스트랩 출력, 실행, smoke test 흐름을 발견할 수 있게 한다.
- bootstrap command에 들어갈 advertise host는 설정값이 비어 있으면 host 기준으로 자동 판별하고, 설정값이 있으면 그 값을 따른다.
- 로컬/dev bundle의 기본 로그는 별도 설정이 없으면 바이너리 폴더 아래에 자동 생성되는 log 디렉터리에 남긴다.
- Node host 사용자는 `iop-edge node register ...`가 출력한 bootstrap 명령만 실행하고, `node.yaml` 생성/편집/확인이나 `iop-node serve --config ...` 직접 실행을 기본 경로로 보지 않는다.
- 로컬/개발 서버 셋업, 실행, config 확인, Node register/bootstrap 출력, 기본 smoke test를 `iop-edge` command 중심으로 수렴시킨다.
- 기존 `iop-node` 바이너리는 실행자로 유지하되, 사용자-facing 제어와 배포 루프는 `iop-edge`가 안내하고 조립한다.

## 기능

### Epic: [edge-dev-loop] Edge-local dev loop

Control Plane이 붙기 전까지 Edge 바이너리와 `edge.yaml`만으로 개발 서버 배포와 Edge-Node smoke를 반복 가능하게 만든다.

- [x] [config-discovery] `iop-edge`는 `--config`가 없을 때 repo 상대 `configs/edge.yaml`에 의존하지 않고, 바이너리와 같은 폴더의 `edge.yaml` 같은 배포용 기본 config 후보를 우선 사용할 수 있다. 검증: 임시 폴더에 `iop-edge`와 `edge.yaml`만 둔 상태에서 `iop-edge config check`와 `iop-edge serve`가 동작한다.
- [x] [config-template] `iop-edge`는 배포용 `edge.yaml` 템플릿을 stdout 또는 바이너리 옆 파일로 생성하는 command를 제공한다. 검증: 빈 임시 폴더에서 `iop-edge`만으로 템플릿을 생성하고, 생성된 파일이 `iop-edge config check`를 통과한다.
- [x] [env-report] `iop-edge`는 현재 사용할 config path, binary dir, listen 주소, advertise host 판별 결과, Node transport 주소, artifact/bootstrap 주소, OpenAI-compatible 주소, configured/connected node 요약을 출력할 수 있다. 검증: `iop-edge` 단일 command 출력만 보고 사용자가 Cline 주소와 Node bootstrap 주소를 알 수 있다.
- [x] [advertise-host] `edge.yaml`의 advertise host 기본값은 빈 값이며, 비어 있으면 host 기준 IP/host를 자동 판별하고, 값이 있으면 자동 판별보다 설정값을 우선한다. 검증: 빈 값/명시 값 각각에서 bootstrap command와 environment 출력의 host가 기대대로 달라진다.
- [x] [log-defaults] `iop-edge`는 로컬/dev bundle에서 로그 경로가 비어 있으면 바이너리 폴더 아래의 log 디렉터리를 자동 사용하고, `env`/help 출력에 실제 로그 위치를 보여준다. 검증: clean temp dir에서 실행하면 `<binary-dir>/logs/` 아래에 edge 로그가 남고, 설정값이 있으면 명시 로그 경로를 따른다.
- [x] [edge-source] `edge.yaml`은 Control Plane 없는 개발 단계의 source of truth로서 `nodes[]`에 Node id, alias, token, adapter, runtime 설정을 담는다. 검증: `iop-edge config check`가 `nodes[]` token/id/alias 충돌과 adapter/runtime 설정 오류를 잡는다.
- [x] [node-register] `iop-edge node register <node-id>`는 Node 정의와 Node별 adapter/runtime 설정을 flag/default로 받아 `edge.yaml`의 `nodes[]`에 생성 또는 갱신하고, 실행 직후 해당 Node host에서 사용할 완성된 bootstrap 명령을 출력한다. 검증: 빈 템플릿에서 `iop-edge node register node-silicon-ollama` 실행 후 `edge.yaml`에 node record가 생기고 출력에는 사용자가 실행할 한 줄 bootstrap만 남는다.
- [x] [help-discovery] `iop-edge help`와 관련 하위 command help는 사용자가 문서를 열지 않아도 `config init`, `config check`, environment 출력, Node register/bootstrap 출력, `serve`, smoke test의 순서와 필수/선택 인자를 알 수 있게 설명한다. 검증: `iop-edge help`, `iop-edge config --help`, `iop-edge node --help`, `iop-edge node register --help`, `iop-edge smoke --help` 출력에 이 Milestone의 공식 사용자 경로가 드러난다.
- [x] [edge-commands] Node 등록 관리, bootstrap command 발급, local/dev setup, 실행 전 config 확인, smoke test 진입점은 공식 사용자 경로에서 `iop-edge` command로 제공된다. 검증: 문서와 help output에서 `bin/*`, 별도 dev deploy 바이너리, 모델용 skill이 공식 경로로 등장하지 않는다.
- [x] [node-hidden-config] Node 사용자 UX는 Edge가 제시한 bootstrap 한 줄로 끝나며 `node.yaml` 생성, 편집, 확인 또는 `iop-node serve --config ...` 직접 실행을 요구하지 않는다. 검증: 사용자용 dev/field 가이드에 node config 파일 조작 단계가 없다.
- [x] [bundle-run] Edge/Node dev bundle은 repo checkout 위치와 무관하게 바이너리 옆 config/artifact만으로 실행 가능하다. 검증: clean temp dir 또는 개발 서버 배포 디렉터리에서 Edge 실행, Node bootstrap, Node registration, `/v1/models` smoke가 통과한다.

## 완료 리뷰

- 상태: 승인됨
- 요청일: 2026-05-28
- 완료 근거: 모든 기능 Task와 Task 안의 검증이 충족되었다.
- 완료 근거: `go test ./...`가 통과했고, `apps/edge/cmd/edge` 테스트가 advertise host 기반 bootstrap URL, 하위 help discovery, config/node/smoke 흐름을 검증한다.
- 완료 근거: clean temp bundle에서 field binary build, Edge serve, artifact HTTP, Node bootstrap registration, `/v1/models` 확인이 통과했다.
- 리뷰 필요:
  - [x] 사용자가 완료 결과를 확인했다
  - [x] archive 이동을 승인했다
- 리뷰 코멘트: 2026-05-28 사용자 요청으로 완료 처리하고 archive로 이동했다.

## 범위 제외

- Control Plane server의 실제 Edge config sync/enrollment 구현
- Flutter client 운영 화면 구현
- 별도 `bin/dev-deploy`, 별도 dev deploy 바이너리, 모델용 agent skill을 공식 사용자 경로로 만드는 작업
- Node 사용자에게 `node.yaml` 작성/편집/확인 또는 `iop-node serve --config ...` 직접 실행을 요구하는 UX
- 사용자가 `edge.yaml` 구조를 외부 문서만 보고 손으로 처음부터 작성해야 하는 UX
- 사용자가 Node record를 추가하기 위해 `edge.yaml`의 `nodes[]`를 손으로 작성해야 하는 UX
- Node별 adapter/runtime 설정을 입력하기 위해 사용자가 `edge.yaml`을 직접 편집해야만 하는 UX
- bootstrap 주소나 Cline 주소를 작업자가 수동으로 계산해 전달해야 하는 UX
- production release, signing, long-term credential rotation, mTLS 세부 구현

## 작업 컨텍스트

- 관련 경로: `apps/edge/cmd/edge`, `apps/edge/internal/bootstrap`, `apps/edge/internal/node`, `apps/edge/internal/transport`, `apps/node/cmd/node`, `apps/node/internal/bootstrap`, `packages/config`, `packages/hostsetup`, `configs/edge.yaml`, `docs/deploy-dev.md`, `docs/field-bootstrap-work-guide.md`, `README.md`, `agent-ops/rules/project/rules.md`
- 표준선(선택): README의 Edge 책임 중 `Edge-managed Node bootstrap/configuration`과 `Edge는 자신의 로컬 런타임 상태를 소유한다`는 원칙을 따른다.
- 표준선(선택): Control Plane 부재 개발 단계에서는 `edge.yaml`이 테스트용 Control Plane 역할을 하며, `nodes[]`가 Node별 실행 설정의 원본이다.
- 표준선(선택): Node record와 Node별 adapter/runtime 설정 생성/갱신은 `iop-edge node register <node-id>` 계열 command가 담당하고, 실행 결과로 바로 bootstrap 명령을 출력한다. `edge.yaml` 수동 편집은 고급/비상 경로로만 둔다.
- 표준선(선택): `edge.yaml`의 host/advertise 계열 설정은 빈 값을 기본으로 두고, 비어 있으면 `iop-edge`가 실행 host 기준으로 판별한다. 명시값이 있으면 자동 판별보다 명시값을 따른다.
- 표준선(선택): `iop-edge`는 템플릿 생성, effective environment 출력, bootstrap command 생성을 제공해 사용자가 YAML 구조와 파생 주소를 추측하지 않게 한다.
- 표준선(선택): 로컬/dev bundle의 로그 기본 위치는 `<binary-dir>/logs/`로 두고, 별도 설정이 있으면 명시값을 따른다. systemd 배포에서도 journal만 유일한 확인 경로로 강제하지 않는다.
- 표준선(선택): command help는 구현 후 붙이는 부가 문서가 아니라 사용자 실행 UX의 일부다. 새 command는 짧은 설명, 예시, 필수/선택 인자, 다음 단계 힌트를 help output에 포함한다.
- 표준선(선택): 공식 사용자 UX는 `iop-edge`와 `iop-node` command 표면에 모으되, Node 사용자는 Edge bootstrap만 실행하고 Node config 파일을 다루지 않는다.
- 표준선(선택): helper script가 필요해도 내부 개발 보조로만 두고 공식 로컬/dev 배포 경로로 문서화하지 않는다.
- 선행 작업: Field Bootstrap UX와 테스트 포트 정비
- 후속 작업: Control Plane과 Client, Agent Bootstrap과 OTO 등록, Multi-Edge 운영
- 확인 필요: 없음
