<a id="runtime-settings-reference"></a>
# 런타임 설정 전수표

이 페이지는 binary가 읽는 CLI·환경변수·`config.json` field와 PostgreSQL `settings` key를 이름 단위로 고정한다. Build, frontend, Compose 입력은 [빌드 계약](dependencies.md#build-contract), HTTP payload shape는 API 계약이 소유한다.

<a id="runtime-cli"></a>
## CLI: 3개 application flag

| 이름 | 타입·기본값 | 소비자·조건 | 결과 |
|---|---|---|---|
| `-addr` | string, `:8787` | `cmd/artex.run`, process 시작 | HTTP/API/UI listen address |
| `-data` | path, `<BaseDir>/data` | manager/store 생성 | traffic·evidence·transcript·workspace root |
| `-proxy` | address, `127.0.0.1:8788` | manager 생성; `""` 허용 | recording proxy를 열며 빈 값이면 생성하지 않음 |

표준 `-h`/`-help`는 Go `flag` 도움말을 stderr에 쓰고 exit 0 한다. 알 수 없는 flag나 값 parse 실패는 stderr usage와 exit 2다. [main](evidence:main)

<a id="runtime-env"></a>
## Go application runtime env: 13개

| 이름 | 타입·기본값 | 우선순위·소비자 | 기능 분기 |
|---|---|---|---|
| `ARTEX_CONFIG` | path string, unset | `config.Path`; 설정되면 CWD/executable 후보보다 우선 | 읽을 `config.json` 위치 |
| `ARTEX_SKILL_DIR` | path string, unset | `config.SkillDir`; `skill_dir`보다 우선 | Skill registry root; 없으면 생성을 시도 |
| `ARTEX_PG_DSN` | PostgreSQL DSN, unset | `config.PostgresDSN`; 모든 file DB field보다 우선 | 없고 file 입력도 없으면 startup 실패 |
| `ARTEX_LLM_PROVIDER` | string, unset | environment fallback provider | 빈 값은 key로 추론; `openai`, `openai-responses`를 별도 처리하고 그 밖은 Anthropic format |
| `ANTHROPIC_API_KEY` | secret string, unset | Anthropic fallback | 선택 format의 key가 비면 env provider를 만들지 않음 |
| `OPENAI_API_KEY` | secret string, unset | OpenAI/Responses fallback | 선택 format의 key가 비면 env provider를 만들지 않음 |
| `ARTEX_LLM_BASE_URL` | URL string, empty | fallback provider | 빈 값은 provider library endpoint 기본 동작 |
| `ARTEX_LLM_MODEL` | model id, provider별 기본 | fallback provider | Anthropic `claude-opus-4-8`, OpenAI `gpt-4o`, Responses `gpt-5` |
| `ARTEX_LLM_PROXY` | HTTP/HTTPS/SOCKS5 URL, empty | fallback provider | 해당 LLM 요청 egress만 변경 |
| `ARTEX_LLM_STREAM` | bool-like, true | fallback provider | `0`, `false`, `off`, `no`만 non-streaming; 나머지는 streaming |
| `ARTEX_BTW_MAX_OUTPUT_TOKENS` | integer, `8192` | side-question 초기화; 유효 범위 256–32768 | 유효한 값만 output budget override, 그 밖은 8192 |
| `ARTEX_NOTIFY_ALLOW_LOCAL` | bool-like, false | notification HTTP dial guard | `1` 또는 대소문자 무시 `true`면 loopback/link-local/unspecified/multicast target 허용; RFC1918 private address는 기본 차단 대상이 아님 |
| `ARTEX_SELFUPDATE_SMOKE` | string, unset | self-update bootstrap | 값이 비어 있지 않으면 bootstrap을 건너뜀; 새 binary `-h` smoke child용 내부 handshake |

LLM env는 DB profile·agent/task binding과 별개의 fallback 입력이다. DB profile secret을 이 표의 key가 덮어쓰는 전역 override로 해석하면 안 된다. [provider](evidence:provider) [runtime env consumers](evidence:runtime-env-consumers)

<a id="dependency-env"></a>
## Dependency-owned explicit runtime env: 6개

ARTEX가 Norma `v0.4.3`의 provider와 tool helper를 호출하므로 다음 이름도 application 동작을 바꾼다.

| 이름 | 타입·기본 | 소비자·override | 결과 |
|---|---|---|---|
| `AGENT_CORE_DISABLE_INTERACTIVE_SHELL` | bool-like, false | Norma `InteractiveShellDisabled`, ARTEX `ToolResolve`; `1`, `true`, `yes`, `on`이 true | agent의 `interactive_shell` 값과 관계없이 5개 `shell_*` tool을 주입하지 않음 |
| `AGENT_CORE_DISABLE_BACKGROUND_TASKS` | bool-like, false | Norma session/Bash; `1`, `true`, `yes`, `on`이 true | `TaskOutput`, `TaskStop`, `TaskList`, `Monitor`와 Bash background 실행을 끔 |
| `NORMA_DISABLE_RIPGREP` | presence bool, unset | Norma `Grep`; non-empty이면 우선 | existing `rg`도 쓰지 않고 pure-Go search를 강제; auto-install도 실행하지 않음 |
| `NORMA_RIPGREP_NO_INSTALL` | presence bool, unset | Norma `Grep`; `rg`가 PATH에 없을 때 | non-empty이면 `npm install -g @vscode/ripgrep` best-effort 시도를 막고 pure-Go fallback 사용; 기본 unset은 설치 시도 허용 |
| `ANTHROPIC_BASE_URL` | URL, public Anthropic endpoint | Norma provider; profile/base 또는 `ARTEX_LLM_BASE_URL`이 비어 있을 때만 | Anthropic-compatible endpoint fallback |
| `OPENAI_BASE_URL` | URL, `https://api.openai.com/v1` | Norma provider; profile/base 또는 `ARTEX_LLM_BASE_URL`이 비어 있을 때만 | OpenAI Chat/Responses endpoint fallback |

Norma도 `ANTHROPIC_API_KEY`와 `OPENAI_API_KEY` fallback을 갖지만 ARTEX가 같은 이름을 먼저 읽으므로 13개 application env 표에 한 번만 센다. [Norma provider/tools](evidence:norma-runtime-env)

두 ripgrep env가 모두 unset인 기본 상태에서 `Grep` 첫 호출은 PATH의 `rg`를 찾고, 없지만 `npm`은 있으면 version을 pin하지 않은 `npm install -g @vscode/ripgrep`를 ARTEX process 권한으로 한 번 실행한다. 이는 외부 registry network와 npm global prefix를 쓸 수 있다. 설치·탐색·실행 실패는 사용자에게 오류로 올리지 않고 pure-Go search로 fallback한다. `Grep`의 read-only/auto-allow metadata와 이 최초 설치 효과의 차이는 [도구 계약](tool-catalog.md#grep-bootstrap)에 기록한다.

<a id="ambient-env"></a>
### Inherited ambient environment

Norma `Bash`, interactive shell, custom `command`, MCP stdio child는 process environment를 상속한다. `HTTP_PROXY`/`http_proxy`, `HTTPS_PROXY`/`https_proxy`, `NO_PROXY`/`no_proxy`는 explicit WebFetch/search proxy가 비어 있을 때 Go default transport에도 영향을 줄 수 있고 child CLI도 각자의 규칙으로 읽는다. `ALL_PROXY`/`all_proxy`는 주로 child CLI가 해석한다. ARTEX LLM client와 recording proxy는 explicit proxy가 비면 direct transport를 구성하므로 이 ambient proxy fallback을 사용하지 않는다.

`PATH`, certificate variables, locale처럼 임의 child process가 읽는 OS environment 전체는 유한한 ARTEX 설정 schema가 아니다. 위 6개 explicit dependency env와 분리하며 고정 완전성 분모에 넣지 않는다.

<a id="config-json"></a>
## `config.json`: 8개 leaf field

| JSON path | 타입·기본값 | 필수/override | 소비자·효과 |
|---|---|---|---|
| `database.dsn` | string, empty | `ARTEX_PG_DSN`이 우선 | 값이 있으면 component field 대신 그대로 연결에 사용 |
| `database.host` | string, 조립 시 `127.0.0.1` | DSN이 없을 때만 | component DSN host |
| `database.port` | integer, 조립 시 `5432` | DSN이 없을 때만 | component DSN port |
| `database.user` | string, empty | DSN이 없을 때만 | component DSN user |
| `database.password` | secret string, empty | DSN이 없을 때만 | URL userinfo password; log에는 redact |
| `database.dbname` | string, empty | DSN이 없을 때만 | component DSN path |
| `database.sslmode` | string, 조립 시 `disable` | DSN이 없을 때만 | component DSN query |
| `skill_dir` | path string, `<BaseDir>/skills` | `ARTEX_SKILL_DIR`가 우선 | Skill filesystem root |

Component DSN 조립은 `host`, `dbname`, `user` 중 하나라도 non-empty이면 시작한다. 나머지 필수성을 미리 검증하지 않으므로 불완전한 조합은 DSN 생성 후 DB 연결에서 실패할 수 있다. Missing/unreadable/invalid JSON은 zero config, unknown field는 ignored다. 상대 `skill_dir`는 process CWD 기준이다. [config](evidence:config)

<a id="operator-settings"></a>
## Operator-managed `settings`: 31개

`settings.value`는 string이지만 소비자가 아래 effective type으로 parse한다. Key가 없거나 값이 invalid일 때의 code fallback을 기본값으로 적었다.

### 일반 runtime/UI: 21개

| key | effective type·기본값 | 소비자·적용 시점 | 조건·효과 |
|---|---|---|---|
| `traffic_capture` | bool, false | manager; 저장 뒤 agent 재조립 | recorder가 실제 열렸을 때 proxy·CA·traffic tool/prompt 활성화 |
| `agent_traffic_binding` | bool, false | finding workflow, 호출 시 조회 | report/finding evidence workflow의 traffic binding 허용 |
| `web_search_enabled` | bool, false | agent assembly | true이고 backend credential 조건을 만족하면 `web_search` 주입 |
| `web_search_backend` | string, `ddgs` | web-search config | `ddgs`, `brave-free`, `tavily`, `deepseek`가 구현된 backend |
| `brave_search_api_key` | secret string, empty | `brave-free` backend | 비어 있으면 해당 backend의 tool을 비활성화 |
| `tavily_search_api_key` | secret string, empty | `tavily` backend | 비어 있으면 해당 backend의 tool을 비활성화 |
| `web_search_proxy` | proxy URL string, empty | search provider | search egress만 route; 저장 뒤 agent 재조립 |
| `global_proxy` | proxy URL string, empty | manager/Bash/WebFetch/recording proxy | HTTP/HTTPS/SOCKS5만 허용; capture on이면 recorder upstream, off면 agent egress에 주입 |
| `workers` | integer, 3 | engine `Run`마다 | `>0`; 이후 task의 concurrent work-agent 수 |
| `llm_record` | bool, false | recorder가 각 LLM call에서 in-memory flag 조회 | 즉시 request/response ledger 기록 전환 |
| `llm_pool_enabled` | bool, false | provider-chain build | unbound/global profile failover chain 활성화; 저장 뒤 provider 재조립 |
| `llm_pool_bind_fallback` | bool, false | provider-chain build | pool이 켜진 경우 bound profile 실패도 chain으로 fallback |
| `task_concurrency_enabled` | bool, false | scheduler | false면 동시 실행 task cap 없음; 변경 즉시 reconcile |
| `task_concurrency_limit` | integer, 5 | scheduler | enabled일 때 최소 1; API의 `<1` 입력은 5로 보정 |
| `noa_compaction` | bool, false | agent run 시작마다 | 이후 run이 built-in compaction 대신 Noa adapter 사용 |
| `constraints_inject_planner` | bool, true | planner 각 round | task allow/deny constraint를 planner system prompt에 주입 |
| `constraints_inject_worker` | bool, true | worker 각 round | task allow/deny constraint를 worker system prompt에 주입 |
| `python_interpreter` | path/command string, auto-detect | custom `script` tool 호출 | empty면 `python3`, 다음 `python`을 PATH에서 찾고 startup에 감지값을 seed |
| `notify_enabled` | bool, true | notifier 매 tick | 모든 notification delivery의 global kill switch |
| `notify_public_base_url` | HTTP(S) URL string, empty | notifier 매 delivery | finding link base; empty면 link button 없음; 저장 시 trailing slash 제거 |
| `notify_digest_interval_min` | integer, 30 | notifier 매 tick | API 허용 1–1440분; invalid stored value는 30분 |

일반 settings handler의 secret 응답은 API 계약을 따른다. 예를 들어 Brave/Tavily 값은 그대로 돌려주지 않고 configured 여부만 노출한다. [manager](evidence:manager) [settings handler](evidence:runtime-settings-server)

### Intercept·judge: 8개

| key | effective type·기본값 | 소비자·적용 시점 | 조건·효과 |
|---|---|---|---|
| `intercept_enabled_tools` | JSON string array; `Bash`, `WebFetch`, `web_search`, `shell_open`, `shell_send`, `Write`, `Edit`, `MultiEdit` | Interceptor cache | 저장 API가 cache를 invalidate; invalid JSON은 empty set으로 해석 |
| `llm_judge_enabled` | bool, false | unmatched intercept 판정마다 fresh read | true이고 reviewer/profile이 있으면 rule miss를 model judge에 보냄 |
| `llm_judge_profile_id` | integer/int64, 0 | judge | 0이면 active/default profile |
| `llm_judge_prompt` | string, built-in `DefaultJudgePrompt` | judge | empty면 현재 built-in template; custom 값은 그대로 사용 |
| `llm_judge_timeout_seconds` | integer, 15 | judge | `<=0` 또는 invalid면 15초 |
| `llm_judge_fail_action` | enum `allow|ask|deny`, `allow` | judge failure/unparseable result | invalid면 `allow` |
| `llm_judge_ask_timeout_seconds` | integer, 300 | judge가 `ask` 반환 | `<=0` 또는 invalid면 300초 |
| `llm_judge_ask_timeout_action` | enum `allow|deny`, `deny` | pending judge timeout | invalid면 `deny` |

[interceptor settings](evidence:runtime-settings-intercept)

### Retry와 auth: 2개

| key | effective type·기본값 | 소비자·적용 시점 | 조건·효과 |
|---|---|---|---|
| `llm_retry_policy` | JSON object, all-zero means built-in defaults | provider build, breaker registry, intent/empty-turn retry | `connect`, `empty`, `stream`, `breaker`, `intent` 각각 `{attempts, interval_ms}`; attempts `0`=inherit, `-1`=disable, max 20; interval `0`=inherit, max 3,600,000ms; save가 provider와 breaker를 hot-apply |
| `auth.password_hash` | bcrypt string, unset | auth init/login/change와 reset script | unset이면 관리자 password 미초기화; 변경은 새 login 검증에 즉시 반영되지만 기존 JWT signing key를 회전시키지 않음 |

[retry policy](evidence:runtime-settings-retry) [auth setting](evidence:runtime-settings-auth)

<a id="internal-setting-markers"></a>
## Internal seed/migration marker: 24개

이 key들은 운영자가 조절하는 feature flag가 아니다. Startup의 one-shot seed/schema/prompt/binding 보정이 완료됐는지 기록한다. 임의 삭제는 다음 부팅에서 migration을 다시 실행할 수 있고, 값 변경은 오래된 row를 그대로 남길 수 있다.

| 소유자·완료 값 | exact key | 효과 |
|---|---|---|
| DB bootstrap; `true` 또는 `done` | `interactive_shell_default_v1`, `asset_intercept_default_rules_v1`, `intercept_default_rules_v1`, `intercept_default_rules_v2`, `intercept_default_rules_v3` | built-in agent shell default와 intercept rule seed |
| finding workflow; `true` | `finding_traffic_tools_v1`, `finding_workflow_tools_v3_host_search_description`, `finding_workflow_tools_v2_reporter`, `finding_retester_seed_v1` | finding tool schema/description, reporter, retester seed |
| tool schema/binding; `true` | `tool_schema_refresh_v7_list_facts_paging`, `goal_met_unbind_default_v1`, `auto_report_finding_v1`, `planner_report_finding_v1`, `planner_list_assets_v1`, `company_scope_rebind_v1`, `worker_readtools_unbind_v1`, `worker_readback_rebind_v2`, `auto_default_bindings_v3` | 기존 catalog row의 one-time schema/binding 보정 |
| prompt/agent seed; `true` | `goals_prompt_constraint_step_v1`, `mainagent_prompt_goalless_intent_v1`, `planner_prompt_compact_realistic_v2`, `worker_prompt_compact_v4`, `reporter_trigger_evidence_version_v1`, `reporter_agent_seed_v1` | built-in prompt version, reporter trigger/agent one-time seed |

Marker는 generic `settings` table에 함께 저장되지만 operator settings 31개 분모와 별도다. [DB bootstrap markers](evidence:runtime-settings-markers) [server seed markers](evidence:runtime-tool-seeds)

<a id="frontend-env"></a>
## 별도 frontend runtime/build env: 5개

| 이름 | 기본 | 소비자·효과 |
|---|---|---|
| `NEXT_EXPORT` | unset | `1`이면 static export와 unoptimized image, trailing slash 활성화 |
| `NEXT_PUBLIC_MOCK` | unset | `1`이면 mock UI, auth mock token, backend rewrite 우회 |
| `AUTOPENTEST_API` | `http://localhost:8787` | non-export/non-mock dev `/api/*` rewrite target |
| `NEXT_PUBLIC_SSE_BASE` | dev browser의 `http(s)://<hostname>:8787`, production empty | browser에 공개되는 SSE base override |
| `NODE_ENV` | Next가 설정 | production console 제거와 dev SSE fallback 분기 |

이는 Go application runtime env 13개에 포함하지 않는다. `NEXT_PUBLIC_*`는 build/client bundle에 노출될 수 있으므로 secret을 넣으면 안 된다. [Next config](evidence:web-next-config)

<a id="skill-owned-env"></a>
## Skill-owned script env

Shipped `skills/api-recon/scripts/runtime_harvest.js`는 `CHROMIUM`, `HTTP_PROXY`, `HTTPS_PROXY`를 읽고 child process에 proxy를 넘기며 `NODE_TLS_REJECT_UNAUTHORIZED=0`을 설정한다. 이는 core ARTEX 설정이 아니라 특정 Skill script의 실행 계약이다. 설치한 Skill·MCP·custom command는 각자 추가 환경변수를 정의할 수 있으므로 동적 capability의 환경 입력은 고정 core 분모에 넣지 않는다.
