본문으로 건너뛰기

JunkBox 코어 가이드

1. 프로젝트 정의

  • JunkBox는 Python 기반 멀티 앱 구조를 사용하는 AI-Native 운영 시스템임.
  • 공통 라이브러리 재사용, API 우선 설계, 운영 허브 집중화, Hub SQLite 공통 저장소, 앱별 SQLite 도메인 저장소, PostgreSQL 기반 잔여 도메인 저장소와 개발 문서 벡터 캐시를 조합하는 구조를 사용함.
  • 현재 구조에서 apps/hub-api는 상시 운영 허브이자 공통 인증 진입점, 공통 마스터 데이터 제공자 역할을 수행함.
  • 현재 구조에서 apps/raid-api는 WoW 레이드 운영 화면과 WCL 점수 동기화를 담당하는 독립 앱임.
  • 현재 구조에서 apps/chess-api는 Lichess Bot API, sleepzz-bot 기반 수 선택과 착수 근거 생성, WebSocket 실시간 로그 송출, 대국 이력 저장과 조회를 담당하는 독립 샌드박스 앱임.
  • 현재 구조에서 apps/jeevtrap는 Discord 기반 독립 실행형 툴킷 데몬이며 공통 AI 런타임, GitHub, Linkding, 시스템 명령 실행, 선택적 로컬 음성 호출 인터페이스를 조합하는 별도 앱임.

2. 기술 스택

2.1 백엔드

  • 언어는 Python 계열을 사용함.
  • 웹 프레임워크는 FastAPI임.
  • ORM 계층은 SQLModel과 SQLAlchemy 2 비동기 스택을 사용함.
  • PostgreSQL 드라이버는 asyncpg임.
  • SQLite 비동기 드라이버는 aiosqlite임.
  • 워크스페이스와 패키지 관리는 uv를 사용함.
  • 공통 애플리케이션 로깅은 loguru 기반 구조를 사용함.
  • 체스 착수 실행은 LangGraph 기반 구조를 사용함.
  • 레이드 점수 연동은 httpx 기반 외부 HTTP 호출과 GraphQL 요청을 사용함.
  • AI 개발 문서 지식 검색은 PostgreSQL pgvector와 OpenAI 호환 embeddings API를 사용함.

2.2 프런트엔드 및 정적 자산

  • 운영 화면 프런트엔드는 apps/hub-ui, apps/chess-ui, apps/raid-ui, apps/rename-ui, apps/workout-ui의 Vite 6 기반 React + TypeScript 프로젝트를 사용함.
  • FastAPI는 서버 렌더링 HTML 골격 위에 각 앱의 dist 정적 자산을 마운트하여 화면을 제공함.
  • 로컬 개발에서는 각 UI 앱의 Vite 개발 서버와 FastAPI 백엔드를 병행 실행할 수 있음.
  • hub 개발 흐름은 15001 프런트와 18001 백엔드 조합을 사용함.
  • chess 개발 흐름은 15002 프런트와 18002 백엔드 조합을 사용함.
  • workout 개발 흐름은 15003 프런트와 18003 백엔드 조합을 사용함.
  • rename 개발 흐름은 15006 프런트와 18006 백엔드 조합을 사용함.
  • raid 개발 흐름은 15009 프런트와 18009 백엔드 조합을 사용함.
  • 운영 배포 포트는 hub-api=8001, chess-api=8002, workout-api=8003, rename-api=8006, raid-api=8009 조합을 사용함.
  • 운영 배포 포트는 jeevtrap=8005를 추가로 사용함.
  • 공통 UI 자산은 libs/shared-ui에서 제공함.
  • 공통 앱 아이콘 자산은 libs/shared-uiapp-icons/{slug}/icon.svg, icon-180.png, icon-192.png, icon-512.png 구조로 제공함.
  • 운영 화면 소스는 .ts.tsx만 사용하며, 신규 화면 로직을 .js로 직접 작성하지 않음.
  • TypeScript 컴파일은 strict 모드를 기준으로 유지함.
  • 운영 자산의 표준 서빙 흐름은 백엔드 포트가 빌드된 프런트 산출물을 직접 서빙하는 구조임.
  • 공통 shared-ui 정적 자산은 서버 템플릿이 버전 쿼리 파라미터를 붙여 링크하므로, 배포 후 CDN이 예전 고정 경로 CSS를 계속 재사용하지 않는 구조임.
  • 공통 CSS는 hub-ui-base.css와 화면 모드별 hub-ui-mobile.css 또는 hub-ui-desktop.css를 조합해서 로드하는 구조임.
  • 공통 CSS와 Tabulator vendor CSS 링크 선언은 libs/shared-ui의 stylesheet 매크로로 중앙화하며, 공통 베이스 템플릿과 standalone 템플릿은 같은 매크로를 호출함.
  • 공통 베이스 템플릿과 standalone 템플릿은 inline <style>을 포함하지 않으며, 모든 CSS는 libs/shared-ui 산출물에서 공급함.
  • 화면 폰트는 shared-ui가 제공하는 PretendardVariable.woff2hub-ui-base.css의 중앙 폰트 선언을 기준으로 통일함.
  • 앱 favicon, Apple touch icon, PWA manifest icon은 포털 메뉴의 APP_ICON + icon_val 관리값을 shared-ui 앱 아이콘 slug로 해석하여 같은 아이콘 세트를 사용함.

2.3 인증

  • JWT 기반 인증을 사용함.
  • 토큰 전달 경로는 Authorization: Bearer 헤더와 COOKIE_NAME 설정값 기준 HttpOnly 쿠키를 병행함.
  • 현재 기본 쿠키 이름은 JUNKBOX_AUTH임.
  • 비밀번호 처리는 passlibbcrypt 조합을 사용함.
  • 로그인 화면은 hub-api가 공통 진입점을 제공하고, 각 앱은 required_moduleredirect를 기준으로 복귀 흐름을 사용함.
  • 공통 로그인 화면은 마지막으로 로그인에 성공한 user_id를 별도 쿠키에 저장하고 다음 로그인 기본값으로 사용함.

3. 워크스페이스 구조

junkbox/
├── apps/
│ ├── hub-api/
│ ├── hub-ui/
│ ├── chess-api/
│ ├── chess-ui/
│ ├── raid-api/
│ ├── raid-ui/
│ ├── rename-api/
│ ├── rename-ui/
│ ├── workout-api/
│ ├── workout-ui/
│ └── jeevtrap/
├── libs/
│ ├── shared-core/
│ ├── shared-ai/
│ ├── shared-ui/
│ └── shared-voice/
├── scripts/
├── .env.shared
├── .env.local.template
├── pyproject.toml
└── uv.lock

3.1 apps

  • hub-api는 인증, 운영 허브, 관리자 화면, 공통 마스터 데이터 API를 담당함.
  • hub-ui는 hub 운영 화면 전용 React + TypeScript 프런트엔드 앱임.
  • chess-api는 Lichess challenge, account/game stream, sleepzz-bot 수 선택, LangGraph 체스 착수 실행, 체스 로그 저장, WebSocket 송출을 담당함.
  • chess-ui는 Lichess 상대 설정, 대국 시작, 실시간 에이전트 로그 관전, 저장된 게임 이력 조회를 담당하는 React + TypeScript 프런트엔드 앱임.
  • raid-api는 레이드 공대원 관리, 공대 배치, WCL 점수 동기화와 데스크톱 화면 진입점을 담당함.
  • raid-ui는 raid 운영 화면 전용 React + TypeScript 프런트엔드 앱임.
  • rename-api는 ASITE 기반 파일명 수집, Favorites, 로그, Rename 작업 API와 데스크톱 화면 진입점을 담당함.
  • rename-ui는 rename 운영 화면 전용 React + TypeScript 프런트엔드 앱임.
  • workout-api는 운동 도메인 API, 설치형 PWA 진입, 운동 기준정보 관리 API, 규칙 엔진 기반 운동 계획 API를 담당함.
  • workout-ui는 운동 기록 목록, 세션 상세, 운동 계획, 운동 관리의 모바일 전용 React + TypeScript 프런트엔드 앱임.
  • jeevtrap는 Discord 기반 독립 실행형 툴킷 앱이며 /md, /북마크, /명령, 선택적 로컬 음성 호출 인터페이스를 담당함.

3.2 libs

  • shared-core는 설정, 비동기 DB, 공통 모델, 인증, 예외, 공통 마스터 데이터 저장소, 공통 master-data 소비 클라이언트, active 포털 메뉴 조회와 TTL 캐시를 제공함.
  • shared-ai는 AI provider 어댑터, 기능별 모델 라우팅, usage log 기록, 체스 착수 그래프 실행, 공통 예외 정규화를 제공함.
  • shared-ai는 direct runner, graph runner, prompt registry, graph registry를 통해 상위 AI 호출 패턴까지 중앙화함.
  • shared-ui는 공통 템플릿, stylesheet 매크로, Tailwind preset, base / mobile / desktop 분리 CSS 자산, 공통 앱 셸 자산, 앱 아이콘 자산, 공통 React UI 유틸과 모바일 고정 폭 레이아웃 자산을 제공함.
  • shared-voice는 로컬 마이크 입력, STT, 호출어 감지, TTS, 음성 이벤트 런타임을 제공하는 공통 음성 라이브러리임.

4. 핵심 구조 원칙

4.1 운영 허브 집중화

  • 인증, 권한 체크, 공통 마스터 데이터 관리, 운영 관리자 화면은 hub-api에 집중함.
  • 다른 앱이 공통코드와 메타 원본을 자체적으로 소유하지 않음.
  • chess-api, raid-api, rename-api, workout-api를 포함한 다른 앱은 로그인 화면을 독자적으로 소유하지 않고 hub-api 공통 로그인 진입점을 사용함.
  • chess-api, raid-api, rename-api, workout-api는 자체 로그인 API와 credential 검증 API를 제공하지 않고, hub-api가 발급한 JWT를 소비함.
  • 다른 앱은 DB 기반 공통 master-data 로컬 원본 데이터를 직접 소유하지 않고 hub-api 내부 API를 통해 조회함.
  • AI 코딩 컨텍스트도 공통 master-data 로컬 원본을 직접 읽지 않고 hub-apiROLE_AI JWT 기반 통합 snapshot API를 통해 조회함.
  • workout 운동 계획 생성은 workout-api가 Workout SQLite 기준정보와 기록을 사용해 규칙 엔진으로 직접 수행함.
  • 체스 샌드박스는 Lichess 실시간 스트림과 sleepzz-bot 기반 수 제출을 chess-api가 직접 담당하고, AI provider 호출과 LangGraph 실행은 shared-ai를 통해 중앙화함.

4.2 공통 로직 중앙화

  • 설정, JWT/쿠키 유틸리티, 공통 인증 의존성, 예외, 공통 엔티티, 공통 마스터 데이터 저장소, master-data 내부 API 소비 클라이언트, TTL 캐시는 shared-core에 둠.
  • 사용자 로그인 세션의 영속 상태와 유효성 판정은 hub-api에 둠.
  • AI provider 어댑터, 기능별 모델 라우팅, usage log 기록, 체스 LangGraph 실행, AI 통신 예외 정규화는 shared-ai에 둠.
  • 레이아웃, 스타일, 공통 템플릿, Tabulator 헬퍼, 앱 셸 동작은 shared-ui에 둠.
  • 로컬 음성 입력, STT/TTS provider, 호출어 감지 런타임은 shared-voice에 둠.
  • 앱별 CSS, 화면별 CSS, inline style은 운영 화면 구현 표준에 포함하지 않음.
  • hub 운영 화면 페이지 진입점과 타입 계약은 hub-ui에 둠.
  • chess 샌드박스 화면 페이지 진입점과 타입 계약은 chess-ui에 둠.
  • raid 운영 화면 페이지 진입점과 타입 계약은 raid-ui에 둠.
  • rename 운영 화면 페이지 진입점과 타입 계약은 rename-ui에 둠.
  • workout 운영 화면 페이지 진입점과 타입 계약은 workout-ui에 둠.
  • Vite 개발 모드 자산 주입과 React refresh preamble 주입은 공통 베이스 템플릿과 서버 템플릿 컨텍스트를 통해 처리함.

4.3 TypeScript 우선 원칙

  • 프런트엔드 소스 코드는 TypeScript 기준으로 유지함.
  • 신규 화면, 공통 헬퍼, 앱 셸 로직은 .ts 또는 .tsx로 작성함.
  • TypeScript 설정은 strict 모드를 유지함.
  • any 사용은 최후 수단으로 제한하며, API 응답과 테이블 데이터는 명시적 타입 계약을 우선함.
  • 브라우저 전역 주입이 필요하면 window 임의 확장보다 import.meta.env와 타입 선언 파일을 우선 사용함.

4.4 현재 데이터 관리 구조

  • 공통코드, 코드 타입, 메타, 포털 메뉴는 DB 기반 마스터 데이터 구조를 사용함.
  • 포털 메뉴의 앱 구분은 menu_type_code=APP을 사용하며, 하위 호환 데이터의 PROJECT 값은 포털 소비 화면에서 APP 그룹으로 해석될 수 있음.
  • 포털 메뉴의 icon_type_code=APP_ICONicon_val은 포털 카드 아이콘, 앱 favicon, PWA 설치 아이콘을 같은 shared-ui 앱 아이콘 slug로 묶는 단일 관리 지점임.
  • 사용자에게 노출되는 일반 UI 텍스트는 소스에 한국어 문구로 직접 작성함.
  • 공통코드 라벨과 메타 설정값처럼 데이터 자체가 마스터데이터인 값은 기존 DB 기반 구조를 사용함.
  • AI 기능별 모델 라우팅은 YAML 기반 정책 파일 구조를 사용함.
  • 사용자, 사용자 세션, AI 로그, 운동 기록, 레이드 운영 데이터는 DB 기반 구조를 사용함.
  • 공통코드, 코드 타입, 메타, 포털 메뉴, 사용자, 사용자 세션, 대시보드 사용자 preference, AI usage log는 Hub SQLite 파일 /sorc001/junkbox/db/hub.sqlite3를 사용함.
  • 운동 도메인 데이터는 workout-api가 단독 writer인 SQLite 파일 DB에 저장함.
  • 체스 대국 마스터와 게임 로그는 DB 기반 구조를 사용하며, 내 bot 수, 상대 수, Lichess 이벤트, 게임 시작/종료 이벤트를 같은 로그 시간축에 저장함.
  • AI 개발 지식 검색용 문서 캐시는 PostgreSQL vault 스키마와 pgvector 기반 벡터 테이블을 사용함.
  • 개발 문서 캐시의 원본은 vault/docs/**/*.md 파일이며, JunkBox 개발 가이드의 Source of Truth는 vault/docs/junkbox/**/*.md 파일임.
  • 따라서 현재 시스템은 DB 기반 운영 데이터, YAML 기반 AI 라우팅 정책, 벡터 DB 기반 개발 문서 캐시를 함께 다루는 구조임.
  • DB 기반 사용자 구조에서 Hub SQLite tb_user_mid 물리 PK와 user_id 논리 식별자를 함께 사용함.
  • DB 기반 사용자 세션 구조는 Hub SQLite tb_user_session_n을 사용하며 JWT의 sid claim과 세션 테이블의 session_id를 연결함.

5. 설정 구조

5.1 Settings 원칙

  • 설정은 pydantic-settings 기반으로 로딩함.
  • 기본 ENV_FILE 값은 .env.local임.
  • ENV_FILE 기준 환경 파일과 같은 디렉터리의 .env.shared를 함께 로드함.
  • ENV_FILE.env.local.jeevtrap, .env.prod.jeevtrap이면 같은 디렉터리의 .env.shared, .env.jeevtrap, 선택된 Jeevtrap 전용 env 파일을 로드함.
  • ENV_FILE이 base profile env 파일명 뒤에 앱 suffix를 붙인 형태이고 Jeevtrap 전용 env가 아니면 같은 디렉터리의 .env.local 또는 .env.prod를 base profile env로 함께 로드할 수 있음.
  • 설정 우선순위는 명시적 초기화 값, 선택된 ENV_FILE, 앱 공통 env, base profile env, .env.shared, 셸 환경 변수, file secret 순서임.
  • import 시점부터 실행 환경 오염을 최소화하는 방향을 사용함.
  • 설정 객체는 app, database, auth, server, modules, masterdata, dashboard, rename, raid, lichess, jeevtrap, ai 중첩 모델 구조를 사용함.
  • 앱 코드는 중첩 모델 접근을 기준으로 구현하며, 일부 flat alias는 호환 목적에 한해 유지함.

5.2 주요 설정 범주

  • DATABASE_URL과 PostgreSQL DB 풀 옵션
  • HUB_DATABASE_URL
  • WORKOUT_DATABASE_URL
  • JWT_SECRET_KEY, JWT 알고리즘
  • apps/hub-api/var/auth-policy.yml 기반 사용자 세션 만료 시간과 슬라이딩 갱신 임계 시간
  • apps/hub-api/var/dashboard-policy.yml 기반 hub 대시보드 HTTP 수집 타임아웃, 캐시 TTL, Glances 호출 타임아웃
  • DASHBOARD_OPENROUTER_MANAGEMENT_API_KEY 기반 hub 대시보드 OpenRouter credits/API key 사용량 조회 설정
  • 쿠키 이름, 도메인, secure, same_site
  • CORS 허용 원본
  • 공통 로깅 정책인 LOG_ROTATION_LIMIT, LOG_RETENTION_DAYS, LOG_COMPRESSION_TYPE
  • profile별 로깅 동작인 LOG_LEVEL, LOG_DIAGNOSE_TOGGLE
  • MASTER_DATA_DIR
  • AI_ROUTING_YAML_PATH
  • HUB_BASE_URL, WORKOUT_BASE_URL, RAID_BASE_URL
  • CHESS_BASE_URL
  • HUB_INTERNAL_BASE_URL, HUB_API_INTERNAL_TOKEN, HUB_API_INTERNAL_TOKEN_HEADER
  • MASTERDATA_CODES_TTL_SECONDS, MASTERDATA_METAS_TTL_SECONDS, MASTERDATA_HTTP_TIMEOUT_SECONDS
  • AI_ACCESS_TOKEN, FASTAPI_BASE_URL
  • ASITE_BASE_URL, ASITE_LOGIN_URL, ASITE_USERNAME, ASITE_PASSWORD
  • RENAME_BASE_DIR
  • WCL_API_URL, WCL_TOKEN_URL, WCL_CLIENT_ID, WCL_CLIENT_SECRET, WCL_MAX_CONCURRENCY
  • LITELLM_API_KEY, LITELLM_BASE_URL, AI_HTTP_TIMEOUT_SECONDS
  • LICHESS_BOT_TOKEN, LICHESS_DEFAULT_OPPONENT_ID, LICHESS_CHALLENGE_RATED, LICHESS_CHALLENGE_CLOCK_LIMIT_SECONDS, LICHESS_CHALLENGE_CLOCK_INCREMENT_SECONDS, LICHESS_CHALLENGE_VARIANT, LICHESS_HTTP_TIMEOUT_SECONDS, LICHESS_STREAM_RECONNECT_DELAY_SECONDS
  • JEEVTRAP_PORT, JEEVTRAP_DISCORD_TOKEN, JEEVTRAP_DISCORD_TEST_GUILD_ID
  • JEEVTRAP_VOICE_ENABLED, JEEVTRAP_VOICE_CONFIG_YAML_PATH
  • JEEVTRAP_GITHUB_TOKEN, JEEVTRAP_GITHUB_OWNER, JEEVTRAP_GITHUB_REPO, JEEVTRAP_GITHUB_BRANCH, JEEVTRAP_GITHUB_DEFAULT_DOCS_PATH
  • JEEVTRAP_LINKDING_DATABASE_URL, JEEVTRAP_LINKDING_OWNER_ID
  • JEEVTRAP_COMMAND_DEFAULT_TIMEOUT_SECONDS, JEEVTRAP_DISCORD_COMMAND_INTENT_DEBUG_REPLY
  • JEEVTRAP_TARGET_DESKTOP_MAIN_DISPLAY_NAME, JEEVTRAP_TARGET_DESKTOP_MAIN_MAC_ADDRESS, JEEVTRAP_TARGET_DESKTOP_MAIN_BROADCAST_IP, JEEVTRAP_TARGET_DESKTOP_MAIN_HOST, JEEVTRAP_TARGET_DESKTOP_MAIN_SSH_PORT, JEEVTRAP_TARGET_DESKTOP_MAIN_SSH_USER, JEEVTRAP_TARGET_DESKTOP_MAIN_SSH_KEY_PATH

5.3 AI 개발 지식 동기화 설정

  • 개발 문서 벡터 캐시 동기화와 검색 스크립트는 애플리케이션 런타임 설정을 사용하지 않음.
  • 수동 빌드/인프라 타임 및 AI 에이전트 전용 설정은 scripts/.env.ai만 사용함.
  • 공유 가능한 설정 예시는 scripts/.env.ai.template에 둠.
  • scripts/.env.ai는 Git 추적 대상이 아니며 로컬 또는 운영 작업자별 비밀값을 담음.
  • FASTAPI_BASE_URL은 AI 에이전트가 hub-api 통합 snapshot API를 호출할 때 사용하는 base URL임.
  • AI_ACCESS_TOKENROLE_AI claim을 가진 AI 에이전트 전용 JWT이며 scripts/fetch_master_data.py가 Bearer 토큰으로 사용함.
  • VAULT_DATABASE_URLjb_ai 계정 기준 PostgreSQL 접속 문자열임.
  • AI_EMBEDDING_API_KEY는 개발 문서 chunk와 검색 query를 임베딩하기 위한 API 키임.
  • AI_EMBEDDING_BASE_URL은 LiteLLM proxy 같은 OpenAI 호환 embeddings API base URL임.
  • VAULT_EMBEDDING_MODEL은 개발 문서 벡터 캐시와 검색 query가 공유하는 임베딩 모델 식별자이며 LiteLLM에 등록된 embedding model id 또는 alias를 사용함.

5.4 AI 라우팅 설정 원칙

  • 로컬 Git 관리 기준 AI 라우팅 기본 파일은 apps/hub-api/var/master-data/ai-routing.yml임.
  • 컨테이너 런타임 기본 경로는 /app/var/master-data/ai-routing.yml임.
  • 운영 호스트 공통 파일 경로는 /sorc001/junkbox/master-data/ai-routing.yml임.
  • APP_PROFILE 값이 있으면 ai-routing.{profile}.yml을 우선 사용함.
  • profile별 파일이 없으면 기본 ai-routing.yml을 fallback으로 사용함.
  • provider 선택과 model 선택은 라우팅 YAML이 결정하고, API 키와 base URL은 .env.shared + ENV_FILE 조합이 결정함.
  • 어떤 provider 구현이 선택되는지는 provider 값이 결정하고, model 값은 선택된 provider 내부에서 실제 원격 모델 식별자로 사용됨.

5.5 환경 파일 분류 원칙

  • .env.shared에는 프로필과 무관한 공통 인프라 정책을 둠.
  • .env.local, .env.prod에는 프로필별로 달라지는 값과 운영 민감도가 높은 값을 둠.
  • .env.local.template은 local/prod env 파일이 공유하는 단일 템플릿으로 사용함.
  • .env.local.env.prod는 같은 변수 항목과 같은 주석 구조를 유지하여 프로필별 값 비교가 가능해야 함.
  • jeevtrap 전용 공통 실행 설정은 .env.jeevtrap에 두고, 프로필별 차이는 .env.local.jeevtrap, .env.prod.jeevtrap에 둠.
  • .env.jeevtrap.template은 local Jeevtrap env 파일 작성 기준 템플릿으로 사용함.
  • .env.jeevtrap는 Discord, GitHub, 공통 명령 target, 내부 토큰처럼 local/prod에서 공유하는 Jeevtrap 값을 보관함.
  • .env.local.jeevtrap, .env.prod.jeevtrap.env.local, .env.prod를 참조하지 않으며, .env.shared, .env.jeevtrap와 함께 독립 실행에 필요한 APP_PROFILE, DATABASE_URL, HUB_INTERNAL_BASE_URL, LITELLM_BASE_URL, JEEVTRAP_PORT, JEEVTRAP_VOICE_ENABLED, profile별 JEEVTRAP_* 값을 보관함.
  • 현재 공통 로깅 정책인 LOG_ROTATION_LIMIT, LOG_RETENTION_DAYS, LOG_COMPRESSION_TYPE.env.shared에 위치함.
  • 현재 프로필별 로깅 동작인 LOG_LEVEL, LOG_DIAGNOSE_TOGGLE.env.local, .env.prod에 각각 위치함.
  • Jeevtrap의 프로필별 로깅 동작인 LOG_LEVEL, LOG_DIAGNOSE_TOGGLE.env.local.jeevtrap, .env.prod.jeevtrap에 각각 위치함.
  • 운영 jeevtrap은 host network로 실행되므로 .env.prod.jeevtrapHUB_INTERNAL_BASE_URL, DATABASE_URL, JEEVTRAP_LINKDING_DATABASE_URL, LITELLM_BASE_URL은 Docker DNS 서비스명이 아니라 호스트 네트워크에서 접근 가능한 주소를 사용함.
  • 운영 jeevtrap의 LiteLLM proxy 접근은 호스트 publish 포트를 기준으로 하며, 현재 운영 기준 LITELLM_BASE_URLhttp://127.0.0.1:8083/v1 형태임.
  • 운영 jeevtrap의 PC 종료 명령은 컨테이너 내부 /run/secrets/jeevtrap_desktop_main_ssh_key에 read-only로 마운트된 SSH 비밀키를 사용함.
  • 운영 jeevtrap의 음성 런타임은 기본적으로 JEEVTRAP_VOICE_ENABLED=False로 비활성화함.
  • 로컬 jeevtrap의 음성 런타임은 JEEVTRAP_VOICE_ENABLED=True로 활성화할 수 있으며, 실제 음성 동작은 JEEVTRAP_VOICE_CONFIG_YAML_PATH가 가리키는 YAML이 결정함.
  • VS Code build task env:copy-to-sorc001는 프로젝트 루트의 .env.prod, .env.jeevtrap, .env.prod.jeevtrap, .env.shared/sorc001/junkbox/env/로 덮어쓰기 복사하는 운영 보조 흐름임.
  • scripts/copy_env_files.sh는 복사 시작 전 Enter 확인, 대상 디렉터리 준비, 파일별 복사 진행 출력, 완료 후 Enter 대기 흐름을 제공함.

6. 데이터베이스 전략

  • PostgreSQL과 SQLite 파일 DB를 함께 사용함.
  • Hub 공통 운영 데이터, 인증, 마스터데이터, 포털 구성, 대시보드 사용자 preference, AI 사용 로그는 Hub SQLite 파일 DB를 사용함.
  • Hub SQLite 파일은 호스트 /sorc001/junkbox/db/hub.sqlite3에 위치함.
  • hub-api 컨테이너 내부 표준 DB 경로는 /app/db/hub.sqlite3이며, Docker compose는 호스트 /sorc001/junkbox/db를 컨테이너 /app/db에 마운트함.
  • HUB_DATABASE_URL의 운영 및 Docker 기준 DSN은 sqlite+aiosqlite:////app/db/hub.sqlite3임.
  • 로컬 비컨테이너 실행의 기본 Hub DSN은 sqlite+aiosqlite:////sorc001/junkbox/db/hub.sqlite3임.
  • Hub SQLite 연결은 PRAGMA journal_mode=WAL, PRAGMA busy_timeout=5000, PRAGMA foreign_keys=ON을 적용함.
  • Hub SQLite 파일은 Docker 이미지 생명주기와 분리된 호스트 영속 파일이며 컨테이너 내부 임시 파일로 관리하지 않음.
  • Hub SQLite의 단일 writer는 hub-api임.
  • 다른 앱은 공통 기준정보, 사용자 인증, 세션 검증, 포털 메뉴, AI usage log를 직접 DB 파일로 소유하거나 write하지 않고 hub-api internal API와 JWT/Cookie 계약을 사용함.
  • PostgreSQL 영역은 잔여 도메인 스키마와 개발 문서 벡터 캐시에 사용함.
  • Jeevtrap 명령 정의와 실행 로그는 jeevtrap 스키마를 사용함.
  • 레이드 관련 도메인 데이터는 raid 스키마를 사용함.
  • 체스 대국과 턴별 에이전트 로그는 chess 스키마를 사용함.
  • 운동 기준정보, 운동 세션, 운동 기록, 운동 계획은 /sorc001/junkbox/db/workout.sqlite3 SQLite 파일을 사용함.
  • workout-api 컨테이너 내부 표준 DB 경로는 /app/db/workout.sqlite3이며, Docker compose는 호스트 /sorc001/junkbox/db를 컨테이너 /app/db에 마운트함.
  • WORKOUT_DATABASE_URL의 운영 및 Docker 기준 DSN은 sqlite+aiosqlite:////app/db/workout.sqlite3임.
  • 로컬 비컨테이너 실행의 기본 workout DSN은 sqlite+aiosqlite:////sorc001/junkbox/db/workout.sqlite3임.
  • Workout SQLite 연결은 PRAGMA journal_mode=WAL, PRAGMA busy_timeout=5000, PRAGMA foreign_keys=ON을 적용함.
  • Workout SQLite 파일은 Docker 이미지 생명주기와 분리된 호스트 영속 파일이며 컨테이너 내부 임시 파일로 관리하지 않음.
  • 공통코드, 메타, 사용자 인증, 사용자 세션, 포털 메뉴, AI usage log는 Workout SQLite 파일로 복제하지 않음.
  • 운동 도메인 데이터는 Hub SQLite 파일로 흡수하지 않음.
  • Workout이 참조하는 공통 기준정보는 hub-api internal master-data API를 통해 조회함.
  • 운동 종목 기준정보는 workout SQLite tb_workout_exercise_m이 런타임 소스 오브 트루스이며, hub 공통코드 EXERCISE는 초기 bootstrap source로만 사용할 수 있음.
  • SQLite 파일 간 DB 레벨 FK를 전제로 하지 않으며, user_id 같은 cross-domain 식별자는 서비스 계층의 느슨한 참조로 유지함.
  • AI 개발 지식 캐시는 vault 스키마를 사용함.
  • vault.docs는 markdown 파일 단위의 원본 경로, 제목, route, namespace, kind, project, frontmatter, content hash, 삭제 상태를 저장함.
  • vault.doc_chunks는 문서를 heading 기반 chunk로 나눈 검색 단위이며 heading_path, heading_level, content, content_hash, token_count, metadata를 포함함.
  • vault.doc_embeddings는 chunk별 VECTOR(1536) 임베딩을 저장하며 embedding_model, embedding_dimension, content_hash를 함께 보관함.
  • vault.index_runs는 문서 색인 실행 이력, 처리 파일 수, 변경/삭제 수, chunk 수, 오류 메시지를 기록함.
  • content_hash는 SHA-256 기반 변경 감지에 사용됨.
  • vault.docs.is_deleted=true는 로컬 원본에서 삭제되어 검색 대상에서 제외된 문서를 나타냄.
  • jb_ai 계정은 vault 스키마 내 기존 테이블 데이터 CRUD와 시퀀스 사용 권한을 가진 AI 전용 계정이며 앱 구동 계정 jb_app과 분리됨.
  • Linkding 북마크 저장은 JunkBox 주 DB가 아니라 별도 Linkding PostgreSQL DB를 직접 사용함.
  • 앱 추가 시 앱별 단독 writer가 명확한 도메인은 별도 SQLite 파일 DB를 우선 고려하고, PostgreSQL 유지 영역은 스키마 단위 분리를 사용함.
  • DB 기반 운영 엔티티는 created_at, updated_at 같은 감사 컬럼 NOT NULL 제약을 충족하는 기본값 전략을 함께 가져야 함.
  • 체스 저장소는 chess.tb_game_mchess.tb_game_move_log_n을 사용하며, board_ascii, pgn, FEN, 시간, actor/event type, Lichess 원본 payload, sleepzz-bot 착수 payload, AI 생성 latency는 AI 프롬프트 재구성 및 사후 분석을 위해 로그 테이블에 함께 저장함.
  • chess.tb_game_m은 hub 사용자 식별자, 내 bot Lichess id, 상대 유형, 상대 Lichess id, 상대 표시명, Lichess 내장 AI level, 초기/최종 FEN, 최종 PGN, 종료 사유를 함께 보존함.

7. 비동기 ORM 규격

7.1 엔진과 세션

  • create_async_engine()async_sessionmaker()를 사용함.
  • 의존성 주입은 async def get_session() 제너레이터 패턴을 사용함.
  • 세션 타입은 sqlalchemy.ext.asyncio.AsyncSession임.

7.2 SQLModel 사용 규칙

  • Field(..., sa_column=Column(...)) 조합에서는 중복 옵션 선언을 피함.
  • PostgreSQL 특수 타입은 PostgreSQL 엔티티에서 sa_column으로 직접 선언함.
  • SQLite 엔티티는 PostgreSQL schema qualifier와 PostgreSQL 전용 타입을 사용하지 않음.
  • 믹스인 컬럼 재사용으로 인한 선언 충돌을 피함.

7.3 조회 패턴

  • AsyncSession에서는 SQLModel 동기 exec()를 사용하지 않음.
  • await session.execute(select(...)) 기반 SQLAlchemy 네이티브 패턴을 사용함.
  • 결과 해석은 scalar_one_or_none(), scalars().all(), all() 등으로 명시함.

8. 인증 및 권한 구조

8.1 인증 책임 경계

  • 사용자 credential 검증, JWT 발급, 공통 로그인 화면, 공통 로그아웃 API는 hub-api가 담당함.
  • 사용자 세션 정책, 사용자 세션 영속 상태, JWT 세션 유효성 판정, JWT 슬라이딩 갱신 판단은 hub-api가 담당함.
  • chess-api, workout-api, raid-api, rename-api는 자체 /api/auth/login 또는 /api/auth/check 계열 credential 검증 API를 제공하지 않음.
  • 개별 앱은 hub-api가 발급한 JWT를 Authorization: Bearer 헤더 또는 COOKIE_NAME 기준 HttpOnly 쿠키에서 읽고, 일반 사용자 JWT의 세션 유효성 판정은 hub-api 내부 인증 API에 위임함.
  • 개별 앱은 브라우저 쿠키 기반 요청에 대해 shared-core 슬라이딩 세션 미들웨어를 등록하고, 갱신 판단과 새 토큰 발급은 hub-api 내부 인증 API에 위임함.
  • 개별 앱의 앱 셸 로그아웃 동작은 HUB_BASE_URL 기준 hub-api/api/auth/logout을 호출함.

8.2 공통 의존성

  • get_current_user는 필수 인증임.
  • get_optional_user는 선택 인증임.
  • require_module(module_code)는 모듈 권한 강제 의존성임.
  • 공통 인증 의존성은 일반 사용자 JWT의 서명과 만료를 검증한 뒤 HUB_INTERNAL_BASE_URLPOST /api/internal/auth/validate를 호출해 세션 유효성을 확인함.
  • AI 에이전트 전용 ROLE_AI JWT는 사용자 세션 테이블 검증 대상이 아니며 require_ai_role 전용 read-only API에서만 허용됨.
  • SlidingSessionMiddleware는 브라우저 쿠키 기반 interaction마다 hub 내부 인증 API를 호출해 세션 갱신 필요 여부를 확인함.
  • SlidingSessionMiddleware는 인증 실패한 토큰을 강제로 갱신하지 않으며, 만료되었거나 유효하지 않거나 hub에서 revoke된 토큰은 표준 인증 의존성과 페이지 라우터 실패 흐름이 처리함.
  • SlidingSessionMiddleware/api/internal/*, 정적 자산, 로그인, 로그아웃, 헬스체크, 문서 경로를 갱신 대상에서 제외함.

8.3 세션 정책

  • 사용자 세션 만료와 슬라이딩 갱신 임계값은 hub-api 소유 정책 파일인 apps/hub-api/var/auth-policy.yml에서 관리함.
  • 현재 세션 정책은 expiration_minutes=120, sliding_threshold_minutes=30임.
  • 로그인 성공 시 hub-api는 세션 정책의 만료 시간을 기준으로 Hub SQLite tb_user_session_n에 세션 row를 생성하고, JWT sid, JWT exp, HttpOnly 쿠키 max_age를 설정함.
  • 일반 사용자 JWT는 sid claim을 포함하며, sid는 Hub SQLite tb_user_session_n.session_id와 매칭되어야 유효함.
  • hub-api는 세션 row의 is_active, revoked_at, expires_at, 사용자 is_active를 기준으로 JWT 세션 유효성을 판정함.
  • 요청 토큰의 남은 시간이 슬라이딩 갱신 임계값 이하이면 hub-api는 같은 sid를 유지하고 DB의 최신 사용자 권한을 기준으로 새 JWT를 발급하며 브라우저 쿠키를 다시 설정함.
  • 로그아웃, 사용자 비밀번호 변경, 계정 비활성화, 권한 변경, 사용자 삭제는 관련 사용자 세션을 revoke하여 기존 일반 사용자 JWT를 무효화함.
  • JWT_SECRET_KEY, 알고리즘, 쿠키 보안 옵션은 환경 설정으로 관리하고, 세션 만료와 슬라이딩 임계값은 hub 정책 YAML로 관리함.
  • HUB_INTERNAL_BASE_URL, HUB_API_INTERNAL_TOKEN, HUB_API_INTERNAL_TOKEN_HEADER는 개별 앱이 hub 내부 인증 API와 master-data API를 호출할 때 사용하는 서버 간 설정임.

8.4 권한 데이터

  • allowed_modules는 Hub SQLite JSON 배열을 기준으로 저장됨.
  • 애플리케이션 내부에서는 list[str]로 다룸.
  • 비교와 판정은 대문자 모듈 코드 기준으로 수행함.
  • 과거 SYSTEM 값은 인증 계층에서 HUB로 정규화하여 처리함.

8.5 실패 정책

  • HTML 페이지 요청은 리다이렉트 기반 인증 흐름을 사용함.
  • API 요청은 JSON 401/403 응답을 유지함.
  • HTML 페이지의 모듈 권한 부족은 hub-api 로그인 화면에 permission_denied=1을 전달하는 흐름으로 처리함.
  • 로그인 화면은 permission_denied=1이 있으면 권한 없음 alert를 1회 표시하고 쿼리 플래그를 제거함.
  • 앱별 권한 오류 템플릿은 사용하지 않으며, 하위 호환 라우트는 로그인 alert 흐름으로 연결함.
  • 운영 관리자 영역은 HUB 권한을 기준으로 접근을 제어함.
  • 운동 기록 영역은 WORKOUT 권한을 기준으로 접근을 제어함.
  • 레이드 운영 화면과 API는 RAID 권한을 기준으로 접근을 제어함.
  • 체스 샌드박스 화면, API, WebSocket 로그는 CHESS 권한을 기준으로 접근을 제어함.
  • 개발 모드 로그인 복귀 시 redirect는 원래 UI 앱 origin을 유지하고, hub-api는 허용 origin 검증 후 해당 UI 앱 경로로 복귀시킴.

9. 공통 마스터 데이터 구조

9.1 현재 대상

  • 공통코드
  • 메타
  • 포털 메뉴
  • AI 라우팅

9.2 저장 방식

  • 공통 마스터 데이터의 소스 오브 트루스는 hub-api가 관리하는 Hub SQLite /sorc001/junkbox/db/hub.sqlite3 파일임.
  • 코드 타입은 tb_code_type_c, 공통코드는 tb_common_c, 메타는 tb_meta_m, 포털 메뉴는 tb_portal_menu_m을 사용함.
  • AI 라우팅 로컬 원본은 apps/hub-api/var/master-data/ai-routing.yml 계열 파일을 사용함.
  • 마스터 데이터 CRUD는 hub-apishared-core 저장소 계층을 통해 수행함.
  • AI 에이전트 표준 통합 snapshot API는 GET /api/v1/master/all이며 ROLE_AI JWT를 사용함.

9.3 운영 방식

  • DB 기반 공통 마스터데이터는 hub-api가 단일 관리 주체임.
  • 화면과 API는 파일 저장소를 직접 노출하지 않고 shared-core 저장소 계층을 통해 접근함.
  • 다른 앱과 모듈은 shared-core.masterdata.MasterDataService를 통해 hub-api 내부 master-data API를 소비함.
  • shared-core.masterdata.HubMasterDataClientHUB_INTERNAL_BASE_URL, HUB_API_INTERNAL_TOKEN, HUB_API_INTERNAL_TOKEN_HEADER로 내부 API를 호출함.
  • shared-core.masterdata.TtlAsyncCache는 코드와 메타 조회 결과를 TTL 기준으로 재사용함.
  • Python 서버 렌더링 문구와 React UI 문구는 각 템플릿/컴포넌트 소스에 한국어로 직접 작성함.
  • APP_PROFILE=local에서 HUB_API_INTERNAL_TOKEN이 비어 있으면 개발 편의용 local store fallback을 사용할 수 있음.
  • 운영 profile에서 HUB_API_INTERNAL_TOKEN이 비어 있으면 내부 master-data API 소비는 실패해야 하며, 조용히 로컬 DB 조회로 전환하지 않음.
  • AI 코딩은 scripts/fetch_master_data.py 실행 결과인 통합 snapshot을 우선 사용함.
  • AI가 대량 문맥을 확보할 때는 FASTAPI_BASE_URL/api/v1/master/all 결과를 기준으로 함.
  • AI 라우팅 정책 파일은 Git 형상관리 원본을 apps/hub-api/var/master-data에 두고, 운영 배포 시 /sorc001/junkbox/master-data/ai-routing.yml로 동기화한 뒤 hub-api, jeevtrap 컨테이너가 공통 볼륨으로 공유함.

10. 운동 계획 규칙 엔진 구조

  • 운동 계획 공개 생성과 화면 진입은 workout-apiworkout-ui가 담당함.
  • 운동 계획 생성 실행 주체는 workout-api이며, hub AI 오케스트레이션이나 shared-ai 그래프를 사용하지 않음.
  • 운동 계획 생성은 Workout SQLite의 운동 기준정보, 운동 세션, 운동 기록, 운동 계획 테이블을 사용함.
  • 운동 계획 루틴 모드 option은 hub 공통코드 WORKOUT_ROUTINE_MODE의 활성 row를 그대로 사용함.
  • 규칙 엔진의 명시 계산 대상은 UPPER, LOWER이며 알 수 없는 루틴 모드는 상체 루틴으로 정규화함.
  • 상체 루틴은 렛 풀다운, 체스트 프레스, 숄더 프레스, 리어 델트 플라이, 펙 델트 플라이, 인클라인 바이셉스 컬 순서를 사용함.
  • 하체 루틴은 옴니 레그 프레스, 라잉 레그 컬, 레그 익스텐션, 힙 어덕션 순서를 사용함.
  • 각 운동은 기본 3세트로 저장하며, 1세트는 탑세트보다 한 단계 낮은 중량 8회, 2세트는 계산된 탑세트, 3세트는 탑세트보다 한 단계 낮은 중량 10회를 사용함.
  • 기준 탑세트는 같은 사용자의 같은 운동에 대한 가장 최근 유효 세션 안에서 성공 세트 중 최대 중량, 같은 중량이면 최대 반복수 기준으로 산정함.
  • 유효 기준 기록은 활성 세션과 활성 기록, SUCCESS 상태, 중량, 반복수, RPE를 모두 필요로 함.
  • 8회 미만 성공은 같은 중량 8회 재시도, RPE 10 이상은 같은 중량과 같은 반복수 유지, 8~11회 RPE 9 이하는 같은 중량에서 1회 증가, 12회 이상 RPE 9 이하는 한 단계 증량 후 8회 재시작 규칙을 사용함.
  • 운동 계획 저장은 tb_workout_plan_n 계획 헤더와 연결 tb_workout_session_n, tb_workout_record_n 세트 레코드로 구성함.
  • tb_workout_plan_n은 AI 응답 JSON, AI 조언, AI usage log 연결을 저장하지 않음.
  • 운동 세션, 운동 기록, 운동 계획 조회와 저장은 현재 사용자 user_id 기준으로 격리됨.

11. Jeevtrap 구조

  • jeevtrap는 Discord 상호작용을 UI 레이어로 사용하고, 서비스 레이어는 Discord 전용 객체를 받거나 반환하지 않음.
  • 인터페이스 레이어는 interfaces/discordinterfaces/voice로 분리됨.
  • discord.py 의존성은 interfaces/discord 내부에서만 사용함.
  • 로컬 음성 인터페이스는 interfaces/voice가 담당하며, 실제 마이크/STT/TTS 구현은 shared-voice를 사용함.
  • 서비스 레이어는 markdown_generator, bookmark_processor, command_executor로 분리됨.
  • 평문 Discord 멘션 명령 의도 판별은 CommandIntentResolverService가 담당함.
  • /md는 AI 초안 생성 후 GitHub repository contents API에 마크다운 문서를 저장함.
  • /북마크는 URL 스크래핑 후 AI 분석을 수행하고 Linkding DB에 직접 저장함.
  • /명령jeevtrap.tb_command_m, jeevtrap.tb_command_logs_n 기준 명령을 조회하고 패키지 내장 스크립트를 실행함.
  • 봇이 멘션된 일반 메시지는 AI 명령 의도 판별 후 실행 확인 UI로 연결할 수 있음.
  • Discord slash command와 멘션 메시지는 JEEVTRAP_DISCORD_TEST_GUILD_IDAPP_PROFILE 기준의 guild guard를 통과한 서버 이벤트만 처리함.
  • 일반 메시지 명령 실행 로그는 DISCORD_MESSAGE, slash command 메뉴 명령 실행 로그는 DISCORD_SLASH request channel code를 사용함.
  • 내장 명령 스크립트는 테스트 콘솔 출력, 데스크탑 WOL 켜기, 데스크탑 SSH 종료를 포함함.
  • jeevtrapshared-aiDirectAiRunner를 사용하며 JEEVTRAP_MD, JEEVTRAP_BOOKMARK, JEEVTRAP_COMMAND_INTENT route key를 기준으로 모델을 선택함.
  • jeevtrap는 공통 로그인 체계를 사용하지 않으며 사용자 인증은 Discord 계정과 interaction context에 의해 결정됨.
  • jeevtrap는 FastAPI /health, Discord 봇 런타임, 선택적 voice 런타임을 단일 프로세스 안에서 함께 구동함.
  • voice 런타임은 JEEVTRAP_VOICE_ENABLED가 true일 때만 시작함.
  • voice 런타임은 apps/jeevtrap/var/voice.yml을 기본 설정 파일로 사용하며, 호출어, alias, STT 모델, TTS provider, 오디오 장치 설정을 YAML에서 읽음.
  • 현재 voice 호출어는 하이파이브 계열이며, 호출 성공 응답은 Supertonic TTS의 남성 voice preset으로 하이파이브!를 합성함.
  • voice 런타임은 shared-voice의 blocking wake loop를 별도 thread로 실행하여 FastAPI 이벤트 루프를 점유하지 않음.
  • jeevtrap의 Discord UI 문구와 slash command 설명은 앱 내부 MessageCatalog의 한국어 문구를 사용함.
  • jeevtrap 런타임에서 HUB_API_INTERNAL_TOKEN이 설정되어 있으면 HUB_INTERNAL_BASE_URLhub-api 내부 API가 가용해야 함.
  • jeevtrap의 일반 Discord UI 문구는 텍스트 DB 마스터데이터에 의존하지 않음.

12. 체스 샌드박스 구조

  • 체스 샌드박스 API와 화면은 chess-apichess-ui가 담당함.
  • chess-api는 Lichess account stream, game stream, challenge API, bot move API를 직접 호출함.
  • 상대 유형이 일반/봇 계정이면 POST /api/challenge/{username}을 사용하고, Lichess 내장 AI이면 POST /api/challenge/ailevel 파라미터를 사용함.
  • 수 선택은 sleepzz-bot이 담당하며, 현재 상황과 착수 근거도 같은 응답에서 작성함.
  • sleepzz-bot 응답은 legal moves choice 목록의 번호를 반환하며, 앱 계층은 choice를 UCI move로 매핑한 뒤 합법 수 여부를 검증함.
  • choice 범위 오류, 매핑 실패, 합법 수 검증 실패가 발생하면 같은 턴에서 한 번 재시도하고, 반복 실패나 호출 실패가 발생하면 fallback 합법 수를 제출함.
  • Lichess move 제출은 sleepzz-bot 착수 생성, 앱 계층 합법 수 검증, 또는 fallback 합법 수 선택 이후에만 수행함.
  • 체스 착수는 shared-aiGraphAiRunnerCHESS_MOVE graph key를 사용함.
  • 체스 착수 입력은 FEN, legal moves, 축약 8x8 ASCII 보드, 축약 PGN, side, forbidden moves를 포함함.
  • AI 착수 프롬프트에는 PGN 전체가 아니라 최근 구간이 사용될 수 있으며, DB와 로그에는 전체 기보를 보존함.
  • AI 응답은 짧은 thought와 legal moves choice 번호를 포함하는 JSON 객체를 기준으로 함.
  • sleepzz-bot thought는 현재 상황:착수 근거: 두 줄 형식을 기준으로 함.
  • 실시간 관전 UI는 WebSocket /api/chess/ws/logs를 통해 game/status/turn_log 이벤트를 수신함.
  • 체스 화면은 실시간 체스판, 에이전트 사고 로그, 선택 수, 상대 수, 상태 타임라인, 저장된 게임 이력 표시를 제공함.
  • 체스 화면은 FEN을 chess.js로 해석해 현재 턴, 체크 여부, legal move 개수, material balance를 보드 하단 메타로 표시함.
  • 체스 화면은 own bot 로그의 event_payload.choice, commentary_status, fallback_reason, commentary_latency_ms를 보드 하단 AI 판단 메타로 표시함.
  • 체스 화면의 Game History는 Tabulator 기반 페이지네이션 표이며 시작 시각 역순으로 대국 이력을 조회함.
  • 과거 게임 선택 시 저장된 로그를 조회하고, 완료된 과거 게임의 move 로그 선택 시 해당 로그의 FEN을 중앙 체스판에 표시함.
  • 진행 중인 게임 선택 시 WebSocket live timeline으로 복귀함.
  • 체스 로그 시간과 DB 저장 시간은 한국 표준시 Asia/Seoul 기준 문자열 또는 timezone-aware datetime을 사용함.

13. 레이드 구조

  • 레이드 운영 화면과 API는 raid-apiraid-ui가 담당함.
  • 레이드 화면은 PC 전용 FHD 밀도 기준의 단일 그리드 중심 구조를 사용함.
  • 공대원 목록공대원 관리 화면 모두 Tabulator 기반 전체 로딩 구조를 사용함.
  • 레이드 화면은 shared-ui 공통 다크 자산과 Tabulator 공통 자산을 그대로 사용함.
  • 레이드 멤버 풀과 레이드별 배치는 분리된 테이블 구조를 사용하며, 하나의 멤버가 여러 레이드에 중복 배치될 수 있음.
  • 레이드 점수 동기화는 Warcraft Logs API 연동을 사용하며, 설정은 settings.raid.wcl 중첩 구조를 기준으로 로드함.
  • 레이드 점수 갱신은 전체 레이드 멤버 집합 기준으로 수행하며, 결과는 raid 스키마 점수 테이블에 반영됨.
  • 레이드 화면은 공통 로그인 진입점을 hub-api에 두고 required_module=RAID 기준으로 복귀 흐름을 처리함.

14. 운영 원칙

  • 모듈 간 직접 DB 결합보다 내부 API 재사용을 우선함.
  • 공통 규칙은 shared-core, shared-ai, shared-ui, shared-voice로 수렴시킴.
  • 운영 화면은 관리 생산성과 빠른 확인을 우선함.
  • apps/hub-ui, apps/chess-ui, apps/raid-ui, apps/rename-ui, apps/workout-ui 빌드 결과물 dist와 Vite manifest는 각 백엔드 앱이 직접 서빙하는 운영 자산임.
  • 개발 모드에서는 Vite dev server가 화면 엔트리와 HMR 자산을 제공하고, FastAPI는 서버 렌더링 HTML과 API를 제공함.
  • 개발 서버 기준 루트 진입 경로는 hub 모드에서 /portal, chess 모드에서 /chess, workout 모드에서 /workout-sessions, rename 모드에서 /rename, raid 모드에서 /raid/members임.
  • VS Code backend task는 각 앱 패키지 의존성을 기준으로 실행하기 위해 uv run --package {package-name} uvicorn ... 형태를 사용함.
  • .venv/bin/uvicorn을 직접 호출하는 방식은 workspace 앱별 런타임 의존성이 설치되지 않은 환경을 만들 수 있으므로 backend task의 표준 실행 방식으로 사용하지 않음.
  • 운영 배포는 docker-compose.prod.yml 기준 독립 컨테이너 구조를 사용하며, hub-api, chess-api, raid-api, rename-api, workout-api, jeevtrap는 각각 자기 실행 자산을 자기 이미지에 포함함.
  • 운영 배포 자동화는 GitHub Actions selective build/deploy 흐름을 사용하며, junkbox-chessjunkbox-jeevtrap도 독립 서비스 단위로 감지, 빌드, 배포됨.
  • 운영 CDN이 고정 경로 정적 자산을 오래 캐시할 수 있으므로, 공통 shared-ui CSS/JS는 버전 쿼리 파라미터 링크를 기본 구조로 사용함.
  • 운영 컨테이너는 /sorc001/junkbox/env/.env.shared/sorc001/junkbox/env/.env.prod를 공통으로 참조하고, ENV_FILE=/app/env/.env.prod 기준으로 로딩함.
  • 운영 jeevtrap 컨테이너는 /sorc001/junkbox/env/.env.shared, /sorc001/junkbox/env/.env.jeevtrap, /sorc001/junkbox/env/.env.prod.jeevtrap를 함께 참조하고, ENV_FILE=/app/env/.env.prod.jeevtrap 기준으로 로딩함.
  • 프로젝트 루트의 .env.prod, .env.jeevtrap, .env.prod.jeevtrap, .env.shared를 운영 env 디렉터리에 반영할 때는 VS Code build task env:copy-to-sorc001 또는 동일한 scripts/copy_env_files.sh 흐름을 사용함.
  • 운영 배포 compose는 hub-api=/logs001/junkbox/hub-api, chess-api=/logs001/junkbox/chess-api, workout-api=/logs001/junkbox/workout-api, rename-api=/logs001/junkbox/rename-api, raid-api=/logs001/junkbox/raid-api 호스트 경로를 각 컨테이너의 /app/logs에 마운트하는 구조를 사용함.
  • 운영 배포 compose는 Hub/Workout SQLite 호스트 경로 /sorc001/junkbox/dbhub-api, workout-api 컨테이너의 /app/db에 마운트함.
  • 각 백엔드 컨테이너의 공통 애플리케이션 로그 파일 경로는 /app/logs/app.log임.
  • 로컬 개발 로그는 프로젝트 루트의 var/log/{app-name} 하위에 생성되며 Git 추적 대상이 아님.
  • 로그 파일 rotation, retention, compression, diagnose 정책은 shared-core의 공통 로깅 초기화와 .env.shared 및 실행 앱별 profile env 파일 조합이 결정함.
  • rename-api${RENAME_BASE_DIR}:${RENAME_BASE_DIR} 동일 경로 바인드 마운트를 전제로 동작함.
  • 로컬 AI 라우팅 Git 원본은 apps/hub-api/var/master-data를 사용하고, 운영 런타임은 /sorc001/junkbox/master-data/ai-routing.yml 공유 파일을 사용함.
  • 운영에서는 APP_PROFILE=prod, HUB_BASE_URL=https://hub.sleepzz.xyz, HUB_DATABASE_URL=sqlite+aiosqlite:////app/db/hub.sqlite3, CHESS_BASE_URL, WORKOUT_BASE_URL=https://workout.sleepzz.xyz, WORKOUT_DATABASE_URL=sqlite+aiosqlite:////app/db/workout.sqlite3, RAID_BASE_URL=https://raid.sleepzz.xyz, HUB_INTERNAL_BASE_URL, COOKIE_DOMAIN=.sleepzz.xyz, COOKIE_SECURE=true 값을 우선 점검함.
  • 운영 기본 bridge network 컨테이너의 HUB_INTERNAL_BASE_URLhttp://junkbox-hub:8001 계열 Docker DNS 주소를 사용함.
  • 운영 jeevtrap은 WOL 브로드캐스트 전송을 위해 host network로 실행되므로 HUB_INTERNAL_BASE_URL, DATABASE_URL, JEEVTRAP_LINKDING_DATABASE_URL, LITELLM_BASE_URL은 호스트 네트워크에서 접근 가능한 주소를 사용함.
  • 운영 jeevtrap의 PC 종료 명령은 컨테이너 내부에 read-only로 마운트된 SSH 비밀키 경로를 JEEVTRAP_TARGET_DESKTOP_MAIN_SSH_KEY_PATH로 사용함.
  • 운영 jeevtrap의 voice 런타임은 기본 비활성 상태이며, 활성화 시 PulseAudio/PipeWire socket과 cookie 마운트, PULSE_SERVER, PortAudio/ALSA 런타임 패키지, apps/jeevtrap/var/voice.yml 설정이 함께 유효해야 함.
  • 레이드 운영에서는 WCL_CLIENT_ID, WCL_CLIENT_SECRET, WCL_API_URL, WCL_TOKEN_URL, WCL_MAX_CONCURRENCY 값을 함께 점검함.
  • 체스 운영에서는 LICHESS_BOT_TOKEN, LITELLM_BASE_URL, LITELLM_API_KEY, DATABASE_URL, AI_ROUTING_YAML_PATH 또는 MASTER_DATA_DIR 값을 함께 점검함.
  • HUB_API_INTERNAL_TOKENraid-api, workout-api, rename-api, chess-apihub-api 내부 인증 API와 내부 master-data API를 호출할 때 사용하는 서버 간 토큰임.
  • hub-api 세션 정책 파일은 apps/hub-api/var/auth-policy.yml이며, 운영 컨테이너에서는 동일 경로가 이미지 또는 마운트에 포함되어야 함.
  • AI_ACCESS_TOKEN은 AI 에이전트 전용 ROLE_AI JWT 값이며 scripts/.env.ai에 보관함.
  • FASTAPI_BASE_URLscripts/fetch_master_data.py가 호출할 hub-api base URL임.
  • vault/docs/**/*.md 문서 원본은 사람이 Git으로 검증하고 커밋하는 Source of Truth임.
  • vault/docs/junkbox/**/*.md는 JunkBox 개발 가이드 문서 원본이며 docs/junkbox/common, docs/junkbox/libs, docs/junkbox/apps로 책임을 나눔.
  • vault 스키마의 문서 벡터 테이블은 AI 에이전트가 필요한 문서 chunk를 먼저 찾기 위한 읽기 최적화 캐시임.
  • 개발 문서 벡터 캐시는 로컬 markdown 원본에서 DB로만 동기화되며, DB 내용을 markdown으로 역동기화하지 않음.
  • scripts/sync_dev_docs.pyvault/docs/**/*.md 파일의 생성, 수정, 삭제를 SHA-256 해시 기준으로 감지하여 vault.docs, vault.doc_chunks, vault.doc_embeddings, vault.index_runs에 반영함.
  • 변경되지 않은 문서는 임베딩을 재생성하지 않음.
  • 변경된 문서는 기존 chunk와 embedding을 문서 기준으로 교체하고 새 chunk를 임베딩하여 저장함.
  • 로컬에서 사라진 문서는 hard delete하지 않고 vault.docs.is_deleted=true로 표시함.
  • scripts/search_dev_docs.py는 검색 query를 임베딩한 뒤 vault.doc_embeddings에서 cosine similarity 기준 상위 chunk를 조회함.
  • AI 작업 프롬프트는 vault/docs/junkbox 전체 파일을 무작정 읽기보다 scripts/search_dev_docs.py 검색 결과의 source, namespace, kind, project, heading, chunk를 기준으로 필요한 원본 문서만 좁혀 읽는 흐름을 우선함.
  • JWT_SECRET_KEYHUB_API_INTERNAL_TOKEN은 용도가 다르므로 서로 다른 랜덤 값을 사용하는 것을 기본 원칙으로 함.
  • AI_ACCESS_TOKEN은 사용자 인증 JWT, 서버 간 내부 토큰과 분리된 별도 값으로 관리함.

15. 관련 문서

  • hub-api 구조는 docs/junkbox/apps/guide-apps-hub-api.md를 따름.
  • hub-ui 구조는 docs/junkbox/apps/guide-apps-hub-ui.md를 따름.
  • chess-api 구조는 docs/junkbox/apps/guide-apps-chess-api.md를 따름.
  • chess-ui 구조는 docs/junkbox/apps/guide-apps-chess-ui.md를 따름.
  • raid-api 구조는 docs/junkbox/apps/guide-apps-raid-api.md를 따름.
  • raid-ui 구조는 docs/junkbox/apps/guide-apps-raid-ui.md를 따름.
  • rename-api 구조는 docs/junkbox/apps/guide-apps-rename-api.md를 따름.
  • rename-ui 구조는 docs/junkbox/apps/guide-apps-rename-ui.md를 따름.
  • workout-api 구조는 docs/junkbox/apps/guide-apps-workout-api.md를 따름.
  • workout-ui 구조는 docs/junkbox/apps/guide-apps-workout-ui.md를 따름.
  • 공통 AI 런타임과 개발 문서 벡터 캐시 구조는 docs/junkbox/libs/guide-lib-shared-ai.md를 따름.
  • 공통 음성 런타임 구조는 docs/junkbox/libs/guide-lib-shared-voice.md를 따름.
  • 공통 마스터 데이터 규칙은 docs/junkbox/common/guide-jb-common-master-data.md를 따름.
  • 공통 UI 원칙과 운영 화면 시각 규칙은 docs/junkbox/common/guide-jb-common-ui.md를 단일 기준으로 따름.