22 KiB
Project Structure — Dashboard 관리자 인증
목적과 경계
Issue #6 인증을 기존 Dashboard 구조에 배치하고 fail-closed 경계와 server-only secret binding을 유지한다.
- 포함 경계:
dashboard/인증 adapter/gate, tests, 운영 문서 - 제외 경계: MCP transport, 금융 schema, DB migration, external auth middleware
디렉터리 구조
dashboard-source — Dashboard source
dashboard/
├── server.py
├── auth_server.py
├── authentication.py
├── app.py
├── common.py
└── page_views/
| 경로 | 종류 | 책임 | 공개 진입점 | 내부 구현 경계 | 편집 정책 | 생성 주체 |
|---|---|---|---|---|---|---|
dashboard/server.py |
source | ASGI app 조립 | app |
route wiring only | source review required | developer |
dashboard/auth_server.py |
source | login/cleanup/logout response | create_auth_routes() |
no DB/page imports | source review required | developer |
dashboard/authentication.py |
source | config, constant-time compare, token verify, gate | require_dashboard_authentication() |
server-only secret use | source review required | developer |
dashboard/app.py |
source | auth gate first, then DB path resolution, common shell/sidebar/refresh assembly, and target body renderer dispatch | main() |
shell/background remain outside the replaceable body; no token signing details | source review required | developer |
dashboard/common.py |
source | shared Dashboard shell, menu/query transition, stable body container, loading/error/retry rendering, and app background/style | render_sidebar_nav(), render_dashboard_body(), inject_app_css() |
no authentication transport or financial write orchestration | source review required | developer |
dashboard/page_views/ |
source | financial UI pages | page_* functions |
no auth transport calls | source review required | developer |
dashboard/common.py owns the shared visual theme through JARCA_COLORS, JARCA_PLOTLY_TEMPLATE, and inject_app_css(). Page renderers consume that theme but do not own or replace the authenticated shell/sidebar background.
verification-tests — 인증 검증
tests/
├── test_dashboard_authentication.py
├── test_dashboard_persistent_authentication.py
├── test_dashboard_auth_server.py
├── test_dashboard_menu_transition.py
├── test_streamlit_browser_evidence.py
└── test_dashboard_authentication_app.py
| 경로 | 종류 | 책임 | 공개 진입점 | 내부 구현 경계 | 편집 정책 | 생성 주체 |
|---|---|---|---|---|---|---|
tests/test_dashboard_authentication.py |
test | credential compare, delay, redaction | pytest tests | test-only settings | source change paired with test | developer |
tests/test_dashboard_persistent_authentication.py |
test | claims, TTL, tamper, rotation | pytest tests | no real secrets | source change paired with test | developer |
tests/test_dashboard_auth_server.py |
test | HTTP routes and cookie scope | pytest tests | synthetic test credentials | source change paired with test | developer |
tests/test_dashboard_menu_transition.py |
test | REQ-002 menu/query/body transition and cache-clear regression | pytest/AppTest | synthetic fixture DB; no real secrets | source change paired with test and browser evidence where required | developer |
tests/test_streamlit_browser_evidence.py |
test | transition evidence manifest, redaction, TLS fixture and fail-closed validation | pytest | generated synthetic values only; no captured credential values | evidence-harness change paired with test | developer |
tests/test_dashboard_authentication_app.py |
test | gate ordering and cleanup/UI | AppTest | fixture DB only | source change paired with test | developer |
browser-evidence — 실제 전환 관찰
| 경로 | 종류 | 책임 | 공개 진입점 | 내부 구현 경계 | 편집 정책 | 생성 주체 |
|---|---|---|---|---|---|---|
scripts/streamlit_browser_evidence.py |
verification tool | synthetic DB와 runtime 생성 인증 설정으로 실제 HTTPS Dashboard 전환의 frame/DOM/document/shell 관찰을 수집하고 redacted manifest를 검증 | --mode transition |
운영 DB·credential 사용 금지; 성공 증거는 browser/server teardown 뒤에만 확정 | source review and focused test required | developer |
docs/evidence/issue-9-dashboard-menu-transition/ |
evidence artifact | exact source hash에 결합한 authenticated browser frame, screenshot, runtime, validator 결과 | manifest.json |
synthetic fixture only; credential/token/cookie 값 금지 | immutable after verification except complete regeneration | verification runner |
deployment-docs — 운영 문서
docs/requirements/req-001_dashboard-administrator-authentication.md
docs/requirements/req-002_dashboard-menu-transition.md
docs/evidence/issue-9-dashboard-menu-transition/
docs/interface-contract.md
docs/runtime-flow.md
README.md
deploy/k8s/README.md
| 경로 | 종류 | 책임 | 공개 진입점 | 내부 구현 경계 | 편집 정책 | 생성 주체 |
|---|---|---|---|---|---|---|
docs/requirements/req-001_dashboard-administrator-authentication.md |
requirement | approved REQ-001 revision 2.0 | REQ-001 | canonical traceability | update in place | developer |
docs/requirements/req-002_dashboard-menu-transition.md |
requirement | approved REQ-002 revision 1.0 | REQ-002 | canonical traceability | update in place | developer |
docs/interface-contract.md |
contract | HTTP/cookie/gate contracts | contract IDs | no implementation secrets | update with API changes | developer |
docs/runtime-flow.md |
design | runtime states/transitions | flow IDs | no secret values | update with flow changes | developer |
README.md |
operations | local/deployment constraints | operator commands | no secret values | operational review | developer |
deploy/k8s/README.md |
operations | k3s secret/TLS procedure | deployment procedure | no secret values | deployment review | developer |
의존 방향
| 출발 영역 | 대상 영역 | 허용 여부 | 규칙 | 공유 계약 위치 |
|---|---|---|---|---|
dashboard.server |
dashboard.auth_server |
허용 | compose ASGI routes only | docs/interface-contract.md |
dashboard.server |
dashboard.app |
허용 | app remains protected by gate | docs/runtime-flow.md |
dashboard.app |
dashboard.authentication |
허용 | gate precedes DB/page dispatch | docs/interface-contract.md |
dashboard.authentication |
dashboard.page_views |
금지 | auth module never imports financial pages | docs/project-structure.md |
dashboard.app |
dashboard.common |
허용 | menu/query/body transition helpers only after auth gate | docs/interface-contract.md |
dashboard.auth_server |
jarca_ledger_mcp DB |
금지 | login/cleanup/logout are DB-free | docs/requirements/req-001_dashboard-administrator-authentication.md |
변경 규칙
| 구분 | 규칙 |
|---|---|
| 새 모듈·파일 배치 | auth route/gate changes stay in dashboard/; menu transition stays in existing dashboard.app/dashboard.common; no DB session module |
| 이름·패키지 | use existing dashboard package and snake_case modules |
| 소스·테스트 대응 | auth source behavior requires focused pytest/AppTest coverage; REQ-002 transition behavior requires focused pytest/AppTest targets and real-browser evidence where specified |
| 생성 산출물 | evidence may be redacted; never commit credentials, secrets, tokens, or cookie values |
검증
-
구조 검사 명령:
python -m compileall -q dashboard tests scripts -
구조 검사 기대 결과: exit 0 and no generated source outside ignored bytecode locations
-
정적 분석 명령:
uv run --extra dashboard --extra dev ruff check dashboard tests scripts -
정적 분석 기대 결과: exit 0 with no diagnostics
-
문서와 실제 트리의 정합성: every listed source/test/document path exists; dependency table has no forbidden reverse edge; REQ-002 symbols resolve to
dashboard/app.py,dashboard/common.py, and named tests
REQ-003 additive structure
ledger-application — shared valuation and snapshot services
jarca_ledger_mcp/
├── application/account_valuation_service.py
├── application/assets_service.py
├── application/accounts_write_services.py
├── application/imports_service.py
├── application/reconciliation_service.py
├── infrastructure/sqlite/migrations.py
├── adapters/mcp/server.py
└── adapters/streamlit/{accounts_gateway.py,assets_gateway.py}
| 경로 | 종류 | 책임 | 공개 진입점 | 내부 구현 경계 | 편집 정책 | 생성 주체 |
|---|---|---|---|---|---|---|
jarca_ledger_mcp/application/account_valuation_service.py |
source | validation, selection, idempotency, correction | named valuation functions | SQLite through shared db | source review + focused tests | developer |
jarca_ledger_mcp/application/assets_service.py |
source | preview, formula, close/correction | preview_asset_snapshot_close, close_asset_snapshot |
snapshot transaction | source review + focused tests | developer |
jarca_ledger_mcp/infrastructure/sqlite/migrations.py |
source | schema 12 additive migration | init_db, migrate_db |
no UI/MCP imports | migration review | developer |
tests/test_account_valuations.py |
test | valuation contracts | pytest | synthetic DB | paired with source | developer |
tests/test_asset_snapshots.py |
test | preview/close/report contracts | pytest | synthetic DB | paired with source | developer |
docs/data-model.md |
canonical doc | entity/schema authority | document | no secrets | update with schema | developer |
의존 방향 추가
| 출발 영역 | 대상 영역 | 허용 여부 | 규칙 | 공유 계약 위치 |
|---|---|---|---|---|
dashboard.page_views |
streamlit gateways |
허용 | page → gateway only | docs/interface-contract.md |
streamlit gateways |
application services |
허용 | adapter delegates; no SQL | docs/runtime-flow.md |
MCP adapters |
application services |
허용 | tool delegates; no duplicate business logic | docs/interface-contract.md |
application services |
infrastructure.sqlite |
허용 | service → shared db/schema | docs/data-model.md |
application services |
dashboard |
금지 | no reverse UI dependency | docs/project-structure.md |
변경 규칙 추가
| 구분 | 규칙 |
|---|---|
| 새 모듈·파일 배치 | valuation/snapshot logic stays in application; adapters remain thin; no direct SQL in page/MCP |
| 이름·패키지 | existing snake_case modules and service re-exports |
| 소스·테스트 대응 | every valuation/snapshot change paired with named synthetic tests and docs validator |
| 생성 산출물 | only redacted synthetic evidence; no credentials, cookie, token or actual account data |
REQ-003 preservation inventory
REQ-001 auth gate, cookie/cleanup/logout and auth-before-DB; REQ-002 menu labels/order, query sibling preservation, stable shell/background and browser evidence remain in the preceding sections. Missing semantic items: 0. Ambiguous semantic items: 0.
REQ-003 canonical module ownership and regression map
| 경로 | 책임·실패 경계 |
|---|---|
jarca_ledger_mcp/application/account_valuation_service.py |
register/list/select/correct; principal mapping, source_ref identity, parent scope, idempotency and no-write conflicts |
jarca_ledger_mcp/application/assets_service.py |
preview/close/report; valuation_date/age/carry, approval, correction, warnings/exclusions and atomic run |
jarca_ledger_mcp/application/asset_account_services.py |
shared account/asset evidence and reconciliation boundary |
jarca_ledger_mcp/infrastructure/sqlite/connection.py |
read_db() mode=ro and db_not_initialized without mkdir/migrate/commit |
jarca_ledger_mcp/infrastructure/sqlite/migrations.py |
explicit init_db() and additive migration |
jarca_ledger_mcp/application/monthly_settlement_service.py |
monthly read model; monthly_source_snapshot_id alias only, never persistence |
Named transitions are valuation-01→valuation-02→valuation-03→valuation-04, close-01→close-02→close-03→close-04, prop-01→prop-02→prop-03, db-init-01, db-read-01, db-read-02, and write-first-use-01; named failures are valuation-fail, close-fail, prop-fail, db_not_initialized, conditional_approval_required, idempotency_conflict, and correction_conflict. auth-before-DB precedes all reads.
Regression tests are test_valuation_round_trip_preserves_null_zero_and_physical_amount_mapping, test_source_ref_is_account_scoped_evidence_identity_without_explicit_key, test_direct_correction_requires_matching_existing_parent_and_reason, test_investment_without_verified_valuation_and_without_opening_balance_is_incomplete, test_timeseries_and_monthly_expose_canonical_asset_provenance, and test_read_queries_missing_nested_db_return_stable_error_without_creating_path. D06 requires these names and actual paths/signatures, not marker prose.
D01 preservation matrix
| item | source evidence | test evidence |
|---|---|---|
| schema 11 legacy rows | infrastructure/sqlite/migrations.py: additive schema 12 migration |
tests/test_issue_12_regressions.py::test_schema12_migration_preserves_legacy_rows_indexes_and_is_idempotent |
| auth gate and cookie lifecycle | dashboard/server.py: login, cleanup, logout, auth-before-DB gate |
tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py |
| REQ-002 menu labels/order | dashboard/common.py: canonical 10-option menu and query transition |
tests/test_dashboard_menu_transition.py |
| MCP envelope and tools | adapters/mcp/server.py: shared success/error response envelope and named tools |
tests/test_contract_regression.py |
| reconciliation and monthly read models | application/reconciliation_service.py, application/monthly_settlement_service.py: read-only propagation |
tests/test_issue_12_review59_reports.py, tests/test_service.py::test_monthly_settlement_summary_read_model_foundation |
Each row is an independently reviewable preservation claim: the source column names the canonical implementation boundary and the test column names executable evidence. A prose inventory without both references is not evidence.
D05 canonical ownership
| concern | owner | boundary |
|---|---|---|
| monthly settlement | monthly_settlement_service.py |
read-only monthly read model; monthly_source_snapshot_id aliases snapshot_run_id, never persistence |
| reconciliation | reconciliation_service.py |
computes completeness/status and provenance; never creates fake transactions |
| policy | assets_service.py |
owns exact/as-of, approval, exclusion/carry, correction and drift policy decisions |
| read-only | assets_service.py preview/report path |
preview is no write: no insert, migrate, mkdir, or commit; explicit close is the write boundary |
| redaction | adapters/report serializers | only redacted evidence crosses output boundary; no credential/token/cookie/raw account data |
The owner is the canonical decision or write boundary; consumers render or read its result and do not reimplement policy.
The Dashboard gateway is jarca_ledger_mcp/adapters/streamlit/overview_gateway.py (including get_asset_snapshot_report and reconciliation/monthly read delegates). It calls shared application read services after auth-before-DB; page rendering is not an alternate policy or persistence boundary. A missing DB has stable unavailable semantics (db_not_initialized) and no-create behavior. The missing DB stable unavailable contract is no-create. An initialized DB read failure is db_error; initialized read has no write, migration, mkdir, or commit. The initialized DB read failure is db_error and initialized read has no write.
Reconciliation ownership remains in reconciliation_service.py: opening evidence and transaction coverage are required inputs. Missing opening evidence or incomplete coverage produces INCOMPLETE_CALCULATION/reconciliation unavailable with provenance, never an inferred balance or fake transaction.
Issue #15 settlement draft ownership
| Path | Ownership / boundary |
|---|---|
jarca_ledger_mcp/application/settlement_draft_service.py |
Shared business validation, frozen selection/review/approval/finalize; owns no UI or transport behavior. |
jarca_ledger_mcp/application/monthly_settlement_service.py |
Read-only monthly aggregate; never draft mutation or transaction readiness write. |
jarca_ledger_mcp/application/assets_service.py |
Existing close/report authority; draft finalization does not call live-select close as shortcut. |
jarca_ledger_mcp/infrastructure/sqlite/migrations.py |
Additive schema 12→13 migration; schema initializer and migration constraints agree. |
jarca_ledger_mcp/adapters/mcp/server.py |
Thin same-name/same-signature MCP tools; no duplicate business logic, no new auth claim. |
jarca_ledger_mcp/adapters/streamlit/ |
Existing Dashboard gateway boundary; auth-before-DB, explicit user actions only. |
tests/test_monthly_settlement_drafts.py |
Synthetic DB lifecycle, failure injection, replay and no-write regressions. |
Never document invented jarca_ledger_mcp.mcp or monthly.py modules. transaction readiness is separate from asset completeness and monthly reads remain read-only.
Shared settlement-draft entrypoints
| Path | Responsibility / entrypoint and dependency boundary |
|---|---|
jarca_ledger_mcp/service.py |
Existing public re-export facade exports the ten lifecycle operations from application/settlement_draft_service.py; adapters share these functions, not separate business implementations. |
jarca_ledger_mcp/adapters/mcp/server.py |
Same-name MCP lifecycle tools delegate to the shared facade, retaining existing success/error envelopes. |
jarca_ledger_mcp/adapters/mcp/tool_contracts.py |
Registers the same facade callables and their inputSchema contracts; internal db_path remains outside public MCP input. |
jarca_ledger_mcp/adapters/streamlit/settlement_gateway.py |
Wraps shared lifecycle functions plus valuation list/register/correct delegates, preserving auth-before-DB and internal database-path resolution. |
dashboard/page_views/settlement_drafts.py |
render_settlement_drafts owns reusable presentation and explicit user-action controls through the gateway; no direct SQL or duplicated approval policy. |
dashboard/page_views/monthly_settlement.py |
page_monthly_settlement invokes the shared panel beside the existing read-only monthly summary. |
dashboard/page_views/assets.py |
page_assets invokes the same panel beside existing valuation and immutable asset-history views. |
jarca_ledger_mcp/application/account_valuation_service.py |
register_account_valuation, list_account_valuations, and correct_account_valuation retain registration, validation and append-only correction ownership; the draft service reuses their evidence. |
jarca_ledger_mcp/application/assets_service.py |
Owns established valuation/amount authority and asset close/report semantics; draft finalization does not bypass review by calling legacy live-select close. |
jarca_ledger_mcp/application/monthly_settlement_service.py |
get_monthly_settlement_summary owns read-only propagation of finalized evidence, not draft mutation or transaction-readiness writes. |
jarca_ledger_mcp/infrastructure/sqlite/schema.py |
schema 13 initialization uses the additive migration path; no adapter owns persistence constraints. |
jarca_ledger_mcp/infrastructure/sqlite/migrations.py |
Creates persistent draft/item tables and indexes, preserving existing schema and rows on repeated initialization. |
Shared settlement-draft policies
| Module | Visibility | Responsibility / caller | Verification |
|---|---|---|---|
jarca_ledger_mcp/application/settlement_cashflow_service.py |
private | _normalize_cashflow_policy and _review_cashflow provide policy normalization and deterministic aggregation through settlement_draft_service.py; not an adapter/public API |
tests/test_settlement_cashflow_policy.py, tests/test_comprehensive_settlement_adapters.py |
jarca_ledger_mcp/application/settlement_draft_service.pychecks one active draft per month for ordinary and correction drafts; SQLite independently enforces it withidx_settlement_drafts_active_monthon monthWHERE status='active'. Completed and cancelled history is retained, not deleted to release that slot.- The same application service validates strict month syntax and month-end basis-date equality. Dashboard and MCP cannot supply an alternate date policy.
- The shared review service exposes unverified
candidate_amountwith authority amount NULL; no adapter may sum it as verified evidence. A confirmed child requires explicit reselection, fresh review and approval; correction children are not silently followed. - Dependencies remain page → gateway → public facade → application → SQLite, with MCP server/tool contracts → the same facade. No UI/MCP direct SQL, application-to-Dashboard reverse dependency or duplicated business validation is introduced.
- Paired synthetic coverage:
tests/test_monthly_settlement_drafts.py(month-end, active-month, unverified/reselection, cancellation and atomicity),tests/test_settlement_draft_adapters.py(facade/gateway/MCP signatures and no-create reads),tests/test_settlement_draft_dashboard.pyandtests/test_settlement_draft_panel_workflow.py(shared panel/actions),tests/test_settlement_draft_mcp_runtime.py(real stdio responses), andtests/test_settlement_draft_docs.py(this mapping). Existing authentication/menu/legacy regression tests remain unchanged.
Issue #18 ownership additions
SQLite schema.py/migrations.py own additive/idempotent schema 13→14, nullable legacy totals, FK/unique/run-index constraints. application/settlement_draft_service.py owns durable draft policy, category-pair selection, grouped computation, complete review fingerprint, unknown blocker, approval guard, six-equation verification, atomic finalize/replay/correction. monthly_settlement_service.py owns readonly stored-run lookup and live-versus-stored distinction; assets_service.py retains existing asset authority and asset-only API semantics. MCP tool contracts/server and Streamlit settlement gateway delegate to shared services; Dashboard displays shared results only after auth. No adapter duplicates policy or runs SQL. Synthetic coverage extends lifecycle, migration, adapter, rollback, drift and correction regressions; no test run is claimed here.