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-ui의app-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.woff2와hub-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임. - 비밀번호 처리는
passlib와bcrypt조합을 사용함. - 로그인 화면은
hub-api가 공통 진입점을 제공하고, 각 앱은required_module과redirect를 기준으로 복귀 흐름을 사용함. - 공통 로그인 화면은 마지막으로 로그인에 성공한
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-api의ROLE_AIJWT 기반 통합 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_ICON과icon_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_m는id물리 PK와user_id논리 식별자를 함께 사용함. - DB 기반 사용자 세션 구조는 Hub SQLite
tb_user_session_n을 사용하며 JWT의sidclaim과 세션 테이블의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_URLWORKOUT_DATABASE_URLJWT_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_DIRAI_ROUTING_YAML_PATHHUB_BASE_URL,WORKOUT_BASE_URL,RAID_BASE_URLCHESS_BASE_URLHUB_INTERNAL_BASE_URL,HUB_API_INTERNAL_TOKEN,HUB_API_INTERNAL_TOKEN_HEADERMASTERDATA_CODES_TTL_SECONDS,MASTERDATA_METAS_TTL_SECONDS,MASTERDATA_HTTP_TIMEOUT_SECONDSAI_ACCESS_TOKEN,FASTAPI_BASE_URLASITE_BASE_URL,ASITE_LOGIN_URL,ASITE_USERNAME,ASITE_PASSWORDRENAME_BASE_DIRWCL_API_URL,WCL_TOKEN_URL,WCL_CLIENT_ID,WCL_CLIENT_SECRET,WCL_MAX_CONCURRENCYLITELLM_API_KEY,LITELLM_BASE_URL,AI_HTTP_TIMEOUT_SECONDSLICHESS_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_SECONDSJEEVTRAP_PORT,JEEVTRAP_DISCORD_TOKEN,JEEVTRAP_DISCORD_TEST_GUILD_IDJEEVTRAP_VOICE_ENABLED,JEEVTRAP_VOICE_CONFIG_YAML_PATHJEEVTRAP_GITHUB_TOKEN,JEEVTRAP_GITHUB_OWNER,JEEVTRAP_GITHUB_REPO,JEEVTRAP_GITHUB_BRANCH,JEEVTRAP_GITHUB_DEFAULT_DOCS_PATHJEEVTRAP_LINKDING_DATABASE_URL,JEEVTRAP_LINKDING_OWNER_IDJEEVTRAP_COMMAND_DEFAULT_TIMEOUT_SECONDS,JEEVTRAP_DISCORD_COMMAND_INTENT_DEBUG_REPLYJEEVTRAP_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_TOKEN은ROLE_AIclaim을 가진 AI 에이전트 전용 JWT이며scripts/fetch_master_data.py가 Bearer 토큰으로 사용함.VAULT_DATABASE_URL은jb_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.jeevtrap의HUB_INTERNAL_BASE_URL,DATABASE_URL,JEEVTRAP_LINKDING_DATABASE_URL,LITELLM_BASE_URL은 Docker DNS 서비스명이 아니라 호스트 네트워크에서 접근 가능한 주소를 사용함. - 운영
jeevtrap의 LiteLLM proxy 접근은 호스트 publish 포트를 기준으로 하며, 현재 운영 기준LITELLM_BASE_URL은http://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-apiinternal API와 JWT/Cookie 계약을 사용함. - PostgreSQL 영역은 잔여 도메인 스키마와 개발 문서 벡터 캐시에 사용함.
- Jeevtrap 명령 정의와 실행 로그는
jeevtrap스키마를 사용함. - 레이드 관련 도메인 데이터는
raid스키마를 사용함. - 체스 대국과 턴별 에이전트 로그는
chess스키마를 사용함. - 운동 기준정보, 운동 세션, 운동 기록, 운동 계획은
/sorc001/junkbox/db/workout.sqlite3SQLite 파일을 사용함. 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-apiinternal 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_m과chess.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_URL의POST /api/internal/auth/validate를 호출해 세션 유효성을 확인함. - AI 에이전트 전용
ROLE_AIJWT는 사용자 세션 테이블 검증 대상이 아니며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 SQLitetb_user_session_n에 세션 row를 생성하고, JWTsid, JWTexp, HttpOnly 쿠키max_age를 설정함. - 일반 사용자 JWT는
sidclaim을 포함하며,sid는 Hub SQLitetb_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-api와shared-core저장소 계층을 통해 수행함. - AI 에이전트 표준 통합 snapshot API는
GET /api/v1/master/all이며ROLE_AIJWT를 사용함.
9.3 운영 방식
- DB 기반 공통 마스터데이터는
hub-api가 단일 관리 주체임. - 화면과 API는 파일 저장소를 직접 노출하지 않고
shared-core저장소 계층을 통해 접근함. - 다른 앱과 모듈은
shared-core.masterdata.MasterDataService를 통해hub-api내부 master-data API를 소비함. shared-core.masterdata.HubMasterDataClient는HUB_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-api와workout-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/discord와interfaces/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_ID와APP_PROFILE기준의 guild guard를 통과한 서버 이벤트만 처리함. - 일반 메시지 명령 실행 로그는
DISCORD_MESSAGE, slash command 메뉴 명령 실행 로그는DISCORD_SLASHrequest channel code를 사용함. - 내장 명령 스크립트는 테스트 콘솔 출력, 데스크탑 WOL 켜기, 데스크탑 SSH 종료를 포함함.
jeevtrap는shared-ai의DirectAiRunner를 사용하며JEEVTRAP_MD,JEEVTRAP_BOOKMARK,JEEVTRAP_COMMAND_INTENTroute 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_URL의hub-api내부 API가 가용해야 함.jeevtrap의 일반 Discord UI 문구는 텍스트 DB 마스터데이터에 의존하지 않음.
12. 체스 샌드박스 구조
- 체스 샌드박스 API와 화면은
chess-api와chess-ui가 담당함. chess-api는 Lichess account stream, game stream, challenge API, bot move API를 직접 호출함.- 상대 유형이 일반/봇 계정이면
POST /api/challenge/{username}을 사용하고, Lichess 내장 AI이면POST /api/challenge/ai와level파라미터를 사용함. - 수 선택은 sleepzz-bot이 담당하며, 현재 상황과 착수 근거도 같은 응답에서 작성함.
- sleepzz-bot 응답은 legal moves choice 목록의 번호를 반환하며, 앱 계층은 choice를 UCI move로 매핑한 뒤 합법 수 여부를 검증함.
- choice 범위 오류, 매핑 실패, 합법 수 검증 실패가 발생하면 같은 턴에서 한 번 재시도하고, 반복 실패나 호출 실패가 발생하면 fallback 합법 수를 제출함.
- Lichess move 제출은 sleepzz-bot 착수 생성, 앱 계층 합법 수 검증, 또는 fallback 합법 수 선택 이후에만 수행함.
- 체스 착수는
shared-ai의GraphAiRunner와CHESS_MOVEgraph 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-api와raid-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-chess와junkbox-jeevtrap도 독립 서비스 단위로 감지, 빌드, 배포됨. - 운영 CDN이 고정 경로 정적 자산을 오래 캐시할 수 있으므로, 공통
shared-uiCSS/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 taskenv: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/db를hub-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_URL은http://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_TOKEN은raid-api,workout-api,rename-api,chess-api가hub-api내부 인증 API와 내부 master-data API를 호출할 때 사용하는 서버 간 토큰임.hub-api세션 정책 파일은apps/hub-api/var/auth-policy.yml이며, 운영 컨테이너에서는 동일 경로가 이미지 또는 마운트에 포함되어야 함.AI_ACCESS_TOKEN은 AI 에이전트 전용ROLE_AIJWT 값이며scripts/.env.ai에 보관함.FASTAPI_BASE_URL은scripts/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.py는vault/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_KEY와HUB_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를 단일 기준으로 따름.