virtual-insanity
← 리포트 목록

Bot tokens + Auth credential Hermes 이전

2026-04-15 migrate [phase17-followup, migration, hermes, auth, tokens]

결론

  • OpenClaw 원본은 유지했고, 필요한 credential 파일을 Hermes 쪽으로 복사했다.
  • 복사 대상:
  • /Users/ron/.openclaw/bot_tokens.json/Users/ron/.hermes/bot_tokens.json
  • /Users/ron/.openclaw/auth/profiles/*/Users/ron/.hermes/auth/profiles/*
  • 권한:
  • /Users/ron/.hermes/bot_tokens.json: 600
  • /Users/ron/.hermes/auth: 700
  • /Users/ron/.hermes/auth/profiles: 700
  • profile 파일들: 600
  • Hermes workspace scripts의 bot token 참조는 이미 .hermes/bot_tokens.json으로 rewrite 되어 있었다.
  • auth/profiles/github-copilot.json을 직접 참조하는 Hermes workspace script는 발견되지 않았다.
  • 현재 Hermes agent 계열은 Copilot credential을 COPILOT_GITHUB_TOKEN/GH_TOKEN/GITHUB_TOKEN 또는 ~/.hermes/auth.json 경로로 찾는 구조다.
  • 이번 복사는 OpenClaw compatibility/rollback용 credential 이전으로 판단된다.
  • claude_telegram_listener.py는 아직 OpenClaw launchd가 띄운 PID 61612가 OpenClaw 경로를 사용 중이다. Hermes 쪽 listener 파일은 .hermes/bot_tokens.json을 보도록 rewrite 되어 있지만, launchd cutover는 아직 안 됐다.

1. 참조 grep 결과

bot_tokens.json 참조 — Hermes workspace/scripts

/Users/ron/.hermes/workspace/scripts/codex_telegram_listener.py:32:_TOKENS_PATH = Path.home() / ".hermes" / "bot_tokens.json"
/Users/ron/.hermes/workspace/scripts/shared/telegram_multibot.py:17:_CONFIG_PATH = Path.home() / ".hermes" / "bot_tokens.json"
/Users/ron/.hermes/workspace/scripts/claude_telegram_listener.py:43:_TOKENS_PATH = Path.home() / ".hermes" / "bot_tokens.json"

판정:

파일 참조 방식 실제 runtime path
codex_telegram_listener.py Path.home() / ".hermes" / "bot_tokens.json" /Users/ron/.hermes/bot_tokens.json
shared/telegram_multibot.py Path.home() / ".hermes" / "bot_tokens.json" /Users/ron/.hermes/bot_tokens.json
claude_telegram_listener.py Path.home() / ".hermes" / "bot_tokens.json" /Users/ron/.hermes/bot_tokens.json

즉 absolute literal은 아니지만, home-relative absolute path로 안전하게 Hermes 파일을 가리킨다.

github-copilot.json / auth profiles 참조

Hermes workspace/scripts에서 auth/profiles/github-copilot.json 직접 참조는 없음.

관련 grep에서는 Copilot model label만 다수 발견됐다.

예:

/Users/ron/.hermes/workspace/scripts/agent_registry.py:36:    "github-copilot/gpt-5-mini"
/Users/ron/.hermes/workspace/scripts/agent_queue_worker.py:110:    fallback_chain = ["github-copilot/gpt-5-mini", "openai-codex/gpt-5.4", "ollama/qwen2.5:3b"]
/Users/ron/.hermes/workspace/scripts/shared/llm.py:487:    if model_id.startswith("github-copilot/"):

shared/llm.py의 GitHub/Copilot 계열 token 로딩은 profile JSON이 아니라 다음 순서다.

_token = os.environ.get("GITHUB_TOKEN", "")
if not _token:
    gh auth token
if not _token:
    ~/.hermes/.env 의 OPENCLAW_TOKEN

Hermes agent 본체 쪽도 github-copilot.json 직접 참조는 없고, Copilot provider 문서/코드는 다음 방식을 사용한다.

COPILOT_GITHUB_TOKEN / GH_TOKEN / GITHUB_TOKEN
~/.hermes/auth.json

따라서 이번에 복사한 /Users/ron/.hermes/auth/profiles/github-copilot.json은 현재 active lookup 경로라기보다 OpenClaw 호환/롤백 credential 보존으로 보는 게 맞다.

2. 복사 결과

원본 메타데이터:

-rw-------  1 ron  staff  106 Mar 10 19:46 /Users/ron/.openclaw/auth/profiles/github-copilot.json
-rw-------  1 ron  staff  107 Mar 10 19:43 /Users/ron/.openclaw/auth/profiles/github-copilot.json.bak-20260310194317
-rw-------@ 1 ron  staff  106 Apr 15 12:54 /Users/ron/.openclaw/auth/profiles/github-copilot.json.bak-403-20260415-125406

복사 후 target:

drwx------@ 3 ron  staff   96 Apr 15 13:42 /Users/ron/.hermes/auth
drwx------@ 5 ron  staff  160 Apr 15 12:54 /Users/ron/.hermes/auth/profiles
-rw-------@ 1 ron  staff  106 Mar 10 19:46 /Users/ron/.hermes/auth/profiles/github-copilot.json
-rw-------@ 1 ron  staff  107 Mar 10 19:43 /Users/ron/.hermes/auth/profiles/github-copilot.json.bak-20260310194317
-rw-------@ 1 ron  staff  106 Apr 15 12:54 /Users/ron/.hermes/auth/profiles/github-copilot.json.bak-403-20260415-125406
-rw-------@ 1 ron  staff  525 Apr 15 13:42 /Users/ron/.hermes/bot_tokens.json

파일 크기 비교:

github-copilot.json src_size= 106 dst_exists= True dst_size= 106
github-copilot.json.bak-20260310194317 src_size= 107 dst_exists= True dst_size= 107
github-copilot.json.bak-403-20260415-125406 src_size= 106 dst_exists= True dst_size= 106

3. 권한/소유권 확인

bot_mode= 0o600 owner_uid= 501
copilot_mode= 0o600 owner_uid= 501

소유자는 현재 사용자 ron이며, 민감 파일은 600으로 설정했다.

4. 라이브 파싱 테스트 — 마스킹 출력

bot_tokens.json

bot_tokens_path= /Users/ron/.hermes/bot_tokens.json
bot_tokens_type= dict
bot_count= 4
bot= claude bot_name= CLAUDE CODE(LOCAL) username= openclaw_harybot token_mask= 83820953...GkLw
bot= claude_bridge bot_name= CLAUDE CODE BRIDGE(LOCAL) username= ronbridgebot token_mask= 87122366...I__U
bot= codex bot_name= CODEX(LOCAL) username= ronclawBot token_mask= 84231387...WFCU
non_bot_entry= collab_chat_id type= int mask= -1003522...8967
bot_token_entry_count= 3

판정:

  • JSON parse OK.
  • 봇 token dictionary 3개 확인.
  • 추가 scalar entry collab_chat_id 1개 존재.
  • 토큰 값은 보고서에 평문 노출하지 않고 prefix/suffix만 마스킹했다.

github-copilot.json

copilot_path= /Users/ron/.hermes/auth/profiles/github-copilot.json
copilot_keys= ['token']
copilot_token_mask= github...ZtKG
copilot_mode= 0o600 owner_uid= 501
profile_files= ['github-copilot.json', 'github-copilot.json.bak-20260310194317', 'github-copilot.json.bak-403-20260415-125406']

판정:

  • JSON parse OK.
  • token key 존재.
  • 값은 마스킹 확인만 수행.

5. claude_telegram_listener 의존성 판단

현재 OpenClaw launchd listener는 여전히 OpenClaw 경로를 사용 중이다.

gui/501/com.openclaw.claude-listener = {
    state = running
    program = /usr/bin/python3
    arguments = {
        /usr/bin/python3
        -u
        /Users/ron/.openclaw/workspace/scripts/claude_telegram_listener.py
    }
    working directory = /Users/ron/.openclaw/workspace
    pid = 61612
    last exit code = (never exited)
}

소스 token path 비교:

/Users/ron/.openclaw/workspace/scripts/claude_telegram_listener.py:43:_TOKENS_PATH = Path.home() / ".openclaw" / "bot_tokens.json"
/Users/ron/.hermes/workspace/scripts/claude_telegram_listener.py:43:_TOKENS_PATH = Path.home() / ".hermes" / "bot_tokens.json"
/Users/ron/.hermes/workspace/scripts/codex_telegram_listener.py:32:_TOKENS_PATH = Path.home() / ".hermes" / "bot_tokens.json"
/Users/ron/.hermes/workspace/scripts/shared/telegram_multibot.py:17:_CONFIG_PATH = Path.home() / ".hermes" / "bot_tokens.json"

판정:

  • PID 61612의 live listener는 아직 OpenClaw 소유이며 /Users/ron/.openclaw/bot_tokens.json을 읽는다.
  • Hermes workspace에는 listener 사본이 있고 .hermes/bot_tokens.json을 보도록 rewrite 되어 있다.
  • 하지만 launchd plist 자체가 아직 /Users/ron/.openclaw/workspace/scripts/claude_telegram_listener.py를 가리키므로, listener cutover는 별도 작업으로 남아 있다.
  • 지금 원본 ~/.openclaw/bot_tokens.json을 유지한 판단은 맞다. 이걸 삭제하면 PID 61612 listener가 깨질 수 있다.

6. Hermes Gateway와 bot_tokens.json 관계

추가 맥락과 확인을 합치면:

  • Hermes Gateway는 현재 ~/.hermes/.envTELEGRAM_BOT_TOKEN을 사용한다.
  • ~/.hermes/.env에서 확인된 관련 key:
env_exists= True keys_of_interest= ['TELEGRAM_ALLOWED_USERS', 'TELEGRAM_BOT_TOKEN']

따라서 Gateway 자체는 bot_tokens.json에 직접 의존하지 않는다. bot_tokens.json은 listener/bridge 계열 스크립트의 dependency다.

7. 변경 파일

생성/복사:

  • /Users/ron/.hermes/bot_tokens.json
  • /Users/ron/.hermes/auth/profiles/github-copilot.json
  • /Users/ron/.hermes/auth/profiles/github-copilot.json.bak-20260310194317
  • /Users/ron/.hermes/auth/profiles/github-copilot.json.bak-403-20260415-125406

수정하지 않은 것:

  • /Users/ron/.openclaw/bot_tokens.json
  • /Users/ron/.openclaw/auth/profiles/*
  • Hermes/OpenClaw scripts 코드
  • LaunchAgents

8. 남은 작업

  1. com.openclaw.claude-listener cutover 결정 필요
  2. 현 상태 유지: OpenClaw listener 계속 사용, OpenClaw bot_tokens 유지 필요.
  3. Hermes 이전: launchd plist를 Hermes script 경로로 바꾸고, /Users/ron/.hermes/bot_tokens.json을 사용하게 전환.
  4. Copilot credential active path 정리 필요
  5. copied profile은 현재 direct lookup 경로가 아니다.
  6. Hermes agent는 COPILOT_GITHUB_TOKEN 또는 ~/.hermes/auth.json 방식이 canonical이다.
  7. 필요하면 github-copilot.json의 token을 .hermes/.envCOPILOT_GITHUB_TOKEN 또는 Hermes auth store로 import하는 별도 작업이 필요하다.

자체평가

  • 정확성: 4.7/5 — 요청 파일 복사/권한/파싱/참조 판단 완료.
  • 완성도: 4.6/5 — listener cutover는 범위 밖으로 남겼고, 현 의존성은 명확히 기록.
  • 검증: 4.8/5 — grep, metadata, chmod, JSON parse, masked token 확인 수행.
  • 최소 변경: 5.0/5 — credential 복사와 권한 설정만 수행, 코드/원본/LaunchAgent 수정 없음.

종합: 4.8/5.