<a id="http-contract"></a>
# HTTP API와 Web UI 계약

Go `http.ServeMux`가 `/api/*`를 등록하고 JWT/CORS middleware를 씌우며, 나머지 `/`는 embedded Next.js static UI를 제공한다. frontend client는 일반 fetch에 Bearer header를, native `EventSource`에는 query `token`을 사용한다. 서버의 `extractToken`은 HTTP method나 route를 구분하지 않아 보호 API 전반에서 query token도 fallback으로 수락한다. [route 근거](evidence:http-routes) [client 근거](evidence:web-api)

<a id="auth-contract"></a>
## 인증 계약

`/api/auth/*`와 `/api/health`는 공통 JWT middleware에서 면제된다. `change-password`는 경로가 면제되므로 handler 안에서 token과 기존 암호를 다시 확인한다. 나머지 256개 operation은 middleware가 JWT를 요구하며, `change-password`까지 합친 257개 operation에서 `extractToken`이 Bearer header → `artex_token` cookie → query `token` 순으로 값을 고른다. query fallback은 SSE에 제한되지 않는다. 발급 token은 HS256·7일 expiry·subject `ARTEX`지만, 검증 코드는 HMAC signing method 전체를 허용해 같은 key의 HS384/HS512도 수락하고 `exp/sub/iss/aud`를 필수 claim으로 요구하지 않는다. [auth 근거](evidence:auth)

정적 UI 자체는 public이고 client-side auth route가 화면을 제어한다. 권한 경계는 API middleware다. token은 frontend에서 localStorage와 JavaScript가 쓰는 `SameSite=Lax` cookie에 기록된다. cookie에는 `HttpOnly`와 `Secure`가 없으므로 browser script에서 읽을 수 있고 HTTP에서도 전송될 수 있다. query token은 SSE 외의 보호 API에서도 유효하므로 URL·proxy/access log·browser history 노출 경계를 API 전체에 적용해야 한다. [client 근거](evidence:web-auth)

<a id="route-families"></a>
## Route family 지도

고정 source revision에는 method와 pattern 조합으로 구별한 HTTP operation이 **261개** 있다. 등록 형태는 literal 245개, finding traffic의 조합 등록 7개, side-question request ID를 포함한 동적 등록 9개다. method별로 GET 114, POST 85, DELETE 32, PUT 17, PATCH 13이다. 숫자는 `http.ServeMux` 등록을 정적으로 추출한 분모이며 실행 시 middleware나 handler가 성공한다는 뜻은 아니다. [route source](evidence:http-routes)

| 독립 API 참조 | operation 수 | 다루는 영역 |
|---|---:|---|
| [태스크·탐색](api/tasks-exploration.md#api-tasks) | 62 | task, goal, constraint, scope, archive, activity |
| [Finding·traffic](api/findings-traffic.md#api-results) | 49 | finding CRUD, deepen/retest/export, evidence snapshot, traffic capture |
| [Agent·tool·관리](api/agents-tools.md#api-management) | 82 | agent, tool, MCP, skill, asset/company, intercept, sync |
| [시스템·운영](api/system-operations.md#api-system) | 68 | auth, health, settings, update, logs, chat, side question, notification |

각 operation의 path/query/header/body field, 직접 response/error variant, idempotency와 완료 경계는 [요청·응답 semantic 참조](api/schema-reference.md#api-schema-reference)가 소유한다. 27개 Next.js page subtree가 실제로 소비하는 wrapper/API/SSE/state/error 경로는 [프런트엔드 소비 지도](api/frontend-consumers.md#frontend-screens)에서 역방향으로 찾는다.

아래 family 표는 탐색용 요약이다. 요청 field, 직접 status와 handler/source 위치는 위 독립 operation 참조가 소유한다.

| family | 대표 경로 | 주 상태 | 응답이 뜻하는 완료 |
|---|---|---|---|
| auth/health | `/auth/*`, `/health` | settings/JWT key | 인증/상태 요청 처리 |
| tasks | `/tasks`, goals, constraints, scope, assets, control, LLM, intents | task + exploration graph | 생성 201은 접수; engine 완료 아님 |
| archives | `/task-archives`, archive/restore/delete batch | task_archives + package | queue 응답; background job 별도 |
| exploration | frontier, graph, nodes, activity, tokens | graph/activity | 현재 snapshot 또는 stream cursor |
| findings | list/group/tree/stats/export/detail/lineage/deepen/retest | graph node + standalone row | deepen/retest는 비동기 시작 상태 포함 |
| finding traffic | finding별 `/traffic` CRUD/order/body | evidence snapshot/binding | POST는 snapshot+binding commit |
| traffic | `/traffic`, hosts, exchange, blob, delete | SQLite/file capture | 요청한 capture 조회/삭제 |
| assets/companies/sync | asset CRUD, scope, ScopeSentry | global asset graph | import/upsert 완료 |
| agent resources | agents, tools, MCP, skills, visibility, triggers | PG config + skill FS | 설정 저장; in-flight run 교체 보장 아님 |
| intercept | rules, pending, history, tool config, judge | intercept tables/settings | decision 저장 및 대기 work 해제 |
| chat/conversation | chat, upload, conversation messages, side questions | conversation/activity/transcript | streaming/run과 HTTP accept 구분 |
| settings/ops | settings, logs, update, notify, gc, report | settings/log/update state | update apply는 202 후 restart path |

<a id="tasks-contract"></a>
## 태스크 계약과 완료 경계

`POST /api/tasks`는 DB transaction으로 task initial state를 만든 뒤 `launchTask`를 호출하고 **201을 즉시 반환**한다. goal decomposition, admission, planner/worker 실행은 그 뒤 background 흐름이다. `seed_first_intent=true`이면 첫 planner round 전에 worker가 seed intent를 claim할 수 있다. [접수 근거](evidence:create-task) [task DB 근거](evidence:tasks-store)

Task 응답은 DB row의 단순 직렬화가 아니다. DTO는 persistent lifecycle과 메모리 engine의 paused/running/last-activity/in-flight/LLM readiness를 섞어 derived status를 만든다. 재시작·장애 순간에는 저장 상태와 화면 상태가 다른 층임을 고려한다. [DTO 근거](evidence:task-dto)

| 조작 | 주요 결과 | 후속 확인 |
|---|---|---|
| pause/resume/control | engine context와 task lifecycle 변경 | task detail/status + activity |
| goal add/edit | terminal task를 다시 활성화할 수 있고 planner trigger 발생 | goal state와 새 round |
| constraint add/edit | 다음 prompt 주입 데이터 변경 | 새 run부터의 activity; 모든 outbound 강제는 아님 |
| intent message | paused intent의 detached run 또는 active work message | intent state + activity |
| rerun | blocked/exhausted/stopped를 frontier로 돌림 | `open/running` CAS |
| archive | persistent archive job queue | archive state/phase/progress |

<a id="activity-contract"></a>
## History와 SSE

UI session tab은 먼저 history page를 읽어 snapshot cursor를 확보하고 `/api/exploration/activity/stream?task=...&since=...`를 연결한다. server는 **subscribe-before-replay**로 live gap을 줄이고 DB backlog를 batch로 보낸 뒤 live broadcaster를 따른다. `Last-Event-ID`로 reconnect하면 persisted gap을 다시 읽는다. [stream 근거](evidence:activity-stream) [UI client 근거](evidence:web-api)

| stream | persistence/cursor | 비고 |
|---|---|---|
| exploration activity | PostgreSQL activity id | reconnect replay 가능, 20초 ping |
| logs | ring/live + 별도 DB history | activity cursor와 별개 |
| update | in-memory current progress | 프로세스 restart 중 연결 단절 예상, UI가 health polling |
| side question | request event stream | 해당 request lifecycle 범위 |

등록된 SSE operation은 `/api/exploration/activity/stream`, `/api/logs/stream`, `/api/update/stream`, `/api/side-questions/{requestID}/events` 네 개다. frontend는 네 경로를 모두 `EventSource`로 소비하며 이 client 제약 때문에 query token을 실제로 사용한다. 서버가 query token을 SSE로 제한하지 않는 별도 경계는 [인증 계약](#auth-contract)을 본다. [UI client](evidence:web-api) [Side question UI](evidence:web-side-questions)

SSE event 도착은 task success를 뜻하지 않는다. task lifecycle, goal, intent terminal state를 별도로 읽어야 한다.

<a id="findings-contract"></a>
## Finding·evidence 계약

Finding list 응답 shape는 query에 따라 다를 수 있다. task 없이 legacy/non-paged 조회는 제한된 array, paging/filter를 쓰면 page object, task context에서는 local graph와 direct-source readonly 결과를 조합한다. client wrapper와 backend handler를 함께 봐야 한다. [route 근거](evidence:http-routes) [client 근거](evidence:web-api)

`PATCH /api/exploration/findings/{id}`의 status, severity, name, vulnclass 갱신은 단일 요청 전체가 하나의 transaction인 구조가 아니고 graph mirror가 best-effort인 경로가 있다. 중간 실패 시 앞선 field가 남을 수 있다. `DELETE`는 evidence mutation lock 아래 standalone row와 graph node/evidence를 정리한다. [finding handler 근거](evidence:finding-http)

Finding traffic POST는 선택한 live traffic을 읽어 body를 hash/copy하고 snapshot과 ordered binding을 commit한다. 수정·순서 변경·삭제는 `evidence_version` optimistic concurrency를 사용하며 stale version은 409다. detail은 제한된 preview, body endpoint는 page/stream 경로를 제공한다. [traffic handler 근거](evidence:finding-http) [증거 계약](../features/findings-evidence.md#traffic-evidence)

`deepen`은 후속 고우선 intent를 queue하고 응답한다. retest는 별도 conversation/agent를 시작한다. 둘 다 HTTP 200/생성 응답을 실제 보안 검증 완료와 동일시하면 안 된다.

<a id="traffic-contract"></a>
## Traffic API

`GET /traffic`은 SQLite index의 page와 count를 가볍게 반환하고, 전체 request/response는 `/traffic/exchange`, 큰 body는 `/traffic/blob`에서 지연 로드한다. host 단일 삭제 query와 exact host batch 삭제는 의미가 다르며 `/traffic/all`은 capture를 비우고 index를 compact한다. 이미 finding에 bind된 evidence snapshot은 별도 저장이므로 capture 삭제와 동일하지 않다. [traffic 근거](evidence:traffic)

<a id="ui-map"></a>
## UI에서 코드로 가는 지도

| UI 경로 | 사용자 기능 | API/상태 문서 |
|---|---|---|
| `/function/tasks` | 생성·제어·archive·restore·category | [태스크 계약](#tasks-contract) |
| `/function/tasks/detail` | overview, goals, constraints, scope, assets, sessions, findings | [탐색 루프](../features/task-exploration.md#lifecycle) |
| `/function/findings` / detail | finding triage, traffic evidence, retest, deepen, export | [finding 계약](#findings-contract) |
| `/function/traffic` | capture 검색·상세·삭제 | [traffic 계약](#traffic-contract) |
| `/function/assets` | asset graph·company scope | [asset/scope](data-model.md#asset-scope) |
| `/function/sync` | ScopeSentry datasource와 project/task asset import | [보조 루프](../modules/agent-runtime.md#supporting-loops) |
| `/system/agents` | agent config, prompt version, wrapup, trigger, visibility | [runtime 조립](../modules/agent-runtime.md#tool-assembly) |
| `/system/tools`, `/system/mcp`, `/system/skills` | 실행 능력 catalog/연결 | [tool 조립](../modules/agent-runtime.md#tool-assembly) |
| `/system/intercept` | rule, asset block/allow, pending approval, judge | [보안 경계](../security-boundaries.md#tool-boundary) |
| `/system/llm`, `/system/settings` | provider, retry, worker, proxy, capture, update | [운영](../operations.md#configuration) |
| `/system/logs`, `/system/notify` | 관찰·delivery | [운영 점검](../operations.md#observability) |

페이지 목록은 Next App Router tree에서, 실제 client method/shape는 `web/src/lib/api.ts`, frontend types는 `web/src/lib/types.ts`에서 확인된다. 위 표에 빠진 `/function/commands`, `/function/llm-records`, `/function/workspace`, `/system/agents/detail`, `/system/intercept/approvals`, `/system/intercept/assets`를 포함한 27/27 entry의 정확한 소비 관계는 [화면별 계약](api/frontend-consumers.md#frontend-screens)이 소유한다. [UI tree 근거](evidence:web-layout) [client 근거](evidence:web-api)

<a id="contract-risks"></a>
## 계약을 사용할 때 주의할 점

- background 작업의 accept response와 완료 상태를 분리한다: task 201, archive queue, deepen, retest, update 202가 대표적이다.
- UI type만 권위 계약으로 쓰지 않는다. backend가 query mode에 따라 응답 shape를 바꾸는 endpoint가 있다.
- query token은 browser `EventSource`를 지원하지만 서버에서 257개 operation에 유효하므로 URL 취급·access-log redaction 정책이 필요하다.
- CORS middleware는 `Access-Control-Allow-Origin: *`와 `Content-Type` header만 선언한다. cross-origin Authorization preflight가 실제로 작동하는지는 실행 검증이 필요하다. same-origin 배포 경로와 혼동하지 않는다. [route 근거](evidence:http-routes)
- 설정/metadata API 일부는 request-wide transaction이 아니다. 자동화 client는 read-after-write와 부분 성공 처리를 고려한다.

<a id="api-denominator"></a>
## API 분모와 확인 한계

| 분모 | 확인 | 한계 |
|---|---:|---|
| 등록 operation | 261/261 | method+pattern+handler의 정적 등록 전수; runtime 호출 미실행 |
| operation 직접 목적지 | 261/261 | 네 API 참조에 안정 anchor 제공 |
| 요청 wire shape | 261/261 located | JSON request field 97, multipart 3, 직접 decoder/form read 미발견 161; shared handler의 method branch를 분리한 수이며 helper validation은 P |
| handler query key | 169 operation-key pair | direct/alias + 확인한 helper 47개; common auth query `token`은 별도 257-operation 계약 |
| JSON body field requiredness | 369 field | required 54 / group 15 / conditional 1 / optional 11 / direct provided-validation 69 / default·normalization 14 / U 205; U를 완료로 세지 않음 |
| 요청 body cap | 28/28 applicable operation | 16 KiB–512 MiB의 exact cap과 overflow status/body를 operation schema에 기록 |
| 직접 response schema | 36 C / 225 P / 0 U | C는 primitive/static field type까지 확인; derived/helper/opaque/nested/stream/file shape는 P; operation 전체 semantic 완료율과 별도 |
| 프런트엔드 page entry | 27/27 | static local import graph; runtime branch는 A |
| SSE frontend 소비 | 4/4 endpoint | `EventSource` call site 5개(두 화면이 side-question hook 공유) |
| OpenAPI/JSON Schema | 없음 | 문서 schema는 handler AST의 관찰이며 별도 versioned spec은 아님 |
| live HTTP 검증 | 미실행 | auth, status, payload, background completion은 배포 환경 대조 필요 |
