<a id="runtime-topology"></a>
# 설치·실행·업데이트·복구

이 문서는 소스가 선언한 운영 경로를 정리한다. 명령·컨테이너·DB migration·update·archive를 실제 실행하지 않았으므로 “실행 가능 확인”이 아니라 “코드/manifest 확인”이다.

```mermaid
flowchart LR
  B[Browser] -->|8787 HTTP/API/SSE| A[ARTEX Go binary]
  A -->|5432| P[(PostgreSQL)]
  A -->|8788 target proxy| T[Traffic recorder]
  A --> D[(data directory)]
  A --> S[skills directory]
  A --> L[LLM / MCP / Search]
  A --> X[Target egress]

  click A "#startup" "프로세스 시작"
  click P "#database-startup" "DB 적용"
  click D "#paths" "로컬 경로"
  click T "features/findings-evidence.md#traffic-evidence" "트래픽 저장"
  click L "modules/agent-runtime.md#provider-routing" "외부 provider"
  click X "security-boundaries.md#scope-gap" "egress 경계"
```

<a id="command-index"></a>
## 정상 운영 명령·파일 index

| 목적 | source entrypoint | 대표 명령 | 입력 계약 |
|---|---|---|---|
| binary 직접 실행 | `artex` / `artex.exe` | `./artex -addr :8787 -data ./data -proxy 127.0.0.1:8788` | application flag 3개; [전수표](contracts/runtime-settings.md#runtime-cli) |
| supervised 실행 | `start.sh`, `start.bat` | `./start.sh -addr :8787` | 모든 인자를 binary에 전달; exit code 0/75/other protocol |
| 첫 설치 | `install.sh` | `./install.sh` | CLI option 없음; Docker/local 대화형 선택 |
| source 개발 | `dev.sh` | `./dev.sh` | CLI option 없음; backend/proxy는 8787/8788, frontend command에는 port 인자 없음 |
| build/package | `build.sh` | `./build.sh`, `./build.sh --release` | option concept 5개, env 14개; [build 계약](contracts/dependencies.md#build-contract) |
| source/image update | `update.sh` | `./update.sh` | CLI option 없음; Docker/local 대화형 선택 |
| 관리자 password reset | `reset-password.sh` | `./reset-password.sh -m docker` | option concept 14개; [reset 계약](#reset-password) |
| Compose lifecycle | `docker-compose.yml` | `docker compose up -d`, `logs -f artex`, `down` | `.env` input 10개; [Compose 계약](contracts/dependencies.md#compose-inputs) |
| web 개발/check | `web/package.json` | `npm run dev`, `npm run check`, `npm run build:static` | npm script 10개; [script 목록](contracts/dependencies.md#web-script-contract) |
| tagged release | `.github/workflows/release.yml` | `git push`의 `v*` tag trigger | Node 22, Go 1.26, 5 binary target, 2 image platform |

`install.sh`, `update.sh`, `dev.sh`는 `$@`를 parse하지 않는다. 정의된 option이 없고 전달한 argument는 효과가 없다. `build.sh`와 `reset-password.sh`만 독립적인 script option parser를 갖는다.

<a id="startup"></a>
## 프로세스 시작 순서

`cmd/artex/main.go`의 `run`은 다음 순서다. [main 근거](evidence:main)

1. `-addr` 기본 `:8787`, `-data` 기본 `<BaseDir>/data`, `-proxy` 기본 `127.0.0.1:8788`을 파싱한다.
2. log capture를 시작한다.
3. DB/port를 열기 전에 self-update bootstrap을 수행하고 교체/rollback이면 exit code 75로 supervisor 재시작을 요청한다.
4. `NewManager`가 필수 PostgreSQL, 선택적 traffic proxy, engine stores를 연다.
5. server가 skill/data/base 경로와 함께 API/UI를 구성한다.
6. HTTP server를 시작하고 signal 또는 update restart를 기다린다.
7. 종료 시 agent abort cause를 전달하고 HTTP에 5초 graceful shutdown window를 준다.

소스 파일 상단의 “dual SQLite graph stores” 주석은 현재 PostgreSQL 기반 업무 상태와 맞지 않는 오래된 설명이다. 실제 constructor와 schema를 우선한다.

<a id="configuration"></a>
## 설정 우선순위

전체 key와 저장/재조립 경계는 [설정 계약](contracts/configuration.md#configuration-contract)이 소유한다.

| 항목 | 우선순위 | 기본/실패 |
|---|---|---|
| config path | `ARTEX_CONFIG` → cwd `config.json` → executable 옆 `config.json` | 없으면 cwd 후보를 보고 |
| PostgreSQL | `ARTEX_PG_DSN` → `database.dsn` → database fields | fallback 없음, 없으면 startup 실패 |
| skill root | `ARTEX_SKILL_DIR` → config `skill_dir` → `<BaseDir>/skills` | 없으면 directory 생성 |
| API listen | CLI `-addr` | `:8787` |
| data root | CLI `-data` | `<BaseDir>/data` |
| recording proxy | CLI `-proxy` | `127.0.0.1:8788`, 빈 값이면 비활성 |
| LLM | DB profiles/role-task bindings → global/env | 역할별 runtime 해석 |

PostgreSQL이 열리지 않으면 server가 시작하지 않는다. traffic proxy 초기화 실패는 로그를 남기고 capture disabled 상태로 계속할 수 있다. [config 근거](evidence:config) [manager 근거](evidence:manager)

<a id="grep-bootstrap-operations"></a>
## `Grep` bootstrap의 network·filesystem 효과

Norma `Grep`는 read-only tool로 노출되지만 기본 상태의 첫 호출은 PATH에 `rg`가 없을 때 `npm install -g @vscode/ripgrep`를 실행할 수 있다. Registry network access와 npm global prefix 쓰기 권한을 ARTEX process에 요구하며, package version은 pin하지 않는다. 이 command는 ARTEX tool/intercept trace에 별도 호출로 남지 않는다. 실패 원인은 숨기고 pure-Go 검색으로 계속하므로 정상 검색 결과도 설치 성공·실패·미시도를 구분하지 않는다. [도구 효과](contracts/tool-catalog.md#grep-bootstrap) [resolver 근거](evidence:norma-runtime-env)

| 원하는 운영 상태 | process 시작 전 설정 | 첫 `Grep` 동작 |
|---|---|---|
| 관리자가 준비한 `rg`만 사용 | `NORMA_RIPGREP_NO_INSTALL=1`; 승인된 `rg`를 PATH에 배치 | 기존 binary 사용, 없으면 pure-Go fallback |
| network/global install 금지 | `NORMA_RIPGREP_NO_INSTALL=1` | npm install 없이 existing `rg` 또는 pure-Go |
| ripgrep 자체 금지 | `NORMA_DISABLE_RIPGREP=1` | PATH를 보지 않고 pure-Go |
| source default 유지 | 두 변수를 unset | PATH lookup → global npm install 시도 → pure-Go fallback |

Container/read-only host에서는 global prefix가 쓰기 불가여도 application startup은 실패하지 않는다. 설치 시도는 startup이 아니라 process의 첫 `Grep` 호출에서 발생하고 process마다 `sync.Once` 상태가 초기화된다. 재현성과 egress 통제가 필요하면 image/package build 단계에서 승인한 `rg`를 고정하거나 위 suppression env를 service definition에 명시해야 한다.

<a id="paths"></a>
## 상태 경로

| 위치 | 내용 | 백업 관점 |
|---|---|---|
| PostgreSQL | tasks, graph, findings, settings, profiles, intercept, notifications | 별도 일관된 DB backup 필요 |
| `<data>/traffic` | SQLite index, CAS blobs, MITM CA, 선택적 legacy host files | 새 capture는 SQLite+CAS; non-empty `exchanges.path`가 있으면 legacy tree도 보존; evidence와 별개 |
| `<data>/evidence/blobs` | finding body CAS | DB snapshot metadata와 함께 보존 |
| `<data>/transcripts` | resumable agent sessions | DB intent와 함께 있어야 유용 |
| `<data>/tasks/<id>` | workspace/artifacts/uploads | task archive 포함 범위 확인 |
| `<BaseDir>/skills` | local Skill files | compose에서 별도 bind mount |
| `<BaseDir>/jwt.key` | API signing key | data workspace 밖; 별도 보존 필요 |
| config/env | DB DSN와 provider/runtime 설정 | secret 관리·복구 계획 필요 |

<a id="database-startup"></a>
## Schema 적용

DB open은 PostgreSQL advisory lock을 잡고 embedded `schema.sql`을 매 시작 실행한다. 고정 source revision의 core 분모는 49 table·520 final column·97 index·4 function·22 trigger이며, DDL은 `IF NOT EXISTS`, `ALTER`, conditional `DO`, backfill을 섞은 idempotent 방식이다. deadlock code `40P01`에는 짧은 retry가 있다. 이후 built-in agents/tools/rules/visibility를 seed한다. [DB 근거](evidence:db-open) [DB 객체 참조](contracts/db/functions-triggers.md#db-functions-triggers)

단일 migration ledger가 없으므로 “현재 binary가 기대하는 schema”는 해당 revision의 `schema.sql`과 코드의 별도 `Ensure*Table` 함수 조합이다. 실제 DB가 어느 DDL까지 성공했는지 확인하는 version row는 없다. core schema/seed 오류는 대체로 fatal이지만 18-column `llm_records`와 13-column `llm_usage` ensure 실패는 manager가 로그 후 진행한다. 따라서 서버가 떠도 두 보조 ledger와 그 5개 index는 없을 수 있다.

`assets`, `company_scope`, `llm_profiles`, `task_scope`, `mcp_servers`의 일부 CHECK는 매 startup에 drop/add된다. `ADD CONSTRAINT ... NOT VALID`가 아니라 기존 row를 재검증하고 ALTER table lock이 일반 writer를 block할 수 있으며, invalid legacy row는 startup transaction을 실패시킨다. Initializer의 session advisory lock은 동일 initializer/archive 경로를 조율하지만 일반 DB connection의 쓰기를 자동으로 막지 않는다. Fresh/upgrade 제약 차이, repeated backfill·seed·default 변경은 [migration 참조](contracts/db/functions-triggers.md#schema-migrations)가 권위다.

<a id="deployment"></a>
## 배포 경로

소스 README는 Docker Compose, release binary+`start.sh`, source build를 제시한다. [README 근거](evidence:source-readme)

Docker Compose 선언은 PostgreSQL 16 alpine의 named `pgdata`, ARTEX image, `./data:/app/data`, `./skills:/app/skills`, 8787 publish, 8788 loopback publish다. PostgreSQL health 후 ARTEX를 시작한다. 이는 manifest 정적 확인이며 실제 image/toolchain 또는 health를 실행 검증하지 않았다. [compose 근거](evidence:compose)

source build는 Next.js static export를 `server/webui/dist`로 옮기고 `-tags embedui` Go binary를 만든다. release/설치 script의 요구 version과 dependency는 script가 권위이며 이 문서 생성 중 실행하지 않았다.

reverse proxy는 UI/API/SSE를 같은 8787 origin으로 넘기는 구성이 기본이다. SSE buffering을 끄라는 source README 지침이 있다. cross-origin dev path와 production same-origin path의 auth/CORS 차이는 [HTTP 계약](contracts/http-ui.md#contract-risks)을 본다.

<a id="install-script"></a>
## `install.sh`: 대화형 첫 설치

Script는 option을 받지 않고 먼저 Docker 전체 설치와 local build 중 하나를 묻는다. [install script](evidence:install-script)

| 경로 | 검사·입력 | write·실행 결과 |
|---|---|---|
| Docker | Docker/Compose 확인; Linux에서는 미설치 시 `curl -fsSL https://get.docker.com \| sh` 실행 여부를 질문 | `.env`가 없으면 example copy를 시도하고 random 24-character PostgreSQL password와 optional Anthropic key를 `sed`로 기록; `docker compose pull` 실패는 무시하고 `docker compose up -d` 실행 |
| Local + existing PostgreSQL | host/port/user/password/dbname/sslmode 대화형 입력 | raw string interpolation으로 `config.json`을 덮어씀; frontend 포함/미포함 binary를 `./artex`로 만들고 즉시 foreground 실행 |
| Local + Docker PostgreSQL | Docker 확인, random/입력 password | container `artex-pg`, host port 5432, named volume `artex-pg`를 생성한 뒤 위 local build/run 수행 |

Linux Docker 자동 설치는 원격 script 실행과 `sudo usermod -aG docker "$USER"`를 포함한다. Local `config.json` 생성은 JSON escape를 하지 않으므로 quote, backslash, newline이 든 credential은 invalid JSON을 만들 수 있다. Existing `.env`는 그대로 재사용하며 필수값 유효성을 script가 미리 확인하지 않는다.

<a id="dev-script"></a>
## `dev.sh`: source 개발 process group

`./dev.sh`는 `go run ./cmd/artex -addr :8787 -proxy 127.0.0.1:8788`과 `web`의 `npm run dev`를 병렬 시작한다. Frontend `/api` rewrite는 기본적으로 backend 8787을 향한다. EXIT/INT/TERM trap이 `kill 0`으로 같은 process group을 정리한다. Port나 binary flag를 argument로 바꾸는 인터페이스는 없다. Frontend override는 [frontend env 5개](contracts/runtime-settings.md#frontend-env)를 쓴다. [dev script](evidence:dev-script)

Script comment, banner, source README는 frontend URL을 `http://localhost:5173`으로 안내하지만 `web/package.json`의 command는 port 없이 `next dev`다. 이 checkout에는 `PORT=5173`을 주입하는 파일도 없다. 따라서 5173은 현재 command가 보장하는 값이 아니며 실제 listen port는 실행 환경/Next 동작을 확인해야 한다.

<a id="build-package"></a>
## Build·package

- 현재 host/arch binary: `./build.sh`
- 지정 target: `./build.sh --target linux/amd64`
- default 5-target ZIP/checksum: `./build.sh --release`
- static UI가 이미 준비된 CI build: `ARTEX_SKIP_FRONTEND=1 ./build.sh --target linux/amd64`

Option 5개, environment control 14개, 필수/optional executable, archive 내용은 [직접 의존성·build 계약](contracts/dependencies.md#build-contract)에 모았다. Build는 source/asset을 만들 뿐 실행 중 process를 재시작하지 않는다.

<a id="start-wrapper"></a>
## `start.sh`·`start.bat`: supervisor protocol

두 wrapper는 자신의 directory로 이동해 `artex`/`artex.exe`를 실행하고 받은 argument를 그대로 넘긴다. [Unix wrapper](evidence:start-script) [Windows wrapper](evidence:start-bat)

| child exit | wrapper 동작 |
|---:|---|
| `0` | 정상 stop으로 보고 wrapper도 0으로 종료 |
| `75` | update/restart request로 보고 delay를 1초로 reset한 뒤 즉시 재실행 |
| 그 밖 | 1, 2, 4, 8… 최대 60초 exponential delay 뒤 재실행 |

Unix wrapper는 INT/TERM을 child에 TERM으로 전달하고 graceful exit를 다시 기다린다. Windows wrapper는 `%*` passthrough와 `ping` delay를 쓰지만 Unix와 같은 explicit signal-forwarding 경로는 없다. Self-update를 쓰면서 binary를 직접 실행하면 exit 75 뒤 자동 재기동되지 않는다.

<a id="docker-operations"></a>
## Docker Compose lifecycle

```bash
docker compose up -d
docker compose logs -f artex
docker compose down
```

Production compose는 PostgreSQL health를 기다린 뒤 ARTEX를 실행하고 두 service 모두 `unless-stopped`다. `down`은 named `pgdata` volume을 기본 삭제하지 않지만 `down -v`는 삭제한다. Host persistence 선언은 `./data`, `./skills`, `pgdata` 세 곳이다. `/app/jwt.key`는 `./data:/app/data` 밖이라 별도 보존해야 한다. [Compose](evidence:compose)

`docker-compose.bench.yml`은 `Dockerfile.bench`와 `bench/.env`를 요구하지만 두 path가 이 source snapshot에 없다. 따라서 파일에 적힌 `docker compose -f docker-compose.bench.yml up --build` 절차는 현재 checkout만으로 실행 가능한 경로가 아니다. [benchmark compose](evidence:bench-compose)

<a id="updates"></a>
## 업데이트와 rollback

| 경로 | 동작 | rollback 범위 |
|---|---|---|
| `update.sh` Docker | optional repository fast-forward, image pull, ARTEX service recreate | 선택한 image/tag 운용에 따름; PostgreSQL service와 volume은 그대로 둠 |
| `update.sh` local | optional repository fast-forward, frontend+binary rebuild | running process를 재시작하지 않으며 old binary backup을 만들지 않음 |
| in-app update | GitHub release/`SHA256SUMS` 확인, extract, `.new` stage, exit 75 | 이전 executable `.old` 한 개 |

<a id="update-script"></a>
### `update.sh`

`./update.sh`는 CLI option이나 process env control을 읽지 않는 대화형 script다. 두 경로 모두 먼저 `git pull --ff-only` 실행 여부를 묻는다. Pull 실패는 warning만 내고 현재 checkout으로 계속한다. [update script](evidence:update-script)

| 선택 | 선행 조건·입력 | exact 동작 | 완료 경계 |
|---|---|---|---|
| Docker | Docker/Compose, `.env`; optional target image tag | 입력한 tag를 `.env`의 `ARTEX_TAG`에 replace/append; `docker compose pull artex`; `docker compose up -d artex` | ARTEX container recreate 완료; PostgreSQL image를 pull/recreate하지 않음 |
| Local | Go; npm은 optional; `config.json` 없음은 warning | npm이 있으면 `npm ci`, static build, `embedui` binary; 없으면 backend-only `artex` | binary write 완료; 실행 중 process는 사용자가 재시작해야 함 |

Script는 DB schema를 직접 실행하지 않는다. 새 binary/container의 다음 startup이 embedded schema를 적용한다. Docker/local 어느 경로도 schema/data rollback을 제공하지 않는다.

<a id="in-app-update"></a>
### In-app binary update artifact와 boot 판정

Artifact는 CWD가 아니라 **현재 executable directory**에 놓인다. Unix 예시는 아래와 같고 Windows는 `.new.exe`, `.old.exe` 순서다. [self-update bootstrap](evidence:self-update)

| path | 의미·수명 |
|---|---|
| `artex` | current executable |
| `artex.new` | downloaded/staged candidate; 다음 bootstrap이 SHA256과 `-h` smoke를 다시 확인 |
| `artex.new.sha256` | candidate의 expected hex SHA256 |
| `artex.old` | swap 직전 executable; manual/automatic rollback source |
| `artex.upgrade.json` | from/to/staged timestamp와 boot attempts marker |
| `artex.failed` | automatic rollback이 밀어낸 실패 binary; 존재할 때 교체 가능 |

Apply 요청은 stage가 끝나면 exit 75를 내고 supervisor가 old binary를 다시 시작한다. Bootstrap이 candidate를 검증해 current/old를 swap한 뒤 다시 exit 75를 낸다. New binary가 HTTP listen 후 30초 생존하면 marker를 지워 settle한다. Marker가 남은 채 세 번 시작에 실패하면 다음 bootstrap이 `.old`를 복원한다. `ARTEX_SELFUPDATE_SMOKE`는 candidate의 `-h` child가 bootstrap을 재귀 실행하지 않게 하는 내부 env다.

이 rollback은 executable만 되돌린다. PostgreSQL schema/data, `config.json`, `.env`, data/skills directory는 과거 상태로 돌아가지 않는다. Update/restart는 실행 중 task를 중단할 수 있다.

<a id="reset-password"></a>
## `reset-password.sh`: 14개 option concept

Script는 administrator username `ARTEX`의 `settings['auth.password_hash']`를 PostgreSQL `pgcrypto` bcrypt cost 10으로 upsert한다. 성공하면 application restart 없이 다음 login부터 적용된다. [password reset script](evidence:reset-password-script)

| option | 값·기본 | consumer·효과 |
|---|---|---|
| `-m`, `--mode` | `local|docker`, auto | explicit DB input/config가 있으면 local, 아니면 Compose file+Docker가 있으면 docker, 그 밖은 local |
| `--dsn` | PostgreSQL DSN, empty | local mode direct connection; component flags보다 DSN 경로 사용 |
| `-H`, `--host` | host, `127.0.0.1` | local component connection |
| `-P`, `--port` | integer/string, `5432` | local component connection |
| `-U`, `--user` | string, 필수 또는 config | local DB role; docker에서는 `.env`보다 우선 |
| `-W`, `--db-password` | secret string, empty | `PGPASSWORD`; docker에서도 `.env`보다 우선 |
| `-d`, `--dbname` | string, 필수 또는 config | target database; docker에서는 `.env`보다 우선 |
| `--sslmode` | string, `disable` | local component connection의 `PGSSLMODE` |
| `--config` | path, `config.json` | local fallback `database.dsn` 또는 component field source |
| `-c`, `--container` | service/container name, `postgres` | docker exec target |
| `--exec` | `compose|docker`, auto | Compose command/file가 있으면 `docker compose exec`, 아니면 `docker exec` |
| `-p`, `--new-password` | non-empty string; 아니면 2회 silent prompt | 새 administrator password |
| `-y`, `--yes` | flag, false | target 확인 prompt 생략 |
| `-h`, `--help` | flag | header usage 출력 후 0 종료 |

Local connection precedence는 explicit flags/`--dsn` → `ARTEX_PG_DSN` → selected `config.json`이다. Component path는 user와 dbname이 없으면 실패하고 host/port/sslmode만 기본값이 있다. Docker credential precedence는 flags → `.env`의 `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` → user/database `artex`; container는 `postgres`다.

Option 밖에서 명시적으로 소비하는 environment input은 5개다: local DSN fallback `ARTEX_PG_DSN`, libpq secret `PGPASSWORD`, Docker `.env` fallback `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`. Docker mode는 `.env`를 shell의 `. ./.env`로 source하므로 그 파일은 단순 key/value data가 아니라 현재 사용자 권한으로 평가되는 shell input이다.

`ARTEX_RESET_NEWPASS`는 script가 새 password를 `psql \getenv`에 전달할 때 임시로 export하고 끝에 unset한다. Component local path는 `PGSSLMODE`를 설정한다. `-p`와 `-W`로 secret을 주면 script가 이후 psql argv에는 넣지 않더라도 최초 shell/process argv에는 나타날 수 있으므로 interactive prompt나 사전에 보호한 environment/config가 더 적합하다. DB role에 `CREATE EXTENSION IF NOT EXISTS pgcrypto` 권한이 없으면 reset이 실패한다.

<a id="archives"></a>
## Task archive

task archive API는 archive row를 persistent queue에 넣고 background worker가 수행한다. archive 대상 task는 paused 또는 terminal이어야 하고 live dependent/source 관계에 따른 제한이 있다. package는 **선별된 task-owned PostgreSQL tables**, streamed LLM records, workspace/transcripts, scoped traffic, copied evidence를 `.tar.zst`로 모으며 checksum/format을 기록한다. DB snapshot에는 task/exploration graph, constraints/activity, relation/scope/assets, findings와 bound evidence, usage, pending intercept, side-question rows 등이 들어가지만 finding retest history와 그 conversation, intercept decision history, notification state는 query 목록에 없다. [archive DB 근거](evidence:task-archive-db) [archive worker 근거](evidence:task-archive-server)

restore는 package checksum과 format/task id를 검증하고 traffic/files/DB를 단계적으로 설치한 뒤 package를 consume한다. stage journal과 startup recovery path가 archive/restore/delete 중단을 다룬다. 클라이언트는 enqueue 응답이 아니라 `task_archives.state`, `phase`, `progress`, `error`, `warnings`로 결과를 판단해야 한다.

Archive compaction은 PostgreSQL commit 후 staged Traffic SQLite delete를 commit하므로 두 store가 하나의 atomic transaction이 아니다. 그 사이 crash gap은 traffic staging journal과 startup recovery가 PostgreSQL archive state를 조회해 보정한다. Restore에서는 incoming archive blob을 SHA-256 검증하고 CAS copy한 뒤 SQLite transaction을 연다. 이미 존재하는 destination blob은 재검증하지 않으며, 후속 DB 실패·duplicate-only import에 orphan이 남을 수 있다. FTS는 small body 전체 또는 spilled body의 archive inline 8 KiB preview로 재생성된다. [Traffic commit/recovery matrix](contracts/db/traffic-sqlite.md#filesystem)

<a id="backup-recovery"></a>
## Backup·복구 경계

Task archive는 한 task의 cold storage/restore 기능이다. 다음을 모두 포함하는 전체 disaster-recovery backup은 아니다.

- PostgreSQL 전체와 global settings
- 모든 task와 archive stub/package
- `config.json`, `.env`, provider/MCP/notification secrets
- `jwt.key`
- 전체 skills
- data directory의 task 밖 파일

또한 해당 task와 연결돼 보여도 finding retest history/conversation처럼 snapshot query에 없는 기능 데이터는 복원되지 않는다. archive를 task의 모든 이력 보존으로 간주해서는 안 된다.

저장소에서 완전한 PostgreSQL backup/restore script, RPO/RTO 선언, isolated restore test 절차는 확인되지 않았다. `pgdata` volume persistence도 backup 자체는 아니다. 실제 운용 전 DB와 filesystem을 일관된 시점에 보존하고 복구 rehearsal하는 절차를 별도로 설계해야 한다.

Traffic root/index/CAS/CA는 코드상 `0755`, CAS blob은 `0644`, archive staging/blob directory는 `0700`, archive/journal file은 `0600`을 요청한다. Umask·ACL·container volume mount가 effective permission을 바꿀 수 있으며 실제 mode/owner는 관찰하지 않았다.

<a id="observability"></a>
## 관찰과 상태 판정

| 관찰면 | 권위/제한 |
|---|---|
| `/api/health` | process/API version 응답; engine 작업 성공을 뜻하지 않음 |
| `/api/stats` | active task derived 상태, in-flight, last activity, asset/traffic count |
| activity history/SSE | persisted agent/engine event와 cursor |
| logs/history/stream | backend 진단; activity와 별도 |
| LLM records/usage | model call/token 관찰; ensure table 실패 시 gap 가능 |
| intercept history/execution | tool decision과 실행 correlation |
| archive state/progress | archive/restore/delete background job 완료 경계 |
| finding evidence version | report/binding freshness 경계 |

실제 readiness에는 PostgreSQL, 선택한 LLM chain, 외부 MCP/search, target egress, proxy CA, filesystem 여유가 함께 필요하다. `/health` 하나만으로 이 의존성을 증명하지 않는다.

<a id="source-use-boundary"></a>
## 원본 배포물의 사용 제한

고정한 upstream README의 “许可与免责声明” 구역은 ARTEX를 개인 학습·코드 연구·로컬 격리 검증에만 쓰고, 승인 여부와 관계없이 웹사이트·온라인 서비스·네트워크 시스템에 실제 scan/probe/exploit을 하지 말라고 명시한다. [원문 위치](evidence:source-readme)

이 문구는 이 문서에서 확인한 runtime scope control과 별개의 **배포물 사용 조건**이다. AGPL-3.0 license text와 README의 추가 조건이 어떤 법적 관계를 갖는지는 이 기술 문서가 판단하지 않는다. 프로젝트가 ARTEX 코드를 재사용·배포·운용하려면 별도의 법률·라이선스 검토 대상이다.

<a id="unverified"></a>
## 실행 전 확인할 미검증 항목

- 실제 PostgreSQL에 전체 schema가 적용되고 expected tables/indexes/checks가 존재하는가
- container recreate 뒤 `jwt.key`, traffic CA, evidence, transcripts가 유지되는가
- task archive round trip이 큰 body/LLM record/상속 task에서도 복원되는가
- update 직후 schema change와 binary rollback 조합이 안전한가
- reverse proxy에서 SSE buffering, timeout, query token log redaction이 맞는가
- traffic proxy 실패/transparent tunnel이 evidence completeness에 어떻게 표시되는가
- DB/file backup을 같은 복구 시점으로 맞출 수 있는가
