docs: 다이어그램 4종 세트 완성, 범례 한글화
아키텍처 · 운영자 심사 워크플로 · 제출물 상태 전이를 신설하고, 기존 증거
데이터 흐름까지 4종의 범례를 한글화했다. 렌더러 기본 범례가 "User UI /
Agent logic / Context-trace" 나 "policy / PII" 처럼 타 도메인 문구였다 —
legend.entries 로 라벨만 바꿨고 종류와 개수는 그대로다.
system-architecture 폐쇄망 경계와 그 밖의 외부 검색 API. 나가는 경로는
store_remote_fetch 하나뿐이고 앞에 호출 게이트가 있다
operator-review-workflow 4레인(제출·자동·심사·차단). 비상 차단이 걸려도
커버리지 부족 표시로 심사가 이어지는 경로 포함
submission-lifecycle 분석 실패와 보류가 종료가 아니라 분석 중으로
돌아오는 두 고리, 판정 후 보존 상태
작성 중 내린 고도 판단 두 개를 남긴다.
아키텍처에서 analyzer/scorer 를 "분석 · 채점" 한 컴포넌트로 합쳤다. 이
고도에서 그 분리는 엣지를 벌지 못하고, store 노드에 엣지 7개가 몰려
통로가 부족해졌다. 채점 내부는 dataflow 다이어그램이 담당한다.
라이프사이클에서 "재분석 요청"을 상태에서 뺐다. decisionStatus 값이 아니라
운영자 액션이고, 보류가 같은 의미를 담아 긴 복귀 엣지를 하나로 줄인다.
검증: 4종 모두 showcase 통과(아티팩트 검사 9/9 · 합성 오류 0 · 경고 0),
visual-check 통과(1440x900 · 2048x1320 각 Light/Dark, 오버플로 0).
html sha256 — dataflow a62f0f5c / architecture f5a4b6e1 /
workflow 402914a3 / lifecycle 62e934a3
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BStHFrjQj2VypecSG9ex1V
This commit is contained in:
parent
e4fffc40be
commit
d45018c4d0
10 changed files with 997 additions and 4 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
# Project INDEX
|
# Project INDEX
|
||||||
|
|
||||||
|
- 2026-09-19 · 다이어그램 4종으로 확장 — `system-architecture`(폐쇄망 경계·SSRF 가드 앞 호출 게이트), `operator-review-workflow`(4레인, 비상 차단 시 심사 계속 경로), `submission-lifecycle`(분석 실패·보류가 분석으로 복귀하는 두 고리)를 신설하고 기존 `evidence-dataflow` 포함 4종의 범례를 한글화했다. 렌더러 기본 범례가 "User UI / Agent logic / policy·PII" 같은 타 도메인 문구였다. 작성 중 내린 고도 판단: 아키텍처에서는 analyzer/scorer 를 한 컴포넌트로 합쳤고(세부는 dataflow 담당), 라이프사이클에서 "재분석 요청"은 decisionStatus 가 아닌 운영자 액션이라 상태에서 제외해 복귀 엣지를 하나로 줄였다. 4종 모두 archify showcase 통과(아티팩트 검사 9/9 · 합성 오류 0 · 경고 0) + visual-check 통과(1440×900 · 2048×1320 각 Light/Dark, 오버플로 0). 수신증 — dataflow html a62f0f5c / architecture f5a4b6e1 / workflow 402914a3 / lifecycle 62e934a3
|
||||||
|
|
||||||
- 2026-09-19 · `docs/diagrams/evidence-dataflow.html` 신설 — 증거 데이터 흐름(수집 → 경계 검사 → 정규화·채점 → 영속화 → 소비) 자체 완결 다이어그램. 9/18 리뷰 수정의 불변식을 그림으로 고정한다: **제공자별 풀·버킷**(RiskScorer v3), **라운드트립이 원 점수를 재현**(`_evidence_payload` ↔ `_domain_evidence_from_ui`), **host 는 저장 시 해석·복원 시 우선 반환**, 지식DB 지문 매칭이 풀링 밖 유일한 high 승격 경로. 프로덕션 LLM 경로가 영속화 payload 를 복원해 쓴다는 사실(`store_operations.py:487`, `EvidenceEnricher` 는 테스트 전용)도 반영. archify showcase 검증 통과(아티팩트 검사 9/9 · 합성 오류 0 · 경고 0), 브라우저 증거 통과(1440×900 · 2048×1320 각 Light/Dark 4컷, 오버플로 0, 최소 투영 텍스트 6.71px ≥ 6px). spec sha256 42c334cd · html sha256 268f3352 (822,655 B). CDN·원격 fetch 없음 — 폐쇄망 규칙 준수
|
- 2026-09-19 · `docs/diagrams/evidence-dataflow.html` 신설 — 증거 데이터 흐름(수집 → 경계 검사 → 정규화·채점 → 영속화 → 소비) 자체 완결 다이어그램. 9/18 리뷰 수정의 불변식을 그림으로 고정한다: **제공자별 풀·버킷**(RiskScorer v3), **라운드트립이 원 점수를 재현**(`_evidence_payload` ↔ `_domain_evidence_from_ui`), **host 는 저장 시 해석·복원 시 우선 반환**, 지식DB 지문 매칭이 풀링 밖 유일한 high 승격 경로. 프로덕션 LLM 경로가 영속화 payload 를 복원해 쓴다는 사실(`store_operations.py:487`, `EvidenceEnricher` 는 테스트 전용)도 반영. archify showcase 검증 통과(아티팩트 검사 9/9 · 합성 오류 0 · 경고 0), 브라우저 증거 통과(1440×900 · 2048×1320 각 Light/Dark 4컷, 오버플로 0, 최소 투영 텍스트 6.71px ≥ 6px). spec sha256 42c334cd · html sha256 268f3352 (822,655 B). CDN·원격 fetch 없음 — 폐쇄망 규칙 준수
|
||||||
|
|
||||||
- 2026-09-18 · `/code-review` 15건 전수 처리 (**헤드라인 1건은 반증**). 반증: "CSE 웹 증거가 라운드트립 후 0→45점" — 리뷰가 `match` 키 없는 합성 Evidence 로 재현한 것이고, 실제 CSE 웹 결과는 `google_custom_search.py:301` 에서 `match="page"` 를 달고 오며 `matchType` 이 충실히 왕복한다(신선/복구 모두 35점). 직렬화 불변식 위반 없음. 대신 그 조사에서 **리뷰가 놓친 라이브 버그**가 나왔다: `counted_web_buckets` 가 제공자를 안 가려 Vision 이 `match="page"` 를 먼저 보고하면 **CSE 웹 히트가 점수도 사유도 없이 통째로 버려졌다**. 실수정 — **점수 v2 → v3**(풀·버킷을 제공자별로: Vision 자기 상관 뷰 4개는 60/medium 유지, Vision full + CSE page 는 45+35=80/high 복원 · 도달 불가였던 `category` 탈출구 제거 + 미등록 유명인 미탐은 의도된 트레이드임을 명시, 승격 경로는 지식DB/FINGERPRINT), **저장·복구 5종**(`_result_host` 가 자기 host 를 안 읽어 재직렬화 시 API 이름으로 퇴행하던 잠재 버그 + 점 없는 토큰 호스트 거부 · 복구가 남긴 고아 `faceCrops` 참조를 재생성/정리 · `assetMissing` 해제 경로 신설로 두 번째 소실도 감사에 남김 · 플래그+감사를 한 트랜잭션으로 · 스코어러 마이그레이션 밴드 변경에 `scoredVersion` 스탬프 + 감사 1회), **콘솔 4종**(긴 CDN 호스트가 제목을 세로 컬럼으로 만든 flex 버그 — 실측 360px 51×95px→246×19px, **1280px 9×380px→205×19px 로 폰 전용이 아니었음** · 20ch 로 잘린 호스트를 툴팁에 · `bloggerLink` 폴백 누락 · 보존 케이스 설명 표시), **PROMPT_VERSION ko-v4-structured-host**(실모델 프로브 qwen2.5:7b — grounded 3/dropped 0/verified True, 반향 없음). 테스트 +14건. **ruff clean · pytest 586 passed**. 발견된 기존 결함 1건은 미수정 기록: 로컬 ollama 가 떠 있으면 `test_http_app.py` 3건이 실호출 타임아웃으로 실패한다(기본 생성 store 가 `127.0.0.1:11434` 기본값에 붙음 — clean HEAD 에서도 동일). 커밋 b0aa4c5 / 4ba83eb / e38dad8
|
- 2026-09-18 · `/code-review` 15건 전수 처리 (**헤드라인 1건은 반증**). 반증: "CSE 웹 증거가 라운드트립 후 0→45점" — 리뷰가 `match` 키 없는 합성 Evidence 로 재현한 것이고, 실제 CSE 웹 결과는 `google_custom_search.py:301` 에서 `match="page"` 를 달고 오며 `matchType` 이 충실히 왕복한다(신선/복구 모두 35점). 직렬화 불변식 위반 없음. 대신 그 조사에서 **리뷰가 놓친 라이브 버그**가 나왔다: `counted_web_buckets` 가 제공자를 안 가려 Vision 이 `match="page"` 를 먼저 보고하면 **CSE 웹 히트가 점수도 사유도 없이 통째로 버려졌다**. 실수정 — **점수 v2 → v3**(풀·버킷을 제공자별로: Vision 자기 상관 뷰 4개는 60/medium 유지, Vision full + CSE page 는 45+35=80/high 복원 · 도달 불가였던 `category` 탈출구 제거 + 미등록 유명인 미탐은 의도된 트레이드임을 명시, 승격 경로는 지식DB/FINGERPRINT), **저장·복구 5종**(`_result_host` 가 자기 host 를 안 읽어 재직렬화 시 API 이름으로 퇴행하던 잠재 버그 + 점 없는 토큰 호스트 거부 · 복구가 남긴 고아 `faceCrops` 참조를 재생성/정리 · `assetMissing` 해제 경로 신설로 두 번째 소실도 감사에 남김 · 플래그+감사를 한 트랜잭션으로 · 스코어러 마이그레이션 밴드 변경에 `scoredVersion` 스탬프 + 감사 1회), **콘솔 4종**(긴 CDN 호스트가 제목을 세로 컬럼으로 만든 flex 버그 — 실측 360px 51×95px→246×19px, **1280px 9×380px→205×19px 로 폰 전용이 아니었음** · 20ch 로 잘린 호스트를 툴팁에 · `bloggerLink` 폴백 누락 · 보존 케이스 설명 표시), **PROMPT_VERSION ko-v4-structured-host**(실모델 프로브 qwen2.5:7b — grounded 3/dropped 0/verified True, 반향 없음). 테스트 +14건. **ruff clean · pytest 586 passed**. 발견된 기존 결함 1건은 미수정 기록: 로컬 ollama 가 떠 있으면 `test_http_app.py` 3건이 실호출 타임아웃으로 실패한다(기본 생성 store 가 `127.0.0.1:11434` 기본값에 붙음 — clean HEAD 에서도 동일). 커밋 b0aa4c5 / 4ba83eb / e38dad8
|
||||||
|
|
|
||||||
|
|
@ -169,9 +169,16 @@ WAL 적용됨. DB 기본 경로는 `data/copyrighter.sqlite3`(gitignore).
|
||||||
|
|
||||||
## 다이어그램
|
## 다이어그램
|
||||||
|
|
||||||
| 파일 | 내용 |
|
소스 JSON 과 산출물 HTML 이 같은 폴더에 쌍으로 있다. **그림을 고치려면 JSON 을 고쳐
|
||||||
|---|---|
|
`archify validate` → `deliver` → `visual-check` 로 재생성한다** (HTML 직접 수정 금지).
|
||||||
| `docs/diagrams/evidence-dataflow.html` | 증거 데이터 흐름 5단(수집·경계 검사·정규화/채점·영속화·소비). 소스는 같은 폴더의 `evidence-dataflow.dataflow.json` (archify dataflow 스키마 v1) — 그림을 고치려면 JSON 을 고쳐 `archify deliver` 로 재생성한다. 자체 완결 HTML(외부 요청 없음) |
|
모두 자체 완결 HTML 이라 외부 요청이 없다.
|
||||||
|
|
||||||
|
| 파일 | 내용 | 타입 |
|
||||||
|
|---|---|---|
|
||||||
|
| `evidence-dataflow.html` | 증거 데이터 흐름 5단(수집·경계 검사·정규화/채점·영속화·소비). 라운드트립 불변식이 중심 | dataflow |
|
||||||
|
| `system-architecture.html` | 시스템 구성. 폐쇄망 경계와 그 밖의 외부 검색 API, SSRF 가드 앞의 호출 게이트 | architecture |
|
||||||
|
| `operator-review-workflow.html` | 운영자 심사 4레인(제출·자동·심사·차단). 비상 차단 시 심사가 이어지는 경로 포함 | workflow v2 |
|
||||||
|
| `submission-lifecycle.html` | 제출물 상태 전이. 분석 실패·보류가 종료가 아니라 분석으로 복귀하는 두 고리 | lifecycle |
|
||||||
|
|
||||||
## 알려진 부채
|
## 알려진 부채
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,24 @@
|
||||||
"viewBox": [
|
"viewBox": [
|
||||||
1080,
|
1080,
|
||||||
500
|
500
|
||||||
]
|
],
|
||||||
|
"legend": {
|
||||||
|
"mode": "auto",
|
||||||
|
"entries": {
|
||||||
|
"emphasis": {
|
||||||
|
"label": "주 경로"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"label": "등급 통제"
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"label": "저장소"
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"label": "데이터 흐름"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"stages": [
|
"stages": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Binary file not shown.
BIN
docs/diagrams/operator-review-workflow.html
Normal file
BIN
docs/diagrams/operator-review-workflow.html
Normal file
Binary file not shown.
349
docs/diagrams/operator-review-workflow.workflow.json
Normal file
349
docs/diagrams/operator-review-workflow.workflow.json
Normal file
|
|
@ -0,0 +1,349 @@
|
||||||
|
{
|
||||||
|
"schema_version": 2,
|
||||||
|
"diagram_type": "workflow",
|
||||||
|
"meta": {
|
||||||
|
"title": "운영자 심사 워크플로",
|
||||||
|
"output": "docs/diagrams/operator-review-workflow.html",
|
||||||
|
"quality_profile": "showcase",
|
||||||
|
"views": [
|
||||||
|
{
|
||||||
|
"id": "happy-path",
|
||||||
|
"label": "정상 경로",
|
||||||
|
"focus": [
|
||||||
|
"folder",
|
||||||
|
"seed",
|
||||||
|
"analyze",
|
||||||
|
"search",
|
||||||
|
"score",
|
||||||
|
"evidence",
|
||||||
|
"decide"
|
||||||
|
],
|
||||||
|
"note": "폴더 투입에서 판정까지 가는 주 경로."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "stop-path",
|
||||||
|
"label": "비상 차단",
|
||||||
|
"focus": [
|
||||||
|
"search",
|
||||||
|
"stop",
|
||||||
|
"skipped",
|
||||||
|
"evidence"
|
||||||
|
],
|
||||||
|
"note": "외부 호출이 멈췄을 때 심사가 어떻게 이어지는지."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "assist",
|
||||||
|
"label": "판독 보조",
|
||||||
|
"focus": [
|
||||||
|
"search",
|
||||||
|
"llm",
|
||||||
|
"evidence"
|
||||||
|
],
|
||||||
|
"note": "LLM 이 증거를 읽고 초안을 내는 보조 경로."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"legend": {
|
||||||
|
"mode": "auto",
|
||||||
|
"entries": {
|
||||||
|
"external": {
|
||||||
|
"label": "외부 입력"
|
||||||
|
},
|
||||||
|
"backend": {
|
||||||
|
"label": "서버 처리"
|
||||||
|
},
|
||||||
|
"frontend": {
|
||||||
|
"label": "운영자 화면"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"label": "차단 · 정책"
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"label": "저장소"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lanes": [
|
||||||
|
{
|
||||||
|
"id": "intake",
|
||||||
|
"label": "제출 · 시드"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "auto",
|
||||||
|
"label": "자동 분석 · 검색"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "review",
|
||||||
|
"label": "운영자 심사"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "exception",
|
||||||
|
"label": "차단 · 보존",
|
||||||
|
"variant": "exception"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"phases": [
|
||||||
|
{
|
||||||
|
"id": "p-intake",
|
||||||
|
"label": "제출 · 시드",
|
||||||
|
"fromCol": 0,
|
||||||
|
"toCol": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "p-auto",
|
||||||
|
"label": "자동 분석 · 검색",
|
||||||
|
"fromCol": 2,
|
||||||
|
"toCol": 3,
|
||||||
|
"variant": "emphasis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "p-judge",
|
||||||
|
"label": "채점 · 심사 · 판정",
|
||||||
|
"fromCol": 4,
|
||||||
|
"toCol": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "g-exception",
|
||||||
|
"label": "외부 호출이 멈춘 경우",
|
||||||
|
"lane": "exception",
|
||||||
|
"fromCol": 3,
|
||||||
|
"toCol": 4,
|
||||||
|
"variant": "security"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "g-assist",
|
||||||
|
"label": "판독 보조",
|
||||||
|
"lane": "review",
|
||||||
|
"fromCol": 3,
|
||||||
|
"toCol": 3,
|
||||||
|
"variant": "dashed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mainPath": [
|
||||||
|
"folder",
|
||||||
|
"seed",
|
||||||
|
"analyze",
|
||||||
|
"search",
|
||||||
|
"score",
|
||||||
|
"evidence",
|
||||||
|
"decide"
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "folder",
|
||||||
|
"lane": "intake",
|
||||||
|
"col": 0,
|
||||||
|
"type": "external",
|
||||||
|
"label": "큐 폴더 · 업로드",
|
||||||
|
"sublabel": "이미지 · manifest",
|
||||||
|
"width": 132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "seed",
|
||||||
|
"lane": "intake",
|
||||||
|
"col": 1,
|
||||||
|
"type": "backend",
|
||||||
|
"label": "시드 · 리로드",
|
||||||
|
"sublabel": "신규 레코드만",
|
||||||
|
"tag": "판정 보존",
|
||||||
|
"width": 132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "analyze",
|
||||||
|
"lane": "auto",
|
||||||
|
"col": 2,
|
||||||
|
"type": "backend",
|
||||||
|
"label": "내부 분석",
|
||||||
|
"sublabel": "지문 · 얼굴 · 출처",
|
||||||
|
"width": 132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "search",
|
||||||
|
"lane": "auto",
|
||||||
|
"col": 3,
|
||||||
|
"type": "backend",
|
||||||
|
"label": "자동 검색",
|
||||||
|
"sublabel": "쿼리 생성 · 승격",
|
||||||
|
"width": 132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "score",
|
||||||
|
"lane": "auto",
|
||||||
|
"col": 4,
|
||||||
|
"type": "backend",
|
||||||
|
"label": "위험도 산출",
|
||||||
|
"sublabel": "점수 · 밴드 · 사유",
|
||||||
|
"tag": "v3",
|
||||||
|
"width": 132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "llm",
|
||||||
|
"lane": "review",
|
||||||
|
"col": 3,
|
||||||
|
"type": "backend",
|
||||||
|
"label": "LLM 증거 판독",
|
||||||
|
"sublabel": "인용 검증 · 12행",
|
||||||
|
"width": 132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "evidence",
|
||||||
|
"lane": "review",
|
||||||
|
"col": 4,
|
||||||
|
"type": "frontend",
|
||||||
|
"label": "증거 검토",
|
||||||
|
"sublabel": "호스트 · 썸네일 · 링크",
|
||||||
|
"width": 132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "decide",
|
||||||
|
"lane": "review",
|
||||||
|
"col": 5,
|
||||||
|
"type": "backend",
|
||||||
|
"label": "판정",
|
||||||
|
"sublabel": "승인 · 보류 · 반려",
|
||||||
|
"tag": "감사 이벤트",
|
||||||
|
"width": 132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "stop",
|
||||||
|
"lane": "exception",
|
||||||
|
"col": 3,
|
||||||
|
"type": "security",
|
||||||
|
"label": "비상 차단 스위치",
|
||||||
|
"sublabel": "쿼터 · provider 토글",
|
||||||
|
"width": 132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "skipped",
|
||||||
|
"lane": "exception",
|
||||||
|
"col": 4,
|
||||||
|
"type": "security",
|
||||||
|
"label": "검색 건너뜀",
|
||||||
|
"sublabel": "0점 · 사유만 기록",
|
||||||
|
"width": 132
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "retained",
|
||||||
|
"lane": "exception",
|
||||||
|
"col": 5,
|
||||||
|
"type": "database",
|
||||||
|
"label": "보존된 케이스",
|
||||||
|
"sublabel": "원본 파일 없음",
|
||||||
|
"tag": "assetMissing",
|
||||||
|
"width": 132
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"id": "e-intake",
|
||||||
|
"from": "folder",
|
||||||
|
"to": "seed",
|
||||||
|
"variant": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-seed-analyze",
|
||||||
|
"from": "seed",
|
||||||
|
"to": "analyze",
|
||||||
|
"variant": "emphasis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-analyze-search",
|
||||||
|
"from": "analyze",
|
||||||
|
"to": "search",
|
||||||
|
"variant": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-search-score",
|
||||||
|
"from": "search",
|
||||||
|
"to": "score",
|
||||||
|
"variant": "emphasis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-score-evidence",
|
||||||
|
"from": "score",
|
||||||
|
"to": "evidence",
|
||||||
|
"variant": "emphasis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-evidence-decide",
|
||||||
|
"from": "evidence",
|
||||||
|
"to": "decide",
|
||||||
|
"label": "근거 확인 후",
|
||||||
|
"variant": "emphasis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-search-stop",
|
||||||
|
"from": "search",
|
||||||
|
"to": "stop",
|
||||||
|
"label": "외부 호출 요청",
|
||||||
|
"variant": "security"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-stop-skipped",
|
||||||
|
"from": "stop",
|
||||||
|
"to": "skipped",
|
||||||
|
"label": "차단됨",
|
||||||
|
"variant": "security",
|
||||||
|
"role": "error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-skipped-evidence",
|
||||||
|
"from": "skipped",
|
||||||
|
"to": "evidence",
|
||||||
|
"label": "커버리지 부족 표시",
|
||||||
|
"variant": "default",
|
||||||
|
"role": "branch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-search-llm",
|
||||||
|
"from": "search",
|
||||||
|
"to": "llm",
|
||||||
|
"label": "판독 요청",
|
||||||
|
"variant": "dashed",
|
||||||
|
"role": "async"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-llm-evidence",
|
||||||
|
"from": "llm",
|
||||||
|
"to": "evidence",
|
||||||
|
"label": "인용된 초안",
|
||||||
|
"variant": "dashed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e-decide-retained",
|
||||||
|
"from": "decide",
|
||||||
|
"to": "retained",
|
||||||
|
"label": "파일 소실 시",
|
||||||
|
"variant": "default",
|
||||||
|
"role": "branch"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"dot": "emerald",
|
||||||
|
"title": "주 경로",
|
||||||
|
"items": [
|
||||||
|
"리로드는 신규 레코드만 분석한다 — 기존 행은 판정과 함께 보존된다",
|
||||||
|
"자동 검색은 쿼리를 생성하고 유사도가 맞는 결과만 증거로 승격한다"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dot": "rose",
|
||||||
|
"title": "멈췄을 때",
|
||||||
|
"items": [
|
||||||
|
"비상 차단은 시드 · 재분석 · 얼굴 크롭 경로까지 모두 막는다",
|
||||||
|
"건너뛴 검색은 0점이며 위험 사유가 아니라 운영 상태로만 기록된다"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dot": "cyan",
|
||||||
|
"title": "판독 보조",
|
||||||
|
"items": [
|
||||||
|
"LLM 은 요약기가 아니라 증거 판독기다 — 인용 없는 claim 은 폐기된다",
|
||||||
|
"프롬프트 입력은 상위 12행으로 제한된다"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
docs/diagrams/submission-lifecycle.html
Normal file
BIN
docs/diagrams/submission-lifecycle.html
Normal file
Binary file not shown.
280
docs/diagrams/submission-lifecycle.lifecycle.json
Normal file
280
docs/diagrams/submission-lifecycle.lifecycle.json
Normal file
|
|
@ -0,0 +1,280 @@
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"diagram_type": "lifecycle",
|
||||||
|
"meta": {
|
||||||
|
"title": "제출물 상태 전이",
|
||||||
|
"output": "docs/diagrams/submission-lifecycle.html",
|
||||||
|
"quality_profile": "showcase",
|
||||||
|
"viewBox": [
|
||||||
|
1080,
|
||||||
|
630
|
||||||
|
],
|
||||||
|
"views": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"label": "주 단계",
|
||||||
|
"focus": [
|
||||||
|
"intake",
|
||||||
|
"analyzing",
|
||||||
|
"pending",
|
||||||
|
"decision",
|
||||||
|
"approved"
|
||||||
|
],
|
||||||
|
"note": "접수에서 승인까지 이어지는 다섯 단계."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "recovery",
|
||||||
|
"label": "복구 경로",
|
||||||
|
"focus": [
|
||||||
|
"analyzing",
|
||||||
|
"failed",
|
||||||
|
"held"
|
||||||
|
],
|
||||||
|
"note": "끝나지 않고 분석으로 되돌아오는 두 고리."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "terminal",
|
||||||
|
"label": "종료 상태",
|
||||||
|
"focus": [
|
||||||
|
"decision",
|
||||||
|
"approved",
|
||||||
|
"rejected",
|
||||||
|
"retained"
|
||||||
|
],
|
||||||
|
"note": "되돌아오지 않는 상태와, 판정을 남긴 채 파일만 사라진 상태."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"legend": {
|
||||||
|
"mode": "auto",
|
||||||
|
"entries": {
|
||||||
|
"start": {
|
||||||
|
"label": "시작"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"label": "진행 중"
|
||||||
|
},
|
||||||
|
"waiting": {
|
||||||
|
"label": "대기"
|
||||||
|
},
|
||||||
|
"decision": {
|
||||||
|
"label": "판정"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"label": "정상 종료"
|
||||||
|
},
|
||||||
|
"failure": {
|
||||||
|
"label": "실패 · 종료"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lanes": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"label": "제출물 단계"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "waiting",
|
||||||
|
"label": "대기 · 보강"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "exceptions",
|
||||||
|
"label": "복구 경로"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "terminal",
|
||||||
|
"label": "종료 상태"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "intake",
|
||||||
|
"type": "start",
|
||||||
|
"label": "접수",
|
||||||
|
"sublabel": "폴더 · 업로드",
|
||||||
|
"lane": "main",
|
||||||
|
"col": 0,
|
||||||
|
"step": "01",
|
||||||
|
"tag": "신규"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "analyzing",
|
||||||
|
"type": "active",
|
||||||
|
"label": "분석 중",
|
||||||
|
"sublabel": "지문 · 얼굴 · 검색",
|
||||||
|
"lane": "main",
|
||||||
|
"col": 1,
|
||||||
|
"step": "02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pending",
|
||||||
|
"type": "active",
|
||||||
|
"label": "심사 대기",
|
||||||
|
"sublabel": "점수 · 밴드 산출됨",
|
||||||
|
"lane": "main",
|
||||||
|
"col": 2,
|
||||||
|
"step": "03",
|
||||||
|
"tag": "unreviewed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "decision",
|
||||||
|
"type": "decision",
|
||||||
|
"label": "판정",
|
||||||
|
"sublabel": "운영자 결정",
|
||||||
|
"lane": "main",
|
||||||
|
"col": 3,
|
||||||
|
"step": "04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "approved",
|
||||||
|
"type": "success",
|
||||||
|
"label": "승인",
|
||||||
|
"sublabel": "사용 가능",
|
||||||
|
"lane": "main",
|
||||||
|
"col": 4,
|
||||||
|
"step": "05",
|
||||||
|
"tag": "approved"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "held",
|
||||||
|
"type": "waiting",
|
||||||
|
"label": "보류",
|
||||||
|
"sublabel": "추가 확인 필요",
|
||||||
|
"lane": "waiting",
|
||||||
|
"col": 0,
|
||||||
|
"tag": "held"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "failed",
|
||||||
|
"type": "failure",
|
||||||
|
"label": "분석 실패",
|
||||||
|
"sublabel": "provider 실패",
|
||||||
|
"lane": "exceptions",
|
||||||
|
"col": 0,
|
||||||
|
"tag": "복구 가능",
|
||||||
|
"yOffset": 78
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rejected",
|
||||||
|
"type": "failure",
|
||||||
|
"label": "반려",
|
||||||
|
"sublabel": "사용 불가",
|
||||||
|
"lane": "terminal",
|
||||||
|
"col": 1,
|
||||||
|
"tag": "rejected"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "retained",
|
||||||
|
"type": "failure",
|
||||||
|
"label": "보존",
|
||||||
|
"sublabel": "원본 파일 없음",
|
||||||
|
"lane": "terminal",
|
||||||
|
"col": 2,
|
||||||
|
"tag": "assetMissing"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transitions": [
|
||||||
|
{
|
||||||
|
"id": "t-intake",
|
||||||
|
"from": "intake",
|
||||||
|
"to": "analyzing",
|
||||||
|
"variant": "emphasis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t-analyzed",
|
||||||
|
"from": "analyzing",
|
||||||
|
"to": "pending",
|
||||||
|
"variant": "emphasis",
|
||||||
|
"route": "straight"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t-review",
|
||||||
|
"from": "pending",
|
||||||
|
"to": "decision",
|
||||||
|
"variant": "emphasis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t-approve",
|
||||||
|
"from": "decision",
|
||||||
|
"to": "approved",
|
||||||
|
"label": "승인",
|
||||||
|
"variant": "emphasis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t-hold",
|
||||||
|
"from": "pending",
|
||||||
|
"to": "held",
|
||||||
|
"label": "추가 확인 필요",
|
||||||
|
"labelDy": 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t-fail",
|
||||||
|
"from": "analyzing",
|
||||||
|
"to": "failed",
|
||||||
|
"label": "provider 실패",
|
||||||
|
"variant": "security",
|
||||||
|
"labelDy": -24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t-retry",
|
||||||
|
"from": "failed",
|
||||||
|
"to": "analyzing",
|
||||||
|
"label": "재시도",
|
||||||
|
"variant": "emphasis",
|
||||||
|
"labelDy": 6,
|
||||||
|
"channelX": 240,
|
||||||
|
"toSide": "bottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t-reject",
|
||||||
|
"from": "decision",
|
||||||
|
"to": "rejected",
|
||||||
|
"label": "반려",
|
||||||
|
"variant": "security",
|
||||||
|
"labelDy": 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t-retain",
|
||||||
|
"from": "rejected",
|
||||||
|
"to": "retained",
|
||||||
|
"label": "원본 파일 소실",
|
||||||
|
"variant": "dashed",
|
||||||
|
"labelDy": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t-rerun",
|
||||||
|
"from": "held",
|
||||||
|
"to": "analyzing",
|
||||||
|
"label": "재분석 요청",
|
||||||
|
"variant": "emphasis",
|
||||||
|
"channelX": 170,
|
||||||
|
"toSide": "left"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"dot": "emerald",
|
||||||
|
"title": "주 단계",
|
||||||
|
"items": [
|
||||||
|
"접수된 제출물은 분석을 거쳐 심사 대기 상태에서 점수와 밴드를 갖는다",
|
||||||
|
"판정은 운영자만 내린다 — 자동 분석은 밴드까지만 만든다"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dot": "rose",
|
||||||
|
"title": "되돌아오는 상태",
|
||||||
|
"items": [
|
||||||
|
"분석 실패와 재분석 요청은 종료가 아니라 분석 중으로 돌아온다",
|
||||||
|
"보류는 운영자가 증거를 보강하려는 상태다 — 재분석은 기존 증거를 지우지 않는다"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dot": "amber",
|
||||||
|
"title": "보존",
|
||||||
|
"items": [
|
||||||
|
"판정을 남긴 케이스는 원본 파일이 사라져도 지워지지 않는다",
|
||||||
|
"파일이 돌아오면 보존 표시는 해제되고 감사에 남는다"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
338
docs/diagrams/system-architecture.architecture.json
Normal file
338
docs/diagrams/system-architecture.architecture.json
Normal file
|
|
@ -0,0 +1,338 @@
|
||||||
|
{
|
||||||
|
"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 v3",
|
||||||
|
"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 선택 사항이며 없으면 연결이 빠르게 실패한다"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
docs/diagrams/system-architecture.html
Normal file
BIN
docs/diagrams/system-architecture.html
Normal file
Binary file not shown.
Loading…
Reference in a new issue