<!-- task=m-control-plane-separation-migration/04+02_bootstrap_command plan=1 tag=REVIEW_API -->

# Plan - REVIEW_API

## 이 파일을 읽는 구현 에이전트에게

이 plan은 code review FAIL 후속 루프다. 아래 Required 이슈만 좁게 수정한다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션만 채우고 active 파일을 그대로 둔다. 사용자에게 직접 질문하거나 선택지를 제시하지 않는다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/independent-control-plane/milestones/control-plane-separation-migration.md`
- Task ids:
  - `bootstrap-command`: OTO Server가 runner bootstrap command를 발급하고, bootstrap script가 `apps/runner` binary/config를 기준으로 runner를 실행하는 흐름이 정의된다.
- Completion mode: check-on-pass

## 범위 결정 근거

- 이전 구현의 서버 endpoint, bootstrap script, proto shape를 유지하되 server-issued command가 실제 script 계약을 만족하도록 보정한다.
- release artifact publishing, checksum signing, package hosting 구현은 계속 제외한다. 다만 서버가 실행 불가능한 command를 발급하지 않도록 release base URL 정책과 테스트 증거를 추가한다.
- command rendering의 shell safety만 다룬다. runner registry 인증 정책이나 enrollment token lifecycle 설계는 이번 후속 범위가 아니다.

## 구현 체크리스트

- [ ] [REVIEW_API-1] server-issued command의 `--release-base-url`이 bootstrap script의 HTTPS 계약을 만족하도록 고치고, endpoint command를 fake curl/tar 기반으로 실제 script에 통과시키는 smoke를 추가한다.
- [ ] [REVIEW_API-2] bootstrap command에 삽입되는 server URL, runner id, enrollment token, release base URL을 shell-safe하게 quote/validate하고 command injection 회귀 테스트를 추가한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [REVIEW_API-1] Release base URL 계약 수정

문제: `services/core/internal/httpserver/server.go:293`이 HTTP `serverURL`을 `--release-base-url`에도 그대로 넣어, `apps/runner/assets/script/shell/oto_agent_bootstrap.sh:82`-`86`의 HTTPS 검증에서 서버 발급 command가 즉시 실패한다.

해결 방법:

- bootstrap command 생성 로직을 helper로 분리하고 release base URL을 명시적으로 결정한다.
- OTO Core가 HTTP로 떠 있는 경우에도 script가 거부할 command를 반환하지 않도록 한다. 예: `OTO_RUNNER_RELEASE_BASE_URL` 같은 명시적 HTTPS override를 우선 사용하고, override가 없을 때는 HTTPS 서버에서만 server URL 기반 release URL을 사용하거나 명확한 5xx/4xx 오류로 command 발급을 거부한다.
- 테스트는 endpoint가 반환한 command에서 인자를 추출하거나 fake `curl`/`tar` fixture를 사용해 script의 HTTPS 검증을 실제로 통과하는지 확인한다.

수정 파일 및 체크리스트:

- [ ] `services/core/internal/httpserver/server.go`
- [ ] `services/core/internal/httpserver/server_test.go`
- [ ] `apps/runner/test/oto_server_connection_smoke_test.dart`
- [ ] 필요 시 `apps/runner/test/oto_agent_bootstrap_script_test.dart`

테스트 작성:

- HTTP OTO Core가 HTTPS release base override 없이 실행 불가능한 command를 발급하지 않는지 검증한다.
- HTTPS release base override가 있으면 response command가 script의 `--release-base-url` 검증을 통과한다는 smoke를 추가한다.

중간 검증:

```bash
cd services/core && go test ./internal/httpserver
cd apps/runner && dart test test/oto_agent_bootstrap_script_test.dart test/oto_server_connection_smoke_test.dart
```

## [REVIEW_API-2] Shell-safe command 렌더링

문제: `services/core/internal/httpserver/server.go:293`이 `r.Host`, `runner_id`, `enrollment_token`을 shell command에 quote/escape 없이 직접 삽입한다.

해결 방법:

- shell command에 들어가는 모든 동적 인자를 POSIX shell-safe single-quote 방식으로 escape하거나, response를 구조화해서 command 문자열 조립 위험을 제거한다.
- Host/server URL은 URL로 파싱 가능한 값만 쓰고, runner id와 token은 command rendering에서 인자 분리나 shell metacharacter 실행이 불가능해야 한다.
- 테스트에는 공백, quote, semicolon, command substitution 문자가 포함된 runner id/token/Host 후보를 넣고 command가 인자를 보존하면서 추가 command를 만들지 않는지 검증한다.

수정 파일 및 체크리스트:

- [ ] `services/core/internal/httpserver/server.go`
- [ ] `services/core/internal/httpserver/server_test.go`

테스트 작성:

- malicious runner id/token이 들어와도 response command가 shell-safe하게 quote되는지 확인한다.
- Host header 또는 server URL 후보의 invalid/malicious 값은 command에 raw 삽입되지 않도록 검증한다.

중간 검증:

```bash
cd services/core && go test ./internal/httpserver
```

## 최종 검증

```bash
cd services/core && go test ./...
cd apps/runner && dart analyze
cd apps/runner && dart test test/oto_agent_migration_plan_test.dart test/oto_agent_bootstrap_script_test.dart test/oto_agent_cli_test.dart test/oto_agent_config_test.dart test/oto_agent_registration_test.dart test/oto_iop_connection_smoke_test.dart test/oto_server_connection_smoke_test.dart
```

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
