JunkBox 코어 가이드
1. 프로젝트 정의
- JunkBox는 Python 기반 멀티 앱 구조를 사용하는 AI-Native 운영 시스템임.
- 공통 라이브러리 재사용, API 우선 설계, 운영 허브 집중화, Hub SQLite 공통 저장소, 앱별 SQLite 도메인 저장소, 개발 문서 벡터 캐시를 조합하는 구조를 사용함.
- 현재 구조에서
apps/hub-api는 상시 운영 허브이자 공통 인증 진입점, 공통 마스터 데이터 제공자 역할을 수행함. - 현재 구조에서
apps/raid-api는 WoW 레이드 운영 화면, WCL 점수 동기화, 공대 구인 알림 수집·분석·매칭을 담당하는 독립 앱임. - 현재 구조에서
apps/chess-api는 단일 Lichess OAuth 계정의 종료 대국 수집과 주석 포함 PGN 업로드, Stockfish 분석, Codex 코칭 데이터 저장, 복기를 담당하는 독립 앱임. - 현재 구조에서
apps/automation-api는 예약 실행, cron 정기 실행, 재시도, 실행 이력, 수동 실행을 독점적으로 소유하는 중앙 작업 실행 플랫폼임. - 현재 구조에서
apps/message-api는 외부 시스템의 즉시 알림 요청, 메시지 채널 전달, 발송 이력, 전달 재시도를 관리하고 Apprise API를 실제 전달 엔진으로 사용하는 독립 앱임. - 현재 구조에서
apps/track-api와apps/track-ui는 주기 여부와 무관한 개인 관리 항목, 실제 처리 이력, 선택적 도래 알림을 관리하는 모바일 전용 Track 앱임. - 현재 구조에서
apps/jeevtrap는 Discord 기반 독립 실행형 툴킷 데몬이며 공통 AI 런타임, GitHub, Linkding, 시스템 명령 실행, 선택적 로컬 음성 호출 인터페이스를 조합하는 별도 앱임.
2. 기술 스택
2.1 백엔드
- 언어는 Python 계열을 사용함.
- 웹 프레임워크는 FastAPI임.
- ORM 계층은 SQLModel과 SQLAlchemy 2 비동기 스택을 사용함.
- SQLite 비동기 드라이버는
aiosqlite임. - 워크스페이스와 패키지 관리는
uv를 사용함. - 공통 애플리케이션 로깅은
loguru기반 구조를 사용함. - 체스 분석은 Stockfish를 사용하고 Codex 코칭은 SQLite의 분석 결과를 근거로 별도 작업으로 반영함.
- 레이드 점수 연동은
httpx기반 외부 HTTP 호출과 GraphQL 요청을 사용함. - 애플리케이션 런타임 저장소는 SQLite를 사용함.
- AI 개발 문서 지식 검색은 애플리케이션 런타임과 분리된 Vault PostgreSQL
pgvector와 OpenAI 호환 embeddings API를 사용함.
2.2 프런트엔드 및 정적 자산
- 운영 화면 프런트엔드는
apps/hub-ui,apps/chess-ui,apps/raid-ui,apps/rename-ui,apps/workout-ui,apps/message-ui,apps/track-ui의 Vite 6 기반 React + TypeScript 프로젝트를 사용함. - FastAPI는 서버 렌더링 HTML 골격 위에 각 앱의
dist정적 자산을 마운트하여 화면을 제공함. - 로컬 개발에서는 각 UI 앱의 Vite 개발 서버와 FastAPI 백엔드를 병행 실행할 수 있음.
hub개발 흐름은15001프런트와18001백엔드 조합을 사용함.chess개발 흐름은15002프런트와18002백엔드 조합을 사용함.workout개발 흐름은15003프런트와18003백엔드 조합을 사용함.message개발 흐름은15004프런트와18004백엔드 조합을 사용함.rename개발 흐름은15006프런트와18006백엔드 조합을 사용함.raid개발 흐름은15009프런트와18009백엔드 조합을 사용함.track개발 흐름은15008프런트와18008백엔드 조합을 사용함.- 운영 배포 포트는
hub-api=8001,chess-api=8002,workout-api=8003,message-api=8004,rename-api=8006,automation-api=8007,raid-api=8009조합을 사용함. - 운영 배포 포트는
jeevtrap=8005를 추가로 사용함. - 운영 배포 포트는
track-api=8008을 추가로 사용함. - 공통 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/
│ ├── message-api/
│ ├── message-ui/
│ ├── automation-api/
│ ├── track-api/
│ ├── track-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 OAuth 계정, 종료 대국 수집, 주석 포함 PGN 업로드, Stockfish 분석, Chess SQLite 저장을 담당함.chess-ui는 완료 대국 목록, 보드 복기, 실제 수와 엔진 최선수 비교, 수별 엔진·코칭 정보 표시를 담당하는 React + TypeScript 프런트엔드 앱임.raid-api는 레이드 공대원 관리, 공대 배치, WCL 점수 동기화, 공대 구인 알림 도메인과 화면 진입점을 담당함.raid-ui는 PC 레이드 운영 화면과 모바일 공대 구인 알림 화면을 제공하는 React + TypeScript 프런트엔드 앱임.rename-api는 ASITE 기반 파일명 수집, Favorites, 로그, Rename 작업 API와 데스크톱 화면 진입점을 담당함.rename-ui는 rename 운영 화면 전용 React + TypeScript 프런트엔드 앱임.workout-api는 운동 도메인 API, 체중·선택 투약 기록 API, 설치형 PWA 진입, 운동 기준정보 관리 API, 규칙 엔진 기반 운동 계획 API를 담당함.workout-ui는 운동 기록 목록, 세션 상세, 체중 관리, 운동 계획, 운동 관리, 러닝 기록·계획·실행의 모바일 전용 React + TypeScript 프런트엔드 앱임.automation-api는 작업 정의, 1회·cron 일정, 다음 실행 시각, claim/lease, 재시도, 실행 이력, 수동 실행을 소유하고 등록된 internal Job HTTP API만 호출함.message-api는 Apprise 전달 어댑터, 메시지 이력, 즉시 발송, 수동 재발송과 Message 웹 앱 셸을 담당함.message-ui는 즉시 발송·발송 이력과 Automation이 소유하는 예약·정기 메시지 관리 화면을 제공하는 모바일 전용 React + TypeScript SPA임.track-api는 Track SQLite에서 관리 항목, 처리 이력, 알림 규칙과 발송 중복 방지 상태를 소유하며 Automation internal Job API를 제공함.track-ui는챙김 기록,챙김 관리와 처리 이력 등록·삭제를 제공하는 모바일 전용 React + TypeScript SPA임.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를 통해 조회함. - 다른 앱은 Hub SQLite 파일을 직접 열지 않으며, AI 사용 로그도
shared-ai가 Hub internal API로 전달하고 Hub가 저장함. - AI 코딩 컨텍스트도 공통 master-data 로컬 원본을 직접 읽지 않고
hub-api의ROLE_AIJWT 기반 통합 snapshot API를 통해 조회함. - workout 운동 계획 생성은
workout-api가 Workout SQLite 기준정보와 기록을 사용해 규칙 엔진으로 직접 수행함. - 체스 복기는
chess-api가 Lichess 종료 대국과 Stockfish 분석을 저장하고 Codex가 SQLite에 코칭 데이터를 반영하는 구조임.
4.2 공통 로직 중앙화
- 시간 기반 작업은
automation-api가 유일하게 소유함. 새 배치·예약·cron 작업은 개별 앱의 lifespan,asyncioloop, scheduler 클래스에 추가하지 않고 Automation 작업 계약으로 등록함. - 도메인 앱은 자기 SQLite 데이터와 업무 규칙을 소유하고, Automation이 호출할 최소 범위 internal Job API를 제공함.
- Automation은 Hub·Message·도메인 SQLite 파일을 직접 읽거나 쓰지 않으며, 대상 앱 HTTP API와 service-to-service 인증으로만 작업을 실행함.
- 도메인 조건이 즉시 발생한 메시지는 도메인 앱 outbox publisher가 Message API를 직접 호출함. 즉시 조건마다 Automation을 경유하지 않음.
- Message API는 메시지 채널 전달, 발송 이력, 전달 재시도만 소유함. Message UI의 예약·정기 관리 화면은 Automation API가 소유하는 작업 정의를 조회·수정하는 표현 계층임.
- 설정, JWT/쿠키 유틸리티, 공통 인증 의존성, 예외, 공통 엔티티, 공통 마스터 데이터 저장소, master-data 내부 API 소비 클라이언트, TTL 캐시는
shared-core에 둠. - 사용자 로그인 세션의 영속 상태와 유효성 판정은
hub-api에 둠. - AI provider 어댑터, 기능별 모델 라우팅, usage-log writer, 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에 저장함. - Track 도메인 데이터는
track-api가 단독 writer인 SQLite 파일/sorc001/junkbox/db/track.sqlite3에 저장함. - 체스 대국 마스터와 게임 로그는 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를 연결함.
4.5 신규 앱 생성 필수 계약
- 신규 앱은
apps/{app}-api와 화면이 필요한 경우apps/{app}-ui를 독립 패키지로 구성하고, Python workspace·Vite TypeScript strict·공통 라이브러리 의존성을 기존 앱 구조에 맞춰 등록함. - 구현 전에
scripts/search_dev_docs.py로 관련 개발 가이드를 의미 검색함. 공통코드, 메타, 포털 메뉴, 앱 아이콘 또는 Hub 설정이 관련되면scripts/fetch_master_data.pysnapshot을 먼저 조회함. - 모바일 전용 앱의 UX 기준 앱은
workout-ui임. 서버 렌더링 공통 앱 셸,BrowserRouter,spa_navigation=true,junkbox:spa-navigate이벤트 수신,SpaRouteTransition,useSpaRouteSettling, 모바일 고정 폭과 하단 고정 액션바를 공통 기준으로 사용함. - 화면 CSS는 반드시
shared-ui빌드 자산으로 공급함. 앱·모듈·화면 전용 CSS 파일,<style>태그, inlinestyle과 앱별font-family선언을 추가하지 않음. - 한글·영문·숫자는
shared-ui의PretendardVariable.woff2와 중앙--jb-font-family선언으로 렌더링함. 사용자 로컬 Pretendard, 유사 sans, 외부 폰트, 브라우저 기본 monospace가 우선하지 않는지 확인함. - 모바일 선택 UI는 네이티브
<select>대신jb-select외형 버튼과shared-uiBottomOptionPicker를 우선 사용함. 화면 전체를 관통하는 CUD 액션은jb-mobile-bottom-bar공통 하단 고정 액션바에 배치함. - 공통 앱 셸, 버튼, 입력창, 토스트, 아이콘, 바텀시트, SPA 유틸은 화면에서 복제하지 않고
@shared-ui/*와 공통 템플릿·클래스로 소비함. 새 공통 외형이 필요하면 앱 CSS가 아니라shared-ui에 추가함. - 신규 API·UI 앱의 개발 포트는 기존 사용 포트를 Hub 포털 메뉴 데이터와 현재 서비스 구성에서 확인한 뒤
1500xVite 포트와1800xFastAPI 포트를 각각 빈 번호로 배정함. 운영 컨테이너 포트는8001부터 기존 배정과 포털 데이터를 확인해 빈800x번호로 배정하며, 단순히 현재 최대 포트 다음 번호를 가정하지 않음. - 신규 앱의 포털 노출이 필요하면 Hub SQLite
tb_portal_menu_m에menu_type_code=APP메뉴를 등록하고 URL, 표시 순서,APP_ICONslug, 운영 포트를 현재 포털 계약에 맞춰 함께 관리함. Hub 데이터 변경 전에는 마스터데이터 조회와 SQLite 직접 반영 또는 수동 산출물 방식을 확정함. - 앱 도메인 데이터는 Hub SQLite에 섞지 않고 앱 전용 SQLite 파일과 앱 전용
*_DATABASE_URL을 사용함. SQLite 확인·생성·변경은 대상 파일과 테이블/컬럼을 확인하고 사용자 승인 범위에 따라 수행하며, 변경 뒤 스키마와 결과를 검증함. - 보호 웹 화면은 별도 인증 체계를 만들지 않고 Hub JWT Cookie/Header,
required_module,allowed_modules, Hub 공통 로그인·복귀 흐름을 사용함. 외부 호출 API와 관리자 화면의 인증·권한 요구는 명시적으로 분리함. - 운영 배포 대상 앱은 앱 API Dockerfile, 개발
docker-compose.yml, 운영docker-compose.prod.yml, 앱 DB·로그 볼륨, Hub 의존성, 환경 파일,docker-compose.prod.yml서비스 포트와 재시작 정책을 함께 구성함. - Dockerfile은 해당 UI
dist와shared-ui빌드 자산을 런타임 이미지에 포함함. GitHub Actions selective build/deploy의 서비스 정의, 배포 정책, 변경 감지 경로, 수동 배포 선택지까지 같은 서비스명으로 등록함. - 신규 앱은 다른 앱과 같은
.vscode/tasks.json개발 작업을 제공하고, Vite typecheck·build, 백엔드 컴파일 또는 테스트, Docker Compose config 검증, 웹 라우트·포털 진입·공통 CSS와 폰트 적용을 확인함. 검증을 위해 시작한 로컬 서버와 포트는 완료 전에 종료함. - 일반 UI 텍스트와 사용자 메시지는 한국어 소스 문구로 작성하며 DB 메시지 마스터를 만들지 않음. 문장형 노출 메시지는 하십시오체와 마침표를 사용함.
- 기능 구현 뒤 개발 가이드 문서는 자동으로 변경하지 않음. 문서 갱신은 사용자 요청이 있는 경우에만
vault/docs/junkboxSource of Truth를 갱신하고, 사용자가 검토한 뒤scripts/sync_dev_docs.py로 벡터 캐시를 동기화함.
5. 설정 구조
5.1 Settings 원칙
- 설정은
pydantic-settings기반으로 로딩함. - 기본
ENV_FILE값은.env.local임. ENV_FILE기준 환경 파일과 같은 디렉터리의.env.shared를 함께 로드함.ENV_FILE기준 환경 파일과 같은 디렉터리의.env.chess를 함께 로드함. Chess OAuth와 Stockfish 설정은 이 파일에서 관리함.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.chess, 선택된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 주요 설정 범주
- 앱별 SQLite DSN과
DB_ECHO HUB_DATABASE_URLCHESS_DATABASE_URLWORKOUT_DATABASE_URLMESSAGE_DATABASE_URLMESSAGE_APPRISE_DEFAULT_CONFIG_KEY: Apprise 요청에 설정 키가 없을 때 사용할 기본 설정 키이며 현재 값은apprise임.RAID_DATABASE_URLJEEVTRAP_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_DIR,RENAME_TARGET_DIR_NAMEWCL_API_URL,WCL_TOKEN_URL,WCL_CLIENT_ID,WCL_CLIENT_SECRET,WCL_MAX_CONCURRENCYLITELLM_API_KEY,LITELLM_BASE_URL,AI_HTTP_TIMEOUT_SECONDSLICHESS_OAUTH_CLIENT_ID,LICHESS_OAUTH_REDIRECT_URI,LICHESS_OAUTH_SYNC_INTERVAL_SECONDS,CHESS_STOCKFISH_PATH,CHESS_STOCKFISH_TIME_LIMIT_SECONDS,CHESS_STOCKFISH_DEPTH,CHESS_STOCKFISH_MULTIPV,JUNKBOX_CHESS_RESTART_POLICYJEEVTRAP_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_BASE_URL,JEEVTRAP_LINKDING_API_TOKENJEEVTRAP_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은 profile env와
ENV_FILE조합이 결정함. - 어떤 provider 구현이 선택되는지는
provider값이 결정하고,model값은 선택된 provider 내부에서 실제 원격 모델 식별자로 사용됨.
5.5 환경 파일 분류 원칙
.env.shared에는 프로필과 무관한 공통 인프라 정책을 둠..env.local,.env.prod에는 프로필별로 달라지는 값과 운영 민감도가 높은 값을 둠.RENAME_BASE_DIR와RENAME_TARGET_DIR_NAME은 모든 프로필에서 같은 비민감 파일 시스템 작업 기준값이므로.env.shared에 둠.AUTOMATION_BASE_URL은 프로필별 서비스 연결 값임..env.local은http://localhost:18007, Docker Compose 기반.env.prod는http://junkbox-automation-api:8007을 사용함..env.chess에는 Lichess OAuth, Stockfish, Chess 서비스 재시작 정책처럼 Chess 전용으로 공유되는 설정을 둠..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는 민감하지 않고 profile과 무관한 Jeevtrap 공통값을 보관함..env.local.jeevtrap,.env.prod.jeevtrap는.env.local,.env.prod를 참조하지 않으며,.env.shared,.env.jeevtrap와 함께 독립 실행에 필요한APP_PROFILE,JEEVTRAP_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,JEEVTRAP_LINKDING_BASE_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.chess,.env.prod,.env.jeevtrap,.env.prod.jeevtrap,.env.shared를/sorc001/junkbox/env/로 덮어쓰기 복사하는 운영 보조 흐름임. scripts/copy_env_files.sh는 복사 시작 전 Enter 확인, 대상 디렉터리 준비, 파일별 복사 진행 출력, 완료 후 Enter 대기 흐름을 제공함. 런타임에서 로드되는 프로젝트 루트 환경 파일을 새로 추가하면ENV_FILES와 이 문서의 복사 대상 목록을 함께 갱신해야 함.
6. 데이터베이스 전략
- 애플리케이션 런타임은 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 계약을 사용함. - Raid 도메인 데이터는
/sorc001/junkbox/db/raid.sqlite3SQLite 파일을 사용함. raid-api컨테이너 내부 표준 DB 경로는/app/db/raid.sqlite3이며, Docker compose는 호스트/sorc001/junkbox/db를 컨테이너/app/db에 마운트함.RAID_DATABASE_URL의 운영 및 Docker 기준 DSN은sqlite+aiosqlite:////app/db/raid.sqlite3이며, 로컬 비컨테이너 기본 DSN은sqlite+aiosqlite:////sorc001/junkbox/db/raid.sqlite3임.- Raid SQLite 연결은
PRAGMA journal_mode=WAL,PRAGMA busy_timeout=5000,PRAGMA foreign_keys=ON을 적용함. - Raid SQLite 파일은 Docker 이미지 생명주기와 분리된 호스트 영속 파일이며 단일 writer는
raid-api임. - Raid SQLite는 Hub 사용자·공통코드·메타 또는 다른 SQLite 파일에 DB 레벨 FK를 걸지 않으며, 해당 데이터는
hub-apiinternal API와 JWT/Cookie 계약으로 소비함. - Chess 도메인 데이터는
/sorc001/junkbox/db/chess.sqlite3SQLite 파일을 사용함. chess-api컨테이너 내부 표준 DB 경로는/app/db/chess.sqlite3이며, 개발·운영 Docker compose는 호스트/sorc001/junkbox/db를 컨테이너/app/db에 마운트함.CHESS_DATABASE_URL의 운영 및 Docker 기준 DSN은sqlite+aiosqlite:////app/db/chess.sqlite3이며, 로컬 비컨테이너 기본 DSN은sqlite+aiosqlite:////sorc001/junkbox/db/chess.sqlite3임.- Chess SQLite 연결은
PRAGMA journal_mode=WAL,PRAGMA busy_timeout=5000,PRAGMA foreign_keys=ON을 적용함. - Chess SQLite 파일은 Docker 이미지 생명주기와 분리된 호스트 영속 파일이며 단일 writer는
chess-api임. - Chess SQLite는 OAuth 연결 계정, 완료 대국, 수순, Stockfish 분석, Codex 코칭 데이터를 소유하며 Hub SQLite 사용자 ID를 저장하거나 SQLite 파일 간 FK를 만들지 않음.
- Jeevtrap 명령 정의와 실행 로그는
/sorc001/junkbox/db/jeevtrap.sqlite3의tb_command_m,tb_command_logs_n을 사용함. JEEVTRAP_DATABASE_URL은 Jeevtrap 전용 SQLite DSN임. 컨테이너는/sorc001/junkbox/db를/app/db에 마운트하여 같은 영속 파일을 사용함.- Jeevtrap SQLite 연결도
PRAGMA journal_mode=WAL,PRAGMA busy_timeout=5000,PRAGMA foreign_keys=ON을 적용함. - 과거 명령 로그에 명령 정의가 없는 행이 존재할 수 있으므로 Jeevtrap command log에는 DB 수준
command_idFK를 두지 않음. - 운동 기준정보, 운동 세션, 운동 기록, 운동 계획, 체중 측정, 투약 기록은
/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 식별자는 서비스 계층의 느슨한 참조로 유지함. - 메시지 발송 이력은
/sorc001/junkbox/db/message.sqlite3SQLite 파일을 사용함. message-api컨테이너 내부 표준 DB 경로는/app/db/message.sqlite3이며, Docker compose는 호스트/sorc001/junkbox/db를 컨테이너/app/db에 마운트함.MESSAGE_DATABASE_URL의 운영 및 Docker 기준 DSN은sqlite+aiosqlite:////app/db/message.sqlite3이며, 로컬 비컨테이너 기본 DSN은sqlite+aiosqlite:////sorc001/junkbox/db/message.sqlite3임.- Message SQLite 파일은 Docker 이미지 생명주기와 분리된 호스트 영속 파일이며
message-api가 유일한 writer임. - Message SQLite는
tb_message_log_n발송 이력만 소유함.schedule_id,recurrence_id,recurrence_scheduled_at은 기존 이력 호환용 컬럼이며 새 일정 정의로 사용하지 않음. - Automation SQLite는
/sorc001/junkbox/db/automation.sqlite3파일을 사용함.automation-api와 automation worker만 writer이며 작업 정의·수정 이력·실행 이력 테이블을 소유함. - 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 북마크 저장은 Linkding REST API를 사용하며, Jeevtrap은 Linkding DB 파일이나 테이블에 직접 접근하지 않음.
- 앱 추가 시 앱별 단독 writer가 명확한 도메인은 별도 SQLite 파일 DB를 사용하고, 다른 앱의 SQLite 파일에는 직접 접근하지 않음.
- DB 기반 운영 엔티티는
created_at,updated_at같은 감사 컬럼NOT NULL제약을 충족하는 기본값 전략을 함께 가져야 함. - 모든 JunkBox SQLite의 일시 컬럼은
Asia/Seoul(KST, UTC+9)을 기준으로 저장함. 신규 앱은datetime.now(timezone.utc),datetime.utcnow(), SQLiteCURRENT_TIMESTAMP로 UTC 값을 저장하지 않고shared-core의now_kst_naive()를 사용함. - SQLite의 timezone 없는 일시는 KST 로컬 일시로 해석함. 외부 API가 UTC 또는 offset을 포함한 일시를 제공하면 저장 전에 KST로 정규화하며, timezone-aware 컬럼 계약이 없는 SQLite 저장소에서는 timezone 정보를 제거함. 날짜 전용 값과 사용자가 직접 입력한 업무 시각은 변환 대상이 아님.
- Chess 저장소는
tb_lichess_account_m,tb_chess_game_m,tb_chess_game_move_n,tb_chess_engine_analysis_n,tb_chess_coaching_n을 사용함. - 대국 원본 PGN·FEN, 모든 수의 Stockfish 분석, 사용자 수의 코칭 결과를 분리 저장하며 세부 계약은 Chess API 가이드를 따름.
7. 비동기 ORM 규격
7.1 엔진과 세션
create_async_engine()과async_sessionmaker()를 사용함.- 의존성 주입은
async def get_session()제너레이터 패턴을 사용함. - 세션 타입은
sqlalchemy.ext.asyncio.AsyncSession임.
7.2 SQLModel 사용 규칙
Field(..., sa_column=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)는 모듈 권한 강제 의존성임.require_modules(*module_codes)는 전달된 모든 모듈 권한을 강제하는 공통 의존성임.- 공통 인증 의존성은 일반 사용자 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권한을 기준으로 접근을 제어함. - Workout의 공유 운동 기준정보 관리 영역은
WORKOUT과HUB권한을 모두 기준으로 접근을 제어함. - 레이드 운영 화면과 API는
RAID권한을 기준으로 접근을 제어함. - 체스 복기 화면과 API는
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기준으로 격리됨. - 러닝 계획과 블록·단계 하위 데이터 조회·저장도 현재 사용자
user_id기준으로 격리됨. - Workout의 공유 운동 기준정보를 제외한 신규 도메인 기능은 소유자
user_id를 저장하고, API/service 계층의 모든 조회·집계·계산·변경 경로에서 현재 사용자 조건을 강제함.
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 REST API로 저장함./명령은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가 담당함. - 단일 Lichess OAuth 계정의 종료 대국을 커서 기준으로 수집하며, 주석이 포함된 Lichess PGN 파일도 업로드할 수 있음.
- 대국 식별자는 자동 수집과 수동 업로드를 통틀어 중복 저장하지 않으며, 연결 계정이 백색 또는 흑색인 대국만 사용자 관점으로 판정함.
- 연결 계정이 대국에 없으면 관전 대국으로 저장하며, 사용자 수 판정과 Codex 코칭 대상에서 제외함.
- 대국 원본 PGN, 수순별 착수 전후 FEN, SAN/UCI, Stockfish 결과와 주 변형을 저장함.
- Codex는 엔진 분석이 완료된 사용자 수에만 구조화된 코칭 결과를 저장함.
- 화면은 최신 등록 순의 대국 목록, 보드 복원, 실제 진행·엔진 최선수 비교·PV 재생, 수별 엔진 정보와 사용자 수 코칭 정보를 제공함.
- 실시간 착수, 대국 생성, WebSocket 관전은 제공하지 않음.
- Chess 컨테이너는
JUNKBOX_CHESS_RESTART_POLICY기본값unless-stopped를 사용함. - 상세 계약은
docs/junkbox/apps/guide-apps-chess-api.md,docs/junkbox/apps/guide-apps-chess-ui.md를 따름.
13. 레이드 구조
- 레이드 운영 화면과 API는
raid-api와raid-ui가 담당함. - 공대원 목록과 관리는 PC/FHD 밀도 기준의 Tabulator 단일 그리드 중심 구조를 사용함.
공대원 목록과공대원 관리화면 모두 Tabulator 기반 전체 로딩 구조를 사용함.- 공대 구인 알림은 모바일 우선 카드·시트·하단 고정 액션바 구조를 사용하며, 검색 조건은 프로필의 추가·제외 검색어만 사용함.
- 공대 구인 본문은 수집·AI 분석 중 메모리에만 두고 저장하지 않음. Raid는 분석·매칭·발송 조건을 소유하고, Automation은 예약 실행을, Message는 실제 전달·이력·재시도를 소유함.
- 레이드 화면은
shared-ui공통 다크 자산과 Tabulator 공통 자산을 그대로 사용함. - 레이드 멤버 풀과 레이드별 배치는 분리된 테이블 구조를 사용하며, 하나의 멤버가 여러 레이드에 중복 배치될 수 있음.
- 레이드 멤버 풀의 구분은
RAID_MEMBER_CATEGORY공통코드로 관리하며,나,추적,공대원의 공통코드 정렬 순서를 목록과 관리 화면에 적용함. 구분은 레이드별 배치가 아닌 멤버 풀 속성임. - 공대원 목록은
나를 항상 포함하고,추적과공대원은 멀티 선택 조회 조건으로 제어함. 점수 갱신은 구분과 무관하게 현재 레이드의 전체 멤버 집합을 대상으로 수행함. - 레이드 점수 동기화는 Warcraft Logs API 연동을 사용하며, 설정은
settings.raid.wcl중첩 구조를 기준으로 로드함. - 레이드 점수 갱신은 전체 레이드 멤버 집합 기준으로 수행하며, 결과는 Raid SQLite의 복합 PK 점수 테이블에 반영됨.
- 레이드 화면은 공통 로그인 진입점을
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,apps/message-ui빌드 결과물dist와 Vite manifest는 각 백엔드 앱이 직접 서빙하는 운영 자산임.- 개발 모드에서는 Vite dev server가 화면 엔트리와 HMR 자산을 제공하고, FastAPI는 서버 렌더링 HTML과 API를 제공함.
- 개발 서버 기준 루트 진입 경로는
hub모드에서/portal,chess모드에서/chess,workout모드에서/workout-sessions,message모드에서/messages,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기준 독립 컨테이너 구조를 사용하며,automation-api와 automation worker는 같은junkbox-automation이미지를 별도 서비스로 실행함. - 운영 배포 자동화는 GitHub Actions selective build/deploy 흐름을 사용하며,
junkbox-message를 포함한 각 앱은 독립 서비스 단위로 감지, 빌드, 배포됨. junkbox-chess배포 정책은autoStartOnDeploy=true와restartPolicy=unless-stopped를 사용하므로, 배포 후 Chess 컨테이너를 생성 상태로 남기지 않고 기동함.- 운영 CDN이 고정 경로 정적 자산을 오래 캐시할 수 있으므로, 공통
shared-uiCSS/JS는 버전 쿼리 파라미터 링크를 기본 구조로 사용함. - 운영 Chess 컨테이너는
/sorc001/junkbox/env/.env.chess,/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.chess,.env.prod,.env.jeevtrap,.env.prod.jeevtrap,.env.shared를 운영 env 디렉터리에 반영할 때는 VS Code build taskenv:copy-to-sorc001또는 동일한scripts/copy_env_files.sh흐름을 사용함. - 운영 배포 compose는
automation-api=/logs001/junkbox/automation-api,automation-worker=/logs001/junkbox/automation-worker를 포함한 앱별 호스트 로그 경로를 각 컨테이너의/app/logs에 마운트하는 구조를 사용함. - 운영 배포 compose는 Hub/Chess/Workout/Message/Raid/Jeevtrap SQLite 호스트 경로
/sorc001/junkbox/db를 각 앱 컨테이너의/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}동일 경로 바인드 마운트를 전제로 동작하며,RENAME_TARGET_DIR_NAME은 마운트된 작업 루트 내부의 입력 작업 폴더를 지정함.- 로컬 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,CHESS_DATABASE_URL=sqlite+aiosqlite:////app/db/chess.sqlite3,WORKOUT_BASE_URL=https://workout.sleepzz.xyz,WORKOUT_DATABASE_URL=sqlite+aiosqlite:////app/db/workout.sqlite3,RAID_BASE_URL=https://raid.sleepzz.xyz,RAID_DATABASE_URL=sqlite+aiosqlite:////app/db/raid.sqlite3,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,JEEVTRAP_LINKDING_BASE_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값을 함께 점검함. - 체스 운영에서는
CHESS_DATABASE_URL, Lichess OAuth 설정, Stockfish 실행 파일 경로와 분석 제한값,JUNKBOX_CHESS_RESTART_POLICY값을 함께 점검함. HUB_API_INTERNAL_TOKEN은raid-api,workout-api,rename-api,chess-api,jeevtrap가hub-api내부 인증 API, 내부 master-data API, AI usage-log 기록 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를 단일 기준으로 따름.