{ "schema_version": 1, "diagram_type": "architecture", "meta": { "title": "POSA Copyrighter 시스템 구성", "output": "docs/diagrams/system-architecture.html", "quality_profile": "showcase", "viewBox": [ 1080, 500 ], "views": [ { "id": "request-path", "label": "요청 경로", "focus": [ "console", "http", "store", "sqlite" ], "note": "정적 콘솔에서 스토어와 SQLite 까지 가는 주 경로." }, { "id": "analysis", "label": "분석 · 채점", "focus": [ "store", "images", "analysis" ], "note": "제출 이미지에서 증거와 점수가 만들어지는 고리." }, { "id": "egress", "label": "외부 경계", "focus": [ "store", "gate", "fetch", "providers", "ollama" ], "note": "폐쇄망을 넘는 유일한 경로와 그 앞의 두 관문." } ] }, "components": [ { "id": "console", "type": "frontend", "label": "운영자 콘솔", "sublabel": "정적 SPA · 빌드 없음", "tag": "app-*.js 15", "pos": [ 36, 170 ], "size": [ 124, 62 ] }, { "id": "throttle", "type": "security", "label": "인증 스로틀", "sublabel": "10회 / 60초", "tag": "주소별", "pos": [ 250, 60 ], "size": [ 124, 62 ] }, { "id": "http", "type": "backend", "label": "http_app", "sublabel": "라우팅 · 인증 · CSP", "tag": "바디 10MB", "pos": [ 250, 170 ], "size": [ 124, 62 ] }, { "id": "images", "type": "database", "label": "이미지 저장소", "sublabel": "제출물 · manifest", "pos": [ 464, 60 ], "size": [ 124, 62 ] }, { "id": "store", "type": "backend", "label": "CopyrighterStore", "sublabel": "믹스인 8개 조립", "tag": "판정 · 감사", "pos": [ 464, 170 ], "size": [ 124, 62 ] }, { "id": "sqlite", "type": "database", "label": "SQLite", "sublabel": "payload JSON · WAL", "tag": "SCHEMA v2", "pos": [ 464, 280 ], "size": [ 124, 62 ] }, { "id": "analysis", "type": "backend", "label": "분석 · 채점", "sublabel": "지문 · 얼굴 · 출처", "tag": "RiskScorer v6", "pos": [ 678, 60 ], "size": [ 124, 62 ] }, { "id": "gate", "type": "security", "label": "외부 호출 게이트", "sublabel": "쿼터 · 비상 차단", "pos": [ 678, 280 ], "size": [ 124, 62 ] }, { "id": "ollama", "type": "external", "label": "로컬 LLM", "sublabel": "ollama · qwen2.5:7b", "tag": "선택", "pos": [ 892, 170 ], "size": [ 124, 62 ] }, { "id": "fetch", "type": "security", "label": "store_remote_fetch", "sublabel": "SSRF 가드", "tag": "DNS 1회 해석", "pos": [ 892, 280 ], "size": [ 124, 62 ] }, { "id": "providers", "type": "external", "label": "외부 검색 API", "sublabel": "Vision · CSE · 네이버", "pos": [ 678, 420 ], "size": [ 124, 62 ] } ], "boundaries": [ { "kind": "region", "label": "폐쇄망 (air-gapped) — 런타임 서드파티는 이미지 처리 3종", "wraps": [ "console", "throttle", "http", "images", "store", "sqlite", "analysis", "gate", "ollama", "fetch" ] }, { "kind": "security-group", "label": "네트워크 경계", "wraps": [ "gate", "fetch" ] } ], "connections": [ { "id": "console-api", "from": "console", "to": "http", "label": "REST", "variant": "emphasis" }, { "id": "auth-throttle", "from": "throttle", "to": "http", "label": "인증 시도 제한", "variant": "security", "labelDy": 24 }, { "id": "http-store", "from": "http", "to": "store", "variant": "emphasis" }, { "id": "store-sqlite", "from": "store", "to": "sqlite", "label": "payload JSON", "variant": "emphasis", "labelDy": 24 }, { "id": "store-images", "from": "store", "to": "images" }, { "id": "analysis-store", "from": "analysis", "to": "store", "label": "증거 · 점수 · 밴드", "labelDy": 24 }, { "id": "store-gate", "from": "store", "to": "gate", "label": "외부 호출 요청" }, { "id": "gate-fetch", "from": "gate", "to": "fetch", "label": "허용 시 통과", "variant": "security" }, { "id": "fetch-providers", "from": "fetch", "to": "providers", "label": "HTTPS", "variant": "security" }, { "id": "store-ollama", "from": "store", "to": "ollama", "label": "판독 요청", "variant": "dashed" } ], "cards": [ { "dot": "rose", "title": "경계", "items": [ "설치·런타임에 인터넷 없음을 전제한다 — CDN · 원격 fetch · 자동업데이트 금지", "외부로 나가는 경로는 store_remote_fetch 하나뿐이고 SSRF 가드가 앞에 있다", "게이트는 쿼터와 비상 차단을 본다 — 시드 · 재분석 · 얼굴 크롭 경로 모두 통과한다" ] }, { "dot": "emerald", "title": "서버", "items": [ "http_app 이 라우팅 · Bearer/세션 인증 · CSP · CSRF · 10MB 바디 상한을 맡는다", "CopyrighterStore 는 믹스인 8개로 조립되고 SQLite payload JSON 에 쓴다", "BatchAnalyzer 를 스토어가 구동하고, 증거·점수·밴드가 스토어로 돌아온다" ] }, { "dot": "cyan", "title": "콘솔 · 의존성", "items": [ "콘솔은 빌드 스텝이 없는 클래식 스크립트 15개로 전역 스코프를 공유한다 — 로드 순서가 계약", "얼굴 검출은 YuNet(ONNX)이고 가중치는 gitignore 된 models/ 에 있다", "런타임 서드파티는 numpy · opencv-python-headless · pillow 3종", "LLM 은 로컬 ollama 선택 사항이며 없으면 연결이 빠르게 실패한다" ] } ] }