# Milestone: Field Bootstrap UX와 테스트 포트 정비

## 위치

- Roadmap: `agent-roadmap/ROADMAP.md`
- Phase: `agent-roadmap/phase/serving-routing-optimization/PHASE.md`

## 목표

field 테스트 환경을 Jenkins agent 연결처럼 매끄럽게 만든다.
code-server 컨테이너는 외부 테스트용 web/bootstrap, Node transport, OTO/specialized agent 포트를 안정적으로 노출하고, Edge는 Node 빌드 산출물을 정해진 위치에 준비해 사용자가 한 줄 bootstrap 명령만 실행하면 다운로드, 설정 생성, Edge 접속까지 이어지게 한다.

## 상태

[완료]

## 승격 조건

- 없음

## 구현 잠금

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

## 범위

- `ssh toki@toki-labs.com`의 `~/docker/services/code-server/compose/docker-compose.yml`에서 code-server 컨테이너 외부 테스트 포트 노출을 정리한다.
- IOP field 테스트용 포트를 web/bootstrap HTTP, Node Edge transport, OTO/specialized agent transport 용도로 분리해 재부팅 후 바로 사용할 수 있게 한다.
- Edge 쪽 build artifact 위치와 URL 규칙을 정하고, Apple Silicon Node를 우선 대상으로 한 one-line bootstrap UX를 만든다.
- bootstrap 명령은 바이너리 다운로드, checksum 또는 버전 확인, 임시 config 생성, `iop-node serve` 실행까지 한 줄 수준으로 이어지는 것을 목표로 한다.
- 사용자에게 보이는 bootstrap 명령은 artifact/bootstrap URL이 완성된 상태로 제공되어야 하며, 사용자가 직접 바꾸는 값은 token 값 하나뿐이다. `IOP_*=` 같은 named parameter는 사용자 기본 경로에 두지 않는다.
- 이번 Milestone은 테스트 환경과 bootstrap UX를 다루며, Control Plane 기반 장기 enrollment/credential 관리와 OTO agent 본격 구현은 후속 Phase/Milestone에서 다룬다.

## 기능

### Epic: [field-bootstrap] Field Bootstrap and Port Readiness

field 테스트를 매번 수동 터널과 임시 명령 조합으로 만들지 않고, 재현 가능한 외부 포트와 한 줄 bootstrap 명령으로 수렴시킨다.

- [x] [compose-ports] code-server compose가 field 테스트용 web/bootstrap, Node transport, OTO/specialized agent 포트를 외부에 노출한다. 검증: `docker compose config`에서 `18080`, `18081`, `19090`, `19190` 계열 포트 매핑이 확인된다.
- [x] [artifact-layout] Node field build 산출물의 고정 위치와 URL 규칙이 정리되어 있다. 검증: Apple Silicon용 `iop-node`를 정해진 URL에서 다운로드할 수 있다.
- [x] [one-line-node] 사용자가 token 값 하나만 넣은 한 줄 명령으로 Apple Silicon Node를 다운로드하고 Edge에 접속할 수 있다. 검증: Mac 테스트 환경에서 `curl -fsSL <완성된-bootstrap-url> | bash -s <token>` 형태의 command 한 줄 실행 후 Edge에서 node registration을 확인한다.
- [x] [serve-lifecycle] `edge serve`가 field bootstrap에 사용할 node/openai listener를 안정적으로 유지한다. 검증: `edge serve --config <field-config>` 실행 후 Node registration과 `/v1/models` 호출이 통과한다.
- [x] [runbook] field bootstrap runbook에 직접 포트 방식과 터널 fallback 방식이 구분되어 있다.
- [x] [llm-test-runbook] LLM/작업자가 사용자 직접 테스트를 준비하고 실패 지점을 분리할 수 있는 상세 runbook이 문서화되어 있다. 검증: `docs/deploy-dev.md`에 포트 확인, Edge config, artifact 생성, HTTP 노출, Mac one-line bootstrap, OpenAI-compatible 확인, 실패 시 확인 순서가 있다.
- [x] [user-test-guide] 사용자가 field bootstrap을 직접 테스트할 수 있는 짧은 가이드가 분리되어 있다. 검증: `docs/field-bootstrap-user-test.md`에 Mac 준비, one-line bootstrap, 호출 확인, 문제 발생 시 공유할 출력이 정리되어 있다.

## 완료 리뷰

- 상태: 승인됨
- 요청일: 2026-05-27
- 승인일: 2026-05-27
- 완료 근거: code-server field 포트, Apple Silicon artifact/bootstrap, Mac one-line Node 연결, Edge node registration, `/v1/models`, OpenAI-compatible Cline streaming delta 호출까지 실테스트로 확인했다.
- 리뷰 필요:
  - [x] 사용자가 완료 결과를 확인했다
  - [x] archive 이동을 승인했다
- 리뷰 코멘트: 사용자가 완료와 archive 이동을 승인했다.

## 범위 제외

- Control Plane 기반 장기 agent enrollment/credential 구현
- OTO agent 본격 구현
- cloud fallback, 자동 부하 라우팅, 품질 평가 feedback 구현
- 외부 공개 도메인/인증 정책의 장기 확정

## 작업 컨텍스트

- 관련 경로: `docs/deploy-dev.md`, `docs/field-bootstrap-work-guide.md`, `bin/build/field-binaries.sh`, `bin/edge.sh`, `apps/edge/cmd/edge`, `apps/edge/internal/bootstrap`, `apps/edge/internal/input`, `apps/edge/internal/transport`, 원격 `~/docker/services/code-server/compose/docker-compose.yml`
- 표준선(선택): Edge-Node transport는 TCP proto-socket을 유지하고, HTTP는 artifact/bootstrap과 OpenAI-compatible 입력 표면에만 사용한다.
- 표준선(선택): 외부 테스트 포트는 기존 code-server compose의 안전 remap 관례를 따라 host 충돌 포트를 피하고, 재부팅 타이밍은 사용자가 결정한다.
- 표준선(선택): bootstrap UX는 Jenkins agent처럼 Edge가 command를 제시하고 대상 host가 실행하는 흐름을 목표로 하되, 초기 field 단계에서는 static artifact URL과 one-time-ish token/config로 시작한다.
- 표준선(선택): 사용자용 bootstrap command는 `curl -fsSL <완성된-bootstrap-url> | bash -s <token>` 형태를 기준으로 한다. artifact URL은 작업자/Edge가 완성해 제공하고, 사용자가 치환하는 값은 token 하나만 둔다.
- 선행 작업: Ollama 실테스트와 후속 안정화
- 진행 기록: 원격 `~/docker/services/code-server/compose/docker-compose.yml`에 `18080:18080`, `18081:8080`, `19190:9190`을 추가하고 기존 `19090:9090`, `19092:9092`를 역할 주석으로 정리했다. `/usr/local/bin/docker compose config --quiet` 검증은 통과했으며, 실제 포트 적용은 사용자가 정한 code-server compose 재기동 시점 이후 확인한다.
- 진행 기록: `agent-ops/rules/project/rules.md`에서 field 테스트 환경 작업을 `docs/deploy-dev.md`와 이 Milestone으로 라우팅하도록 보강했고, `docs/deploy-dev.md`에 code-server compose 원본, 기준 포트, 직접 포트/터널 fallback, Apple Silicon `darwin/arm64` 기준을 기록했다.
- 진행 기록: `VERSION=field-check APPS=node TARGETS="darwin/arm64" ... DIST_DIR=/tmp/... ./bin/build/field-binaries.sh`로 Apple Silicon용 `iop-node`, `iop-node.sha256`, `SHA256SUMS`, `manifest.env`, `bootstrap/node-darwin-arm64.sh` 생성과 layout을 확인했다.
- 진행 기록: `docs/deploy-dev.md`의 상세 절차를 LLM/작업자용 runbook으로 명명하고, 사용자가 직접 볼 짧은 가이드를 `docs/field-bootstrap-user-test.md`로 분리했다.
- 계획 작성: `agent-task/m-field-bootstrap-test-port-readiness/01_serve_lifecycle/PLAN-cloud-G07.md`에서 `edge serve` listener 유지와 field-like Node registration, `/v1/models` 검증을 선행 작업으로 분리했다.
- 계획 작성: `agent-task/m-field-bootstrap-test-port-readiness/02+01_one_line_node/PLAN-cloud-G07.md`에서 Apple Silicon one-line bootstrap 실기기 등록 검증을 `01_serve_lifecycle` 의존 작업으로 분리했다.
- 후속 작업: Agent Bootstrap과 OTO 등록, Control Plane 기반 enrollment
- 진행 기록: Mac에서 bootstrap artifact 다운로드와 Node 재접속을 반복 확인했고, Edge 로그에서 `node-silicon-ollama` registration, `/v1/models`, OpenAI-compatible streaming delta, Cline XML tool protocol 통과를 확인했다.
- 완료 기록: 사용자 확인 후 archive 이동 승인을 받았다.

## 아카이브 후 참고 메모

- 사용자 정정 사항: Edge-Node 실사용 연결에 외부 공개가 필요한 포트는 `19090` 하나다. `18080`은 bootstrap shell script, `iop-node` binary, checksum을 내려받는 artifact HTTP 포트일 뿐이며 Edge-Node 런타임 포트가 아니다.
- 최종 방향: 외부 bootstrap/artifact 접근은 `toki-labs.com:18080`을 기준으로 유지한다. Node 사용자는 별도 설정 파일을 만들거나 관리하지 않고, bootstrap 내부에서 `toki-labs.com:19090`으로 연결되게 한다.
- 다음 테스트에서는 `curl http://toki-labs.com:18080/... | bash -s <token>` 형태로 bootstrap을 받고, script 기본값으로 `toki-labs.com:19090`을 굽는다.
- 사용자용 명령은 반드시 artifact URL이 완성된 `curl -fsSL <완성된-bootstrap-url> | bash -s <token>` 형태로 제공한다. 사용자가 직접 넣는 값은 token 하나뿐이며 `IOP_ARTIFACT_BASE_URL=...`, `IOP_EDGE_ADDR=...`, `IOP_NODE_TOKEN=...` 같은 named parameter를 요구하지 않는다.
- 완료 테스트 스냅샷: live artifact version은 `field-live-20260527-19ddd9bd38b7`, node id는 `node-silicon-ollama`, model은 `gemma4:26b`였다. 테스트 token은 임시값이므로 재사용하지 않는다.
- 재테스트 시 먼저 stale process를 확인한다: `ps -ef | rg 'python3 -m http.server 18080|go run ./apps/edge/cmd/edge|/tmp/go-build.*/edge serve|iop-edge serve'`. 필요하면 임시 edge/artifact server를 정리하고 새 설정으로 다시 띄운다.
- 권장 재테스트 형태: 작업자 내부 빌드 설정에서 artifact 주소는 `http://toki-labs.com:18080`, Edge runtime 주소는 `toki-labs.com:19090`으로 굽는다. 사용자에게 주는 실행 명령은 `curl -fsSL http://toki-labs.com:18080/bootstrap/node-darwin-arm64.sh | bash -s <token>` 형태로 제공하고, token은 명령 마지막 positional value 하나로만 받는다.
- 작업 가이드 문서: `docs/field-bootstrap-work-guide.md`
