# PR-Hermes-Sandbox-Env-Propagation-1 Closure Report

**Date**: 2026-05-17
**선행 PR**: PR-Hermes-Docsrv-Search-1, PR-Hermes-WebSearch-1, PR-Hermes-ToolCall-Adapter-1
**범위**: 1-line config 변경 (`terminal.env_passthrough` allowlist)
**파일**: `~/.hermes/config.yaml`

## Summary

PR-Hermes-Docsrv-Search-1 / PR-Hermes-WebSearch-1 의 user-facing E2E 마지막 조각. PR-Hermes-ToolCall-Adapter-1 closure 시 Layer 2 Hermes chat 의 `execute_code` 샌드박스가 HERMES_DOCSRV_TOKEN env 를 inherit 못 함 → DS API 401 발견. Hermes 의 `terminal.env_passthrough` 메커니즘 (skill 의 `required_environment_variables` 또는 user-config allowlist 가 sandbox 의 env 스트립을 우회) 에 1줄 추가로 해결.

## Root cause

`tools/env_passthrough.py` docstring 정리:
- `execute_code` / `terminal` 샌드박스는 기본적으로 모든 env 변수를 strip (보안)
- 두 가지 path 로 allowlist 등록 가능:
  1. **Skill `skill_view` tool_call** 시 → `register_env_passthrough(required_environment_variables)` 자동 발화
  2. **User config `terminal.env_passthrough`** → 영구 allowlist

**기존 docsrv_ask SKILL.md 의 frontmatter**:
```yaml
prerequisites:
  commands: [curl, jq]
  env: [HERMES_DOCSRV_TOKEN]
```

→ legacy `env_vars` 형식. `skill_view` 가 호출되어야 변환 + register. 그러나 `hermes chat -s docsrv_ask` preload 는 **system prompt inject 만**, `skill_view` 호출 발화 안 됨 → allowlist 등록 0 → sandbox 가 HERMES_DOCSRV_TOKEN strip → 401.

## Fix

`~/.hermes/config.yaml` 의 terminal section 1줄 변경:

```yaml
terminal:
  ...
  env_passthrough:
    - HERMES_DOCSRV_TOKEN  # PR-Hermes-Sandbox-Env-Propagation-1
  ...
```

**보안 검증** (GHSA-rhgp-j443-p4rf 정합):
- `_HERMES_PROVIDER_ENV_BLOCKLIST` 가 ANTHROPIC_*, OPENAI_*, CLAUDE_API_KEY 등 Hermes-managed provider 토큰 차단
- HERMES_DOCSRV_TOKEN 은 user-managed 토큰 (voice-memo-bot account JWT) → blocklist 외 → 안전

## 검증

### 1. Direct config verification

```bash
$ HERMES_DOCSRV_TOKEN=... ~/.hermes/hermes-agent/venv/bin/python -c '
from tools.env_passthrough import is_env_passthrough, _load_config_passthrough
print(_load_config_passthrough())
print(is_env_passthrough("HERMES_DOCSRV_TOKEN"))
print(is_env_passthrough("CLAUDE_API_KEY"))
'

parent process has HERMES_DOCSRV_TOKEN: True (len=157)
config terminal.env_passthrough loaded: ['HERMES_DOCSRV_TOKEN']
HERMES_DOCSRV_TOKEN in allowlist: True              ✅
CLAUDE_API_KEY in allowlist (should be False): False  ✅
```

### 2. Hermes chat E2E (이전 401 시나리오 재현)

```
hermes chat -s docsrv_ask -q 'docsrv_ask 으로 내 자료에서 voice memo 관련 자료 찾아줘'
```

**Proxy 로그 trace** (이전 vs 현재):

| 단계 | 이전 (Adapter A only) | 현재 (Adapter A + env_passthrough) |
|---|---|---|
| Hermes Turn 1 | Gemma → execute_code(curl) | Gemma → execute_code(curl) |
| Sandbox env | HERMES_DOCSRV_TOKEN STRIPPED | HERMES_DOCSRV_TOKEN PROPAGATED ✅ |
| DS API call | HTTP 401 "유효하지 않은 토큰" | **HTTP 200** ✅ |
| DS RAG pipeline | (skip) | query_analyze + classifier + evidence + synthesis ALL RUN ✅ |
| Result | Hermes 401 loop (4 turn) | **Real corpus answer with 2 citations** |

**DS log 발췌** (보고서 작성 시점, voice memo query):
```
[INFO] query_analyze ok query='voice memo' conf=0.90 intent=semantic_search elapsed_ms=6692
[INFO] evidence ok query='voice memo' candidates=2 kept=2 elapsed_ms=10665
[INFO] classifier ok query='voice memo' verdict=sufficient (raw=partial) covered=1 missing=2 elapsed_ms=13076
[INFO] synthesis ok query='voice memo' evidence_n=2 answer_len=186 citations=2 conf=medium elapsed_ms=3948
[INFO] ask query='voice memo' results=10 evidence=2 cite=2 synth=completed conf=medium completeness=full refused=False total=17363
```

**DS synthesis 결과** (실제 corpus, hallucinated 아님):
> "The evidence mentions voice memos as one of the things to add joy to your day [2]. Additionally, there is a test voice memo related to the first review item for gas engineer studies [1]."

Citations:
- [1] "테스트 음성 메모입니다. 가스기사 학습 검토 항목 첫 번째" — 실제 DS memo (test-voice-memo)
- [2] "Voice memos, snail mail and your own private screening room." — "The Good List: 6 Things to Add Joy to Your Day" 문서

### 3. Layer 2 user-facing 응답

Hermes 가 DS 응답 받은 후 multi-turn agent loop 에서 추가 docsrv_ask 호출 (refinement) — 응답 합성에 ~5-10분 소요 (Mac mini 26B + 23000 input tokens). Background 중단했으나 모든 핵심 검증 (env propagation + DS 실 호출 + 실 citations) 통과.

## File changes

### Mac mini
- `~/.hermes/config.yaml`:
  - `terminal.env_passthrough: [] → [HERMES_DOCSRV_TOKEN]`
- `~/.hermes/config.yaml.pre-env-passthrough.20260517` (7일 안전망)

### 변경 없음
- SKILL.md (정상 동작 — sandbox env가 propagate 되므로 skill 본문 변경 불필요)
- mlx-proxy.py
- DS code
- Hermes gateway / launchagent

## 결정 사항

1. **PR-Hermes-Sandbox-Env-Propagation-1 = SHIPPED**:
   - Direct config verification PASS
   - Hermes chat E2E DS API 200 + real corpus citations 확보
   - **PR-1 / PR-2 user-facing E2E 마지막 조각 풀림**
2. **남은 운영 관심사** (별 트랙):
   - **PR-Hermes-Skill-Curl-Refine-1** (선택): docsrv_ask SKILL.md frontmatter 를 legacy `prerequisites.env` → 표준 `required_environment_variables` 로 마이그레이션. 효과 = `skill_view` 도 자동 registration. 본 config-level fix 와 중복 안전망. P3.
   - **PR-Hermes-Multi-Turn-Refinement-1** (선택): Gemma 가 첫 docsrv_ask 결과로 만족 못 하면 같은 query 를 refinement 와 함께 재호출 → multi-turn 길어짐. skill 본문에 "1회 호출 후 결과 그대로 사용" 강조. P3.
   - **PR-Hermes-MaxTokens-Followup**: 23320 input tokens (31 tools + 90 skills + persona) → 30s+ first-token. tools/skills 선택적 로딩. P3.

## 후속 트랙 (PR-1/2 user-facing E2E 진입 가능)

| 우선순위 | 트랙 | 이전 진입 조건 충족? |
|---|---|---|
| **이제 가능** | PR-1 Layer 2/3 user-facing E2E 검증 (Discord smoke) | ✅ (Adapter A + env_passthrough) |
| **이제 가능** | PR-2 Layer 2/3 user-facing E2E 검증 (Discord smoke, web_search 자율 호출) | ✅ |
| 별 트랙 | PR-Hermes-Answer-Policy-1 (출처 라벨 plugin-level 강제) | PR-1/2 user-facing 안정화 후 |
| 별 트랙 | PR-Hermes-FamilyACL-N | 진입 조건 미정 |

## 7일 안전망 (2026-05-24)

- Mac mini: `~/.hermes/config.yaml.pre-env-passthrough.20260517`
- 복귀 시: `terminal.env_passthrough` 리스트 비우기 (또는 백업 복원)

## 검증 commands (재실행)

```bash
# Direct config verify
ssh macmini "~/.hermes/hermes-agent/venv/bin/python -c 'from tools.env_passthrough import is_env_passthrough; print(is_env_passthrough(\"HERMES_DOCSRV_TOKEN\"))'"

# Hermes chat E2E (Discord 채널 입력으로 등가)
ssh macmini "HERMES_DOCSRV_TOKEN=... && ~/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main chat -s docsrv_ask -q '<query>'"

# DS API 호출 확인
ssh gpu "cd ~/Documents/code/hyungi_Document_Server && docker compose logs --since=5m fastapi | grep 'ask query'"
```
