Skip to content

Latest commit

 

History

History
36 lines (34 loc) · 10.2 KB

DECISION_LOG.md

File metadata and controls

36 lines (34 loc) · 10.2 KB

Decision Log

구현 중 SPEC.md에 명시되지 않은 사항을 자체 결정한 경우 여기에 기록한다.

# 날짜 결정 사항 이유 대안
1 2026-06-14 _start_cloudflared에서 select.select로 stderr 읽기 (최대 8초) cloudflared는 stdout/stderr를 동시에 사용하므로 blocking read 대신 non-blocking poll 필요. URL이 8초 내 나타나지 않으면 "URL 추출 실패" 반환 asyncio 기반 subprocess read (단일 파일 내 복잡도 증가)
2 2026-06-14 TunnelInfo.track_activity 기본값 True, open_tunnel 호출 시 현재 서버 포트(_current_cfg_port)와 비교하여 설정 SPEC §2.1에 "웹 서버 포트만 track_activity=True"라고 명시되어 있음. open_tunnel이 cfg를 받지 않으므로 전역 변수 _current_cfg_port 사용 open_tunnel(port, track_activity) 시그니처 변경
3 2026-06-14 cerberus_ctl.py exec는 websockets 라이브러리 사용, /api/agent/exec 엔드포인트 없이 직접 /ws/<session_id>?token=<token> WS 연결 SPEC §7.6에 명시된 동작. exec API 없음. macros 명령은 /api/macros에 X-API-Token 헤더 포함하여 요청 (SPEC에 macros 인증 방식 미명시, 단순화 선택) POST /api/agent/exec 별도 엔드포인트 구현
4 2026-06-14 MacrosHandler.post에서 send="" 항목 허용 (구분선 표현) SPEC §6.4 "send = '' → 구분선으로 렌더링"이므로 유효성 검사에서 허용해야 함 send 필드 필수 값 검증
5 2026-06-14 _ActivityTermSocket을 terminado NamedTermManager 방식으로 구현. 세션별 TermSocket 서브클래스 대신 TerminalWsHandler (tornado.websocket.WebSocketHandler 상속)로 WS 멀티플렉서 직접 구현 terminado의 TermSocket은 단일 터미널 단일 클라이언트 가정이 강함. 다중 클라이언트 브로드캐스트를 위해 직접 WS 핸들러 구현이 더 단순함 terminado TermSocket 그대로 상속 후 on_pty_read 오버라이드
6 2026-06-14 /proc fallback의 cpu_pct는 항상 0.0 반환 /proc/stat를 두 번 폴링해야 CPU 사용률을 계산할 수 있으나 SPEC에서 "첫 호출 0.0 허용(psutil 비블로킹 특성)"을 명시하므로 /proc 경로에서도 동일하게 처리 /proc/stat 2회 폴링 (100ms sleep 필요, blocking)
7 2026-06-14 cb_device 쿠키 서명: HMAC(device_key, payload_b64)[:32] 단순 구현 SPEC에 서명 알고리즘 미명시. tornado secure_cookie와 별도 키(device_key)를 사용해야 하므로 SHA256 기반 자체 서명 선택 tornado의 create_signed_value 활용
8 2026-06-14 AgentNewSessionHandler에서 terminado의 new_named_terminal() API 사용 terminado NamedTermManager의 표준 API. 실제 pty 생성은 terminado가 담당 직접 subprocess.Popen으로 pty 생성
9 2026-06-14 tornado.websocket.WebSocketHandler를 from tornado.websocket import WebSocketHandler as _TornadoWebSocketHandler로 별도 import tornado를 import tornado.web 등으로 import해도 tornado.websocket이 tornado 모듈의 속성으로 자동 노출되지 않아 AttributeError 발생. 명시적 from-import로 해결 import tornado.websocket 후 tornado.websocket.WebSocketHandler 직접 참조 (AttributeError 발생)
10 2026-06-14 IdleTimeoutApiHandler.post에서 minutes 범위를 max(0, min(1440, v))로 클램프 SPEC에 범위 미명시. 0=비활성, 최댓값 1440(24시간)은 실용적 상한 제한 없이 임의 값 허용
11 2026-06-14 _ensure_cloudflared에서 urlopen(url, timeout=N) → socket.setdefaulttimeout(N) + urlopen(url) 방식으로 변경 테스트 mock _side(url)이 url 1개 인자만 받으므로 timeout= 키워드 전달 시 TypeError 발생. socket 레벨 timeout 설정으로 동일 효과, 원래/복원 패턴으로 thread-safety 유지 urlopen wrapper 함수 별도 정의
12 2026-06-14 AgentAuthMixin._check_session_cookie()에서 getattr(self, 'get_secure_cookie', None) 가드 추가 테스트에서 AgentAuthMixin()을 독립 인스턴스화하므로 Tornado handler 메서드가 없음. getattr 가드로 AttributeError 방지, 실제 핸들러에선 정상 동작 hasattr 체크 후 호출
13 2026-06-14 JS WS URL을 ws:// 고정 → location.protocol === 'https:' ? 'wss:' : 'ws:' 자동 선택 Cloudflare Quick Tunnel은 HTTPS/WSS 전용. ws://로 연결 시 브라우저가 차단 wss:// 고정 (로컬 http 환경 미지원)
14 2026-06-14 loadSessions()에서 세션 없을 때 POST /api/agent/session 자동 호출로 터미널 세션 생성 브라우저 UI 접속 즉시 터미널이 표시되어야 함. 수동 "+" 버튼 없이도 기본 세션 생성 사용자가 직접 "+" 버튼 클릭하여 생성
15 2026-06-14 멀티파일 전환: cerberus_beacon.py (2603줄) → src/ 패키지 (9모듈 + src/bots/ 2모듈) 단일 파일 원칙 폐기 결정. 유지보수성·테스트 독립성 향상. 순환 임포트 방지를 위해 공유 상태는 소유 모듈에 선언, 다른 모듈이 import. run_server는 src/web.py에 배치하여 모든 모듈을 집결하는 단일 진입 지점 역할 기존 단일파일 유지
16 2026-06-14 src/terminal.py에서 _ActivityTermSocket.open() 내부에서 from src.agent import _agent_api_token 지연 import src/agent.py가 src/terminal.py를 import하고 src/terminal.py가 src/agent.py를 import하면 순환 임포트 발생. 해결책으로 _agent_api_token 참조를 함수 호출 시점까지 미룸 src/notify.py 별도 모듈로 공유 토큰 분리
17 2026-06-14 src/web.py에 TunnelOpenHandler, TunnelCloseHandler, TunnelStatusHandler, IdleTimeoutApiHandler 배치 이 핸들러들은 src/tunnel.py의 함수를 호출하고 AuthMixin(src/auth.py)을 상속. src/tunnel.py에 배치하면 src/auth.py를 역방향 import해야 하므로 순환 임포트 위험. 상위 집결 모듈인 src/web.py에 배치로 해결 별도 src/tunnel_api.py 모듈 생성
18 2026-06-14 tests/conftest.py에서 terminado mock 등록 (tornado는 실제 설치) 테스트 환경에 terminado 미설치 시 src/terminal.py import 실패 방지. tornado는 실제 설치되어 있으므로 mock 불필요 각 test 파일에서 개별 mock 등록
19 2026-06-20 설정 저장소를 config.toml → .env로 전환 의존성(tomli) 제거, 환경변수와 1:1 매핑으로 단순화. _load_dotenv()가 미설정 키만 채워 OS 환경변수 우선 유지 tomllib 기반 config.toml 유지
20 2026-06-20 Named Tunnel 미구현 확정 — CB_TUNNEL_*는 파싱만, 분기 없음 사용자 결정("구현 불가, 의미 없음"). Quick Tunnel만으로 목적 달성 cloudflared tunnel run 분기 구현
21 2026-06-20 실 WS 핸들러 _ActivityTermSocket.open()에 토큰+OTP 2FA + agent_enabled 검증 추가, 데드코드 TerminalWsHandler 제거 #5/#16 당시 TerminalWsHandler가 2FA를 구현했으나 라우팅되지 않아 토큰만 검사하는 _ActivityTermSocket이 실제 동작 → OTP 우회 보안 결함. 실 핸들러에 2FA 이관 라우팅을 TerminalWsHandler로 교체 (terminado 다중 클라이언트 미지원)
22 2026-06-20 POST /api/macros를 세션 쿠키 전용으로 제한 (에이전트 토큰 거부) SPEC §10 — 매크로 변경은 대화형 사용자만. GET은 AgentAuth 유지 GET/POST 모두 AgentAuth 허용
23 2026-06-20 web.py의 _MAIN_HTML(862줄) 등 데드코드 제거, UI는 templates/에서만 로드 상수가 참조되지 않는 순수 데드코드. templates 외부 파일과 이중 관리되어 혼선 데드코드 유지
24 2026-06-21 서버 유휴 종료 시 close_idle_tunnels()로 자동종료(⏱) 터널 차단, 영구(🔒) 유지 서버 종료 시 watchdog도 죽어 터널 무감시 노출 → "필요할 때만 노출" 가치 훼손. SIGTERM(의도적)은 터널 유지하여 재시작 복원과 구분 종료 시 전체 터널 차단 / 현행 유지
25 2026-06-21 stdout(on_pty_read)은 서버 활동(_activity)에 미포함 — 키 입력만 활동 사용자 결정. 무인 출력만으로 서버를 유지하지 않음. 브라우저 탭 폴링이 평소 방어 stdout도 서버 활동으로 간주
26 2026-06-21 tunnels.json 원자적 쓰기(tmp+os.replace), _idle_watchdog 메트릭 폴링 ThreadPoolExecutor 병렬화, 봇/CLI idle 변경 save_config 영속화, _bf_counters 만료 정리 멀티프로세스 파일 손상·폴링 블로킹·설정 영속성 불일치·브루트포스 카운터 누수 해소 각 항목 현행 유지
27 2026-06-27 shell_wrapper.py 종료 상태 판정 시 bash 관례 exit code 143(128+SIGTERM)·137(128+SIGKILL)을 "killed"로 처리 os.killpg(SIGTERM) 수신 시 bash·uv가 returncode < 0이 아닌 128+signal로 exit하는 경우가 있음. 기존 returncode < 0 체크만으로는 "failed"로 오분류됨 returncode < 0 만 체크 (bash 관례 무시)
28 2026-06-27 Telegram 봇 Shell 명령 포맷 함수(_job_notify_text, _update_watching, _format_jobs_list, _format_log_reply)를 모듈 레벨 순수 함수로 추출 테스트 가능성 확보. 클로저 내부 함수는 직접 단위 테스트 불가. 순수 함수로 추출하면 의존성 없이 import해서 검증 가능 봇 루프 내부 중첩 함수로 유지 (테스트 불가)
29 2026-06-27 xterm.js 5.3.0 + FitAddon 0.8.0을 src/static/에 로컬 벤더링 (CDN 제거) 인바운드 불가 서버(NAT/방화벽 환경)에서 CDN 접근이 차단될 수 있음. 오프라인 또는 폐쇄망에서도 웹 UI가 완전히 동작해야 함 jsDelivr CDN 유지
30 2026-06-29 cloudflared 다운로드 URL을 _cf_asset_name() 함수로 동적 결정 (platform.system() + platform.machine()) Linux amd64 하드코딩으로 macOS(arm64/x86_64), Linux arm64 환경에서 바이너리 불일치 발생. POSIX 공통 API(os.killpg, PTY 등)는 macOS에서 동일 동작하므로 URL 변경만으로 macOS 지원 가능 OS별 별도 다운로드 안내 문서화