Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret이 주입하는 환경변수 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD에서 단일 관리자 자격 증명을 읽고, password secret을 애플리케이션에서 constant-time 비교한다. 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 실패 로그인 응답은 최소 1초 지연하되 애플리케이션 잠금은 두지 않고 우회 가능성과 배포 경계의 별도 rate limiting 필요성을 운영 문서에 명시한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
문제
현재 dashboard/app.py의 main()은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다. docker-compose.yml은 Dashboard를 호스트 40059에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고, .env.example에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.
포함 범위
Streamlit Dashboard 진입 시 적용되는 공통 관리자 인증 경계 정의
인증 전 페이지 dispatch, 금융 데이터 조회, 상태 변경 gateway 호출을 차단하는 동작 정의
인증 성공·실패·설정 누락·로그아웃에 대한 사용자 관찰 가능 동작 정의
관리자 자격 증명의 배포 설정 계약과 secret 취급 원칙 정의
기존 Dashboard 페이지 및 상태 변경 기능에 대한 인증 회귀 테스트 범위 정의
Compose 및 환경설정 예시와 운영 문서에 필요한 변경 정의
선택된 결정을 정식 requirement와 acceptance criteria로 전환하기 위한 문서 산출물 정의
제외 범위
다중 사용자 계정, 역할 기반 권한, 사용자별 데이터 분리의 즉시 구현
회원가입, 비밀번호 찾기, 관리자 UI를 통한 자격 증명 관리
외부 IdP/OIDC/SSO 도입 자체의 즉시 구현
SQLite 스키마 또는 가계부 도메인 모델 변경
MCP 서버 인증은 별도 Issue와 requirement로 분리하며 이번 구현 범위에서 제외
네트워크 경계, TLS, Ingress 인증 프록시 등 인프라 보안 전체의 대체
제약 조건
인증 검사는 dashboard/app.py에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.
단일 관리자 ID와 평문 password secret은 배포 secret이 주입하는 환경변수 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD에서만 읽고 Streamlit secrets로 fallback하지 않는다. 두 값 중 하나라도 없거나 비어 있거나 읽을 수 없으면 fail-closed하며, password는 애플리케이션에서 constant-time 비교해야 한다.
비밀번호·token·hash 등 credential material을 로그, 오류 메시지, URL/query parameter, Issue 또는 repository 문서에 평문으로 노출하지 않아야 한다.
인증 상태는 현재 Streamlit browser session에서만 유지하며 명시적 로그아웃 시 폐기하고 새 session에는 재인증을 요구해야 한다.
잘못된 관리자 ID 또는 password로 실패한 각 로그인 응답은 제출부터 완료까지 최소 1초가 되도록 지연한다. 애플리케이션 수준 잠금은 두지 않으며, 새 session·병렬 요청을 통한 우회 가능성과 배포 경계에서 별도 rate limiting이 필요함을 운영 문서에 명시해야 한다.
Dashboard 인증만 구현하며 MCP endpoint가 보호된다고 간주해서는 안 된다. MCP 인증은 별도 Issue와 requirement에서 다룬다.
현재 배포는 동일 이미지의 MCP·Dashboard 컨테이너와 공유 PVC, 단일 replica를 사용한다. 인증 변경은 이 저장 구조를 불필요하게 바꾸지 않아야 한다.
테스트는 실제 운영 금융 DB나 실제 credential을 사용하지 않고 synthetic fixture와 test-only secret을 사용해야 한다.
확정된 결정을 gitea-agent trusted requirements bundle과 gitea-agent.requirement/v1 계약에 따라 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.
확정된 결정
genichin/jarca-ledger-mcp Issue #1은 관리자 인증 기능 추가를 요구한다.
인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃을 제공하며 새 session에는 재인증을 요구한다.
잘못된 관리자 ID 또는 password의 각 실패 응답은 최소 1초 지연하고 애플리케이션 잠금은 두지 않으며, 우회 가능성과 배포 경계의 별도 rate limiting 필요성을 운영 문서에 명시한다.
이번 범위는 Streamlit Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
저장소 근거
dashboard/app.py: main()이 get_db_path(), UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.
dashboard/common.py: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은 transactions_gateway.update_ledger_transaction()을 호출하고 선택적으로 분류 규칙도 저장한다.
dashboard/page_views/accounts.py: 계좌 변경 form과 변경 저장 동작이 존재한다.
dashboard/page_views/imports.py: 금융 PDF upload, import 내역 조회, rollback_ledger_import_batch() 호출과 hard-delete 선택지가 존재한다.
README.md: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.
docker-compose.yml: Dashboard는 40059:8501로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다.
.env.example: 현재 예시 설정은 JARCA_LEDGER_DB_PATH만 제공한다.
pyproject.toml: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.
tests/test_dashboard_app.py: streamlit.testing.v1.AppTest와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.
위 근거는 모두 pinned SHA 069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6에서 확인했다.
변경 내용: 관리자 인증 기능을 판정 가능한 요구사항과 acceptance criteria로 정의한다.
포함할 내용:
모든 Dashboard 페이지의 인증 전 로그인 화면 전용 렌더링과 page dispatch·DB 조회·gateway 호출 차단 계약
환경변수 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD만 사용하는 credential source 계약과 Streamlit secrets fallback 금지
평문 password secret의 constant-time 비교와 credential material 비보존 계약
잘못된 관리자 ID 또는 password의 각 실패 응답을 제출부터 완료까지 최소 1초 지연하고 성공 응답에는 이 실패 지연을 적용하지 않는 계약
애플리케이션 잠금을 두지 않는 정책, 새 session·병렬 요청 우회 가능성과 배포 경계의 별도 rate limiting 필요성을 운영 문서에 명시하는 계약
단일 관리자 ID·password 입력, secret 비노출 및 설정 누락·빈 값·읽기 오류·형식 오류 시 fail-closed 계약
현재 Streamlit browser session 범위의 인증 상태, 새 session 재인증 조건과 명시적 로그아웃 동작
Dashboard만 보호하고 MCP 인증을 별도 Issue와 requirement로 분리하는 경계
인증 성공·실패·설정 오류의 사용자 관찰 가능 동작과 실패 시 상태 무변경 조건
기존 Dashboard 기능의 인증 후 호환성 및 배포 설정·secret 주입 계약
미인증·설정 오류·잘못된 credential 상태에서 get_db_path()·page dispatch·DB·gateway·upload·rollback을 호출하지 않고 query parameter로 우회할 수 없다는 acceptance criteria
정상 인증 후 기존 Dashboard 페이지 접근, 같은 browser session의 rerun에서 인증 유지, 로그아웃 직후 상태 폐기, 새 session 재인증에 대한 acceptance criteria
잘못된 관리자 ID와 잘못된 password 각각에 대해 실패 응답이 제출 후 1초 전에 완료되지 않고 동일한 일반 오류를 표시하며 DB·gateway 호출과 상태 변경이 없다는 acceptance criteria
credential 원문 또는 verifier를 화면·로그·traceback·URL/query parameter·st.session_state에 노출하지 않는 acceptance criteria
근거 Issue #1, 관련 설계 문서와 구현 PR 미정의 추적성
제외할 내용:
확정 범위 밖의 다중 사용자·역할 기반 권한·사용자별 데이터 분리
SQLite 가계부 도메인 스키마 변경과 자격 증명의 repository 평문 저장
별도 Issue와 requirement로 분리된 MCP 인증, TLS, Ingress 또는 네트워크 경계 전체의 구현
근거: pinned source revision에 할당된 요구사항 문서가 없으므로 첫 요구사항 ID는 REQ-001이다. Issue #1과 dashboard/app.py, .env.example, docker-compose.yml, tests/test_dashboard_app.py가 위 계약의 필요성을 뒷받침한다.
데이터 모델
조치: 영향 없음
경로: docs/data-model.md
변경 내용: 단일 관리자 자격 증명은 배포 secret이 주입하는 환경변수에서 읽고 영속 사용자 계정을 도입하지 않는다. 가계부 엔터티·필드·관계·불변조건과 SQLite schema 또는 migration을 변경하지 않으므로 데이터 모델 문서를 생성하지 않는다.
근거: 제외 범위가 SQLite 스키마와 가계부 도메인 모델 변경을 명시하며 pinned source revision에도 docs/data-model.md가 없다.
인터페이스 계약
조치: 신규 작성
경로: docs/interface-contract.md
변경 내용: 환경변수 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD만 사용하는 credential 입력, Streamlit secrets fallback 금지, 평문 password secret의 constant-time 비교, 인증 결과와 현재 browser session 상태 출력, 인증·로그아웃 operation, 잘못된 관리자 ID 또는 password의 실패 응답별 최소 1초 지연과 일반 오류, 애플리케이션 잠금 없음, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 별도 범위인 MCP 보호 경계를 정의한다.
근거: dashboard/app.py에는 인증 gate가 없고 .env.example과 docker-compose.yml에는 인증 입력·설정 계약이 없으며 pinned source revision에도 docs/interface-contract.md가 없다.
프로젝트 구조
조치: 신규 작성
경로: docs/project-structure.md
변경 내용: dashboard/app.py가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를 dashboard/ 내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임, tests/의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.
근거: 현재 dashboard/app.py가 DB 경로 취득과 page dispatch를 직접 시작하고 dashboard/page_views/와 tests/test_dashboard_app.py가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는 docs/project-structure.md가 없다.
런타임 흐름
조치: 신규 작성
경로: docs/runtime-flow.md
변경 내용: Streamlit session 시작, 환경변수 credential 로드와 설정 검증, 평문 password secret의 constant-time 비교, 설정 오류의 즉시 중단, 미인증 상태의 로그인 화면 전용 렌더링과 get_db_path()·DB·gateway·upload·rollback 미호출, credential 검증 성공·실패 전이, 각 실패 제출부터 일반 오류 응답 완료까지 최소 1초를 보장하는 지연, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.
근거: 현재 dashboard/app.py는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도 docs/runtime-flow.md가 없다.
기타 구현 산출물
README.md 갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이, 애플리케이션 잠금이 없고 1초 지연은 새 session·병렬 요청으로 우회될 수 있으므로 배포 경계에 별도 rate limiting이 필요하다는 운영 제약을 설명한다.
.env.example 갱신: 실제 secret 값 없이 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD의 이름과 용도를 문서화한다.
docker-compose.yml 갱신: 배포 secret의 두 환경변수를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.
Dashboard 인증 gate 및 관련 테스트: 정상 인증, 잘못된 관리자 ID, 잘못된 password, 설정 누락, logout, 인증 전 DB·gateway 미호출, 각 실패 응답의 최소 1초 지연, 일반 오류와 애플리케이션 잠금 부재를 검증한다.
운영 배포 manifest 또는 runbook은 repository 내 관리 위치가 확인되고 선택된 인증 방식에 영향이 있을 때만 갱신한다.
미해결 질문
없음
<!-- gitea-agent:discovery-draft/v2 -->
## 요약
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret이 주입하는 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`에서 단일 관리자 자격 증명을 읽고, password secret을 애플리케이션에서 constant-time 비교한다. 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 실패 로그인 응답은 최소 1초 지연하되 애플리케이션 잠금은 두지 않고 우회 가능성과 배포 경계의 별도 rate limiting 필요성을 운영 문서에 명시한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
## 문제
현재 `dashboard/app.py`의 `main()`은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다. `docker-compose.yml`은 Dashboard를 호스트 `40059`에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고, `.env.example`에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.
## 포함 범위
- Streamlit Dashboard 진입 시 적용되는 공통 관리자 인증 경계 정의
- 인증 전 페이지 dispatch, 금융 데이터 조회, 상태 변경 gateway 호출을 차단하는 동작 정의
- 인증 성공·실패·설정 누락·로그아웃에 대한 사용자 관찰 가능 동작 정의
- 관리자 자격 증명의 배포 설정 계약과 secret 취급 원칙 정의
- 기존 Dashboard 페이지 및 상태 변경 기능에 대한 인증 회귀 테스트 범위 정의
- Compose 및 환경설정 예시와 운영 문서에 필요한 변경 정의
- 선택된 결정을 정식 requirement와 acceptance criteria로 전환하기 위한 문서 산출물 정의
## 제외 범위
- 다중 사용자 계정, 역할 기반 권한, 사용자별 데이터 분리의 즉시 구현
- 회원가입, 비밀번호 찾기, 관리자 UI를 통한 자격 증명 관리
- 외부 IdP/OIDC/SSO 도입 자체의 즉시 구현
- SQLite 스키마 또는 가계부 도메인 모델 변경
- MCP 서버 인증은 별도 Issue와 requirement로 분리하며 이번 구현 범위에서 제외
- 네트워크 경계, TLS, Ingress 인증 프록시 등 인프라 보안 전체의 대체
## 제약 조건
- 인증 검사는 `dashboard/app.py`에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.
- 단일 관리자 ID와 평문 password secret은 배포 secret이 주입하는 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`에서만 읽고 Streamlit secrets로 fallback하지 않는다. 두 값 중 하나라도 없거나 비어 있거나 읽을 수 없으면 fail-closed하며, password는 애플리케이션에서 constant-time 비교해야 한다.
- 비밀번호·token·hash 등 credential material을 로그, 오류 메시지, URL/query parameter, Issue 또는 repository 문서에 평문으로 노출하지 않아야 한다.
- 인증 상태는 현재 Streamlit browser session에서만 유지하며 명시적 로그아웃 시 폐기하고 새 session에는 재인증을 요구해야 한다.
- 잘못된 관리자 ID 또는 password로 실패한 각 로그인 응답은 제출부터 완료까지 최소 1초가 되도록 지연한다. 애플리케이션 수준 잠금은 두지 않으며, 새 session·병렬 요청을 통한 우회 가능성과 배포 경계에서 별도 rate limiting이 필요함을 운영 문서에 명시해야 한다.
- Dashboard 인증만 구현하며 MCP endpoint가 보호된다고 간주해서는 안 된다. MCP 인증은 별도 Issue와 requirement에서 다룬다.
- 현재 배포는 동일 이미지의 MCP·Dashboard 컨테이너와 공유 PVC, 단일 replica를 사용한다. 인증 변경은 이 저장 구조를 불필요하게 바꾸지 않아야 한다.
- 테스트는 실제 운영 금융 DB나 실제 credential을 사용하지 않고 synthetic fixture와 test-only secret을 사용해야 한다.
- 확정된 결정을 gitea-agent trusted requirements bundle과 gitea-agent.requirement/v1 계약에 따라 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.
## 확정된 결정
- `genichin/jarca-ledger-mcp` Issue #1은 관리자 인증 기능 추가를 요구한다.
- Discovery 근거 revision은 `main`의 `069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6`으로 고정한다.
- 인증 전에는 로그인 화면만 표시하고 모든 Dashboard 페이지의 조회·쓰기·import를 차단한다.
- 배포 secret이 주입하는 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`만 credential source로 사용하며 Streamlit secrets로 fallback하지 않는다.
- `JARCA_DASHBOARD_ADMIN_PASSWORD`에는 평문 password secret을 주입하고 애플리케이션에서 constant-time 비교한다.
- 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃을 제공하며 새 session에는 재인증을 요구한다.
- 잘못된 관리자 ID 또는 password의 각 실패 응답은 최소 1초 지연하고 애플리케이션 잠금은 두지 않으며, 우회 가능성과 배포 경계의 별도 rate limiting 필요성을 운영 문서에 명시한다.
- 이번 범위는 Streamlit Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
## 저장소 근거
- `dashboard/app.py`: `main()`이 `get_db_path()`, UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.
- `dashboard/common.py`: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은 `transactions_gateway.update_ledger_transaction()`을 호출하고 선택적으로 분류 규칙도 저장한다.
- `dashboard/page_views/accounts.py`: 계좌 변경 form과 `변경 저장` 동작이 존재한다.
- `dashboard/page_views/imports.py`: 금융 PDF upload, import 내역 조회, `rollback_ledger_import_batch()` 호출과 hard-delete 선택지가 존재한다.
- `README.md`: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.
- `docker-compose.yml`: Dashboard는 `40059:8501`로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다.
- `.env.example`: 현재 예시 설정은 `JARCA_LEDGER_DB_PATH`만 제공한다.
- `pyproject.toml`: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.
- `tests/test_dashboard_app.py`: `streamlit.testing.v1.AppTest`와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.
- 위 근거는 모두 pinned SHA `069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6`에서 확인했다.
## 문서 영향 및 산출물
### 요구사항명세서
- 조치: 신규 작성
- 문서 ID: `REQ-001`
- 경로: `docs/requirements/req-001_dashboard-administrator-authentication.md`
- 제목: Dashboard 관리자 인증
- 변경 내용: 관리자 인증 기능을 판정 가능한 요구사항과 acceptance criteria로 정의한다.
- 포함할 내용:
- 모든 Dashboard 페이지의 인증 전 로그인 화면 전용 렌더링과 page dispatch·DB 조회·gateway 호출 차단 계약
- 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`만 사용하는 credential source 계약과 Streamlit secrets fallback 금지
- 평문 password secret의 constant-time 비교와 credential material 비보존 계약
- 잘못된 관리자 ID 또는 password의 각 실패 응답을 제출부터 완료까지 최소 1초 지연하고 성공 응답에는 이 실패 지연을 적용하지 않는 계약
- 애플리케이션 잠금을 두지 않는 정책, 새 session·병렬 요청 우회 가능성과 배포 경계의 별도 rate limiting 필요성을 운영 문서에 명시하는 계약
- 단일 관리자 ID·password 입력, secret 비노출 및 설정 누락·빈 값·읽기 오류·형식 오류 시 fail-closed 계약
- 현재 Streamlit browser session 범위의 인증 상태, 새 session 재인증 조건과 명시적 로그아웃 동작
- Dashboard만 보호하고 MCP 인증을 별도 Issue와 requirement로 분리하는 경계
- 인증 성공·실패·설정 오류의 사용자 관찰 가능 동작과 실패 시 상태 무변경 조건
- 기존 Dashboard 기능의 인증 후 호환성 및 배포 설정·secret 주입 계약
- 미인증·설정 오류·잘못된 credential 상태에서 `get_db_path()`·page dispatch·DB·gateway·upload·rollback을 호출하지 않고 query parameter로 우회할 수 없다는 acceptance criteria
- 정상 인증 후 기존 Dashboard 페이지 접근, 같은 browser session의 rerun에서 인증 유지, 로그아웃 직후 상태 폐기, 새 session 재인증에 대한 acceptance criteria
- 잘못된 관리자 ID와 잘못된 password 각각에 대해 실패 응답이 제출 후 1초 전에 완료되지 않고 동일한 일반 오류를 표시하며 DB·gateway 호출과 상태 변경이 없다는 acceptance criteria
- credential 원문 또는 verifier를 화면·로그·traceback·URL/query parameter·`st.session_state`에 노출하지 않는 acceptance criteria
- 근거 Issue `#1`, 관련 설계 문서와 구현 PR `미정`의 추적성
- 제외할 내용:
- 확정 범위 밖의 다중 사용자·역할 기반 권한·사용자별 데이터 분리
- SQLite 가계부 도메인 스키마 변경과 자격 증명의 repository 평문 저장
- 별도 Issue와 requirement로 분리된 MCP 인증, TLS, Ingress 또는 네트워크 경계 전체의 구현
- 근거: pinned source revision에 할당된 요구사항 문서가 없으므로 첫 요구사항 ID는 `REQ-001`이다. Issue #1과 `dashboard/app.py`, `.env.example`, `docker-compose.yml`, `tests/test_dashboard_app.py`가 위 계약의 필요성을 뒷받침한다.
### 데이터 모델
- 조치: 영향 없음
- 경로: `docs/data-model.md`
- 변경 내용: 단일 관리자 자격 증명은 배포 secret이 주입하는 환경변수에서 읽고 영속 사용자 계정을 도입하지 않는다. 가계부 엔터티·필드·관계·불변조건과 SQLite schema 또는 migration을 변경하지 않으므로 데이터 모델 문서를 생성하지 않는다.
- 근거: 제외 범위가 SQLite 스키마와 가계부 도메인 모델 변경을 명시하며 pinned source revision에도 `docs/data-model.md`가 없다.
### 인터페이스 계약
- 조치: 신규 작성
- 경로: `docs/interface-contract.md`
- 변경 내용: 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`만 사용하는 credential 입력, Streamlit secrets fallback 금지, 평문 password secret의 constant-time 비교, 인증 결과와 현재 browser session 상태 출력, 인증·로그아웃 operation, 잘못된 관리자 ID 또는 password의 실패 응답별 최소 1초 지연과 일반 오류, 애플리케이션 잠금 없음, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 별도 범위인 MCP 보호 경계를 정의한다.
- 근거: `dashboard/app.py`에는 인증 gate가 없고 `.env.example`과 `docker-compose.yml`에는 인증 입력·설정 계약이 없으며 pinned source revision에도 `docs/interface-contract.md`가 없다.
### 프로젝트 구조
- 조치: 신규 작성
- 경로: `docs/project-structure.md`
- 변경 내용: `dashboard/app.py`가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를 `dashboard/` 내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임, `tests/`의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.
- 근거: 현재 `dashboard/app.py`가 DB 경로 취득과 page dispatch를 직접 시작하고 `dashboard/page_views/`와 `tests/test_dashboard_app.py`가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는 `docs/project-structure.md`가 없다.
### 런타임 흐름
- 조치: 신규 작성
- 경로: `docs/runtime-flow.md`
- 변경 내용: Streamlit session 시작, 환경변수 credential 로드와 설정 검증, 평문 password secret의 constant-time 비교, 설정 오류의 즉시 중단, 미인증 상태의 로그인 화면 전용 렌더링과 `get_db_path()`·DB·gateway·upload·rollback 미호출, credential 검증 성공·실패 전이, 각 실패 제출부터 일반 오류 응답 완료까지 최소 1초를 보장하는 지연, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.
- 근거: 현재 `dashboard/app.py`는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도 `docs/runtime-flow.md`가 없다.
### 기타 구현 산출물
- `README.md` 갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이, 애플리케이션 잠금이 없고 1초 지연은 새 session·병렬 요청으로 우회될 수 있으므로 배포 경계에 별도 rate limiting이 필요하다는 운영 제약을 설명한다.
- `.env.example` 갱신: 실제 secret 값 없이 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`의 이름과 용도를 문서화한다.
- `docker-compose.yml` 갱신: 배포 secret의 두 환경변수를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.
- Dashboard 인증 gate 및 관련 테스트: 정상 인증, 잘못된 관리자 ID, 잘못된 password, 설정 누락, logout, 인증 전 DB·gateway 미호출, 각 실패 응답의 최소 1초 지연, 일반 오류와 애플리케이션 잠금 부재를 검증한다.
- 운영 배포 manifest 또는 runbook은 repository 내 관리 위치가 확인되고 선택된 인증 방식에 영향이 있을 때만 갱신한다.
## 미해결 질문
없음
gitea-spec-agent
was assigned by genichin2026-09-09 14:36:52 +00:00
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 현재 Dashboard는 인증 절차 없이 SQLite 데이터를 직접 읽고 쓰며 호스트 포트 40059로 노출될 수 있으므로, 화면 렌더링과 DB 접근보다 앞선 공통 인증 경계가 필요하다. 구체적인 보호 범위, 자격 증명 방식, 세션 정책과 MCP 적용 여부는 아래 미해결 질문에서 결정한다.
문제
현재 dashboard/app.py의 main()은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다. docker-compose.yml은 Dashboard를 호스트 40059에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고, .env.example에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.
포함 범위
Streamlit Dashboard 진입 시 적용되는 공통 관리자 인증 경계 정의
인증 전 페이지 dispatch, 금융 데이터 조회, 상태 변경 gateway 호출을 차단하는 동작 정의
인증 성공·실패·설정 누락·로그아웃에 대한 사용자 관찰 가능 동작 정의
관리자 자격 증명의 배포 설정 계약과 secret 취급 원칙 정의
기존 Dashboard 페이지 및 상태 변경 기능에 대한 인증 회귀 테스트 범위 정의
Compose 및 환경설정 예시와 운영 문서에 필요한 변경 정의
선택된 결정을 정식 requirement와 acceptance criteria로 전환하기 위한 문서 산출물 정의
제외 범위
다중 사용자 계정, 역할 기반 권한, 사용자별 데이터 분리의 즉시 구현
회원가입, 비밀번호 찾기, 관리자 UI를 통한 자격 증명 관리
외부 IdP/OIDC/SSO 도입 자체의 즉시 구현
SQLite 스키마 또는 가계부 도메인 모델 변경
MCP 서버의 인증 적용 여부는 OQ-004 결정 전까지 이 초안의 확정 범위에서 제외
네트워크 경계, TLS, Ingress 인증 프록시 등 인프라 보안 전체의 대체
제약 조건
인증 검사는 dashboard/app.py에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행되어야 한다.
인증 설정이 없거나 읽을 수 없거나 유효하지 않으면 보호 기능을 우회하지 않고 fail-closed해야 한다.
비밀번호·token·hash 등 credential material을 로그, 오류 메시지, URL/query parameter, Issue 또는 repository 문서에 평문으로 노출하지 않아야 한다.
기존 Dashboard는 MCP를 경유하지 않고 MCP와 같은 SQLite 파일을 직접 사용하므로, Dashboard 인증만으로 MCP endpoint가 보호된다고 간주해서는 안 된다.
현재 배포는 동일 이미지의 MCP·Dashboard 컨테이너와 공유 PVC, 단일 replica를 사용한다. 인증 변경은 이 저장 구조를 불필요하게 바꾸지 않아야 한다.
테스트는 실제 운영 금융 DB나 실제 credential을 사용하지 않고 synthetic fixture와 test-only secret을 사용해야 한다.
구현 전에 아래 OQ의 선택을 확정하고 docs/requirements/TEMPLATE.md 형식의 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.
확정된 결정
genichin/jarca-ledger-mcp Issue #1은 관리자 인증 기능 추가를 요구한다.
보호 대상 애플리케이션에는 최소한 Streamlit Dashboard가 포함된다. 구체적인 보호 범위와 방식은 아직 확정되지 않았다.
저장소 근거
dashboard/app.py: main()이 get_db_path(), UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.
dashboard/common.py: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은 transactions_gateway.update_ledger_transaction()을 호출하고 선택적으로 분류 규칙도 저장한다.
dashboard/page_views/accounts.py: 계좌 변경 form과 변경 저장 동작이 존재한다.
dashboard/page_views/imports.py: 금융 PDF upload, import 내역 조회, rollback_ledger_import_batch() 호출과 hard-delete 선택지가 존재한다.
README.md: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.
docker-compose.yml: Dashboard는 40059:8501로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다.
.env.example: 현재 예시 설정은 JARCA_LEDGER_DB_PATH만 제공한다.
pyproject.toml: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.
tests/test_dashboard_app.py: streamlit.testing.v1.AppTest와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.
docs/requirements/README.md 및 docs/requirements/TEMPLATE.md: requirement metadata, REQ-NNN.xx 식별자, acceptance criterion과 verification/evidence 구조를 정의한다.
위 근거는 모두 pinned SHA fc9ada51d6ee67fbe7a4144361608c1096227720에서 확인했다.
변경 내용: 관리자 인증 기능을 판정 가능한 요구사항과 acceptance criteria로 정의한다.
포함할 내용:
OQ-001에서 확정된 Dashboard 보호 범위와 인증 전 page dispatch·DB 조회·gateway 호출 차단 계약
OQ-002에서 확정된 자격 증명 source, 입력·검증 방식, secret 비노출 및 설정 누락 시 fail-closed 계약
OQ-003에서 확정된 세션 유지 범위, 재인증 조건과 로그아웃 동작
OQ-004에서 확정된 Dashboard와 MCP 인증 경계
인증 성공·실패·설정 오류의 사용자 관찰 가능 동작과 실패 시 상태 무변경 조건
기존 Dashboard 기능의 인증 후 호환성 및 배포 설정·secret 주입 계약
정상 인증, 잘못된 credential, 설정 누락, 로그아웃, 인증 전 DB·gateway 미호출에 대한 acceptance criteria
근거 Issue #1, 관련 설계 문서와 구현 PR 미정의 추적성
제외할 내용:
확정 범위 밖의 다중 사용자·역할 기반 권한·사용자별 데이터 분리
SQLite 가계부 도메인 스키마 변경과 자격 증명의 repository 평문 저장
별도 범위로 결정된 MCP 인증, TLS, Ingress 또는 네트워크 경계 전체의 구현
근거: pinned source revision의 docs/requirements/에는 README와 TEMPLATE만 있어 할당된 요구사항 문서가 없으므로 첫 ID는 REQ-001이다. Issue #1과 dashboard/app.py, .env.example, docker-compose.yml, tests/test_dashboard_app.py가 위 계약의 필요성을 뒷받침한다.
데이터 모델
조치: 영향 없음
경로: docs/data-model.md
변경 내용: 현재 확정 범위에서는 가계부 엔터티·필드·관계·불변조건과 SQLite schema 또는 migration을 변경하지 않으므로 데이터 모델 문서를 생성하지 않는다. OQ-002 선택으로 영속 사용자 계정 모델이 필요해지면 구현 전에 Discovery 범위와 이 판정을 갱신해야 한다.
근거: 제외 범위가 SQLite 스키마와 가계부 도메인 모델 변경을 명시하며 pinned source revision에도 docs/data-model.md가 없다.
인터페이스 계약
조치: 신규 작성
경로: docs/interface-contract.md
변경 내용: OQ-002에서 확정된 관리자 자격 증명 또는 신뢰 header 입력, 설정·secret source, 인증 결과와 세션 상태 출력, 인증·로그아웃 operation, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 OQ-004의 MCP 보호 경계를 정의한다.
근거: dashboard/app.py에는 인증 gate가 없고 .env.example과 docker-compose.yml에는 인증 입력·설정 계약이 없으며 pinned source revision에도 docs/interface-contract.md가 없다.
프로젝트 구조
조치: 신규 작성
경로: docs/project-structure.md
변경 내용: dashboard/app.py가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를 dashboard/ 내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임, tests/의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.
근거: 현재 dashboard/app.py가 DB 경로 취득과 page dispatch를 직접 시작하고 dashboard/page_views/와 tests/test_dashboard_app.py가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는 docs/project-structure.md가 없다.
런타임 흐름
조치: 신규 작성
경로: docs/runtime-flow.md
변경 내용: Streamlit session 시작과 인증 설정 로드, 설정 오류의 즉시 중단, 미인증 상태의 로그인 화면 전용 렌더링과 DB·gateway 미호출, credential 검증 성공·실패 전이, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.
근거: 현재 dashboard/app.py는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도 docs/runtime-flow.md가 없다.
기타 구현 산출물
README.md 갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다.
.env.example 갱신: 실제 secret 값 없이 선택된 인증 설정의 변수 이름과 용도를 문서화한다.
docker-compose.yml 갱신: 선택된 credential source를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.
Dashboard 인증 gate 및 관련 테스트: 정상 인증, 잘못된 credential, 설정 누락, logout, 인증 전 DB·gateway 미호출을 검증한다.
운영 배포 manifest 또는 runbook은 repository 내 관리 위치가 확인되고 선택된 인증 방식에 영향이 있을 때만 갱신한다.
미해결 질문
OQ-001 — Dashboard의 보호 범위를 어디까지 적용할 것인가?
Dashboard에는 금융 데이터 조회 화면과 거래·계좌 변경, import rollback 같은 쓰기 기능이 함께 있다. 조회만 허용하면 금융 정보가 노출되고, 쓰기 작업만 보호하면 공통 진입점이 아닌 페이지별 분기가 필요하다.
인증 전에는 로그인 화면만 표시하고 모든 Dashboard 페이지의 조회·쓰기·import를 차단한다. (추천)
조회 화면은 익명으로 허용하고 상태 변경·import 기능만 인증한다.
보호 대상 페이지를 별도 목록으로 지정한다.
OQ-002 — 관리자 자격 증명을 어떤 방식으로 제공할 것인가?
현재 저장소에는 사용자 모델이나 인증 dependency가 없으며 Issue는 단수 관리자 인증을 요청한다. 최소 변경은 배포 secret에서 단일 관리자 자격 증명을 읽는 방식이지만, 외부 IdP 또는 reverse proxy가 운영 표준이라면 경계를 애플리케이션 밖에 둘 수 있다.
배포 secret 또는 Streamlit secrets에서 단일 관리자 ID와 검증용 password secret을 주입하고 애플리케이션에서 검증한다. (추천)
Ingress/reverse proxy의 인증을 정본으로 삼고 Dashboard는 신뢰 가능한 인증 header를 검증한다.
OIDC/SSO provider와 연동한다.
다중 사용자 계정 저장소와 역할 모델을 도입한다.
OQ-003 — 인증 세션과 로그아웃 정책은 어떻게 할 것인가?
Streamlit rerun 동안 인증 상태를 유지할 범위와 세션 종료 조건이 정해져야 테스트 가능한 계약을 만들 수 있다. 브라우저를 넘는 장기 세션은 cookie 보호·만료·폐기 설계가 추가로 필요하다.
현재 Streamlit browser session에서만 인증 상태를 유지하고 명시적 로그아웃을 제공하며, 새 session에는 재인증을 요구한다. (추천)
유휴 timeout을 적용한 서버 측 세션을 도입한다.
보호된 persistent cookie로 브라우저 재시작 후에도 로그인을 유지한다.
OQ-004 — MCP endpoint도 이번 요구사항에서 인증할 것인가?
Dashboard와 MCP는 같은 SQLite 파일을 공유하지만 별도 서비스와 포트로 동작한다. Dashboard의 로그인 gate는 MCP 8000 endpoint를 보호하지 않는다.
이번 범위는 Streamlit Dashboard로 한정하고 MCP 인증은 별도 Issue/requirement로 분리한다. (추천)
Dashboard와 MCP에 동일한 관리자 credential 정책을 함께 적용한다.
MCP는 네트워크 경계로만 제한하되 그 전제와 검증 책임을 이번 requirement에 포함한다.
<!-- gitea-agent:discovery-review-origin/v1 owner=genichin repository=jarca-ledger-mcp issue=1 body-sha256=bab39e5869518bd47162ba4efa5732a8bf82ffb0c5692bec2755e3b137791ab7 -->
## Discovery 검토 전 사용자 수정본
<!-- gitea-agent:discovery-draft/v2 -->
## 요약
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 현재 Dashboard는 인증 절차 없이 SQLite 데이터를 직접 읽고 쓰며 호스트 포트 `40059`로 노출될 수 있으므로, 화면 렌더링과 DB 접근보다 앞선 공통 인증 경계가 필요하다. 구체적인 보호 범위, 자격 증명 방식, 세션 정책과 MCP 적용 여부는 아래 미해결 질문에서 결정한다.
## 문제
현재 `dashboard/app.py`의 `main()`은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다. `docker-compose.yml`은 Dashboard를 호스트 `40059`에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고, `.env.example`에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.
## 포함 범위
- Streamlit Dashboard 진입 시 적용되는 공통 관리자 인증 경계 정의
- 인증 전 페이지 dispatch, 금융 데이터 조회, 상태 변경 gateway 호출을 차단하는 동작 정의
- 인증 성공·실패·설정 누락·로그아웃에 대한 사용자 관찰 가능 동작 정의
- 관리자 자격 증명의 배포 설정 계약과 secret 취급 원칙 정의
- 기존 Dashboard 페이지 및 상태 변경 기능에 대한 인증 회귀 테스트 범위 정의
- Compose 및 환경설정 예시와 운영 문서에 필요한 변경 정의
- 선택된 결정을 정식 requirement와 acceptance criteria로 전환하기 위한 문서 산출물 정의
## 제외 범위
- 다중 사용자 계정, 역할 기반 권한, 사용자별 데이터 분리의 즉시 구현
- 회원가입, 비밀번호 찾기, 관리자 UI를 통한 자격 증명 관리
- 외부 IdP/OIDC/SSO 도입 자체의 즉시 구현
- SQLite 스키마 또는 가계부 도메인 모델 변경
- MCP 서버의 인증 적용 여부는 OQ-004 결정 전까지 이 초안의 확정 범위에서 제외
- 네트워크 경계, TLS, Ingress 인증 프록시 등 인프라 보안 전체의 대체
## 제약 조건
- 인증 검사는 `dashboard/app.py`에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행되어야 한다.
- 인증 설정이 없거나 읽을 수 없거나 유효하지 않으면 보호 기능을 우회하지 않고 fail-closed해야 한다.
- 비밀번호·token·hash 등 credential material을 로그, 오류 메시지, URL/query parameter, Issue 또는 repository 문서에 평문으로 노출하지 않아야 한다.
- 기존 Dashboard는 MCP를 경유하지 않고 MCP와 같은 SQLite 파일을 직접 사용하므로, Dashboard 인증만으로 MCP endpoint가 보호된다고 간주해서는 안 된다.
- 현재 배포는 동일 이미지의 MCP·Dashboard 컨테이너와 공유 PVC, 단일 replica를 사용한다. 인증 변경은 이 저장 구조를 불필요하게 바꾸지 않아야 한다.
- 테스트는 실제 운영 금융 DB나 실제 credential을 사용하지 않고 synthetic fixture와 test-only secret을 사용해야 한다.
- 구현 전에 아래 OQ의 선택을 확정하고 `docs/requirements/TEMPLATE.md` 형식의 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.
## 확정된 결정
- `genichin/jarca-ledger-mcp` Issue #1은 관리자 인증 기능 추가를 요구한다.
- Discovery 근거 revision은 `main`의 `fc9ada51d6ee67fbe7a4144361608c1096227720`으로 고정한다.
- 보호 대상 애플리케이션에는 최소한 Streamlit Dashboard가 포함된다. 구체적인 보호 범위와 방식은 아직 확정되지 않았다.
## 저장소 근거
- `dashboard/app.py`: `main()`이 `get_db_path()`, UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.
- `dashboard/common.py`: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은 `transactions_gateway.update_ledger_transaction()`을 호출하고 선택적으로 분류 규칙도 저장한다.
- `dashboard/page_views/accounts.py`: 계좌 변경 form과 `변경 저장` 동작이 존재한다.
- `dashboard/page_views/imports.py`: 금융 PDF upload, import 내역 조회, `rollback_ledger_import_batch()` 호출과 hard-delete 선택지가 존재한다.
- `README.md`: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.
- `docker-compose.yml`: Dashboard는 `40059:8501`로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다.
- `.env.example`: 현재 예시 설정은 `JARCA_LEDGER_DB_PATH`만 제공한다.
- `pyproject.toml`: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.
- `tests/test_dashboard_app.py`: `streamlit.testing.v1.AppTest`와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.
- `docs/requirements/README.md` 및 `docs/requirements/TEMPLATE.md`: requirement metadata, `REQ-NNN.xx` 식별자, acceptance criterion과 verification/evidence 구조를 정의한다.
- 위 근거는 모두 pinned SHA `fc9ada51d6ee67fbe7a4144361608c1096227720`에서 확인했다.
## 문서 영향 및 산출물
### 요구사항명세서
- 조치: 신규 작성
- 문서 ID: `REQ-001`
- 경로: `docs/requirements/req-001_dashboard-administrator-authentication.md`
- 제목: Dashboard 관리자 인증
- 변경 내용: 관리자 인증 기능을 판정 가능한 요구사항과 acceptance criteria로 정의한다.
- 포함할 내용:
- OQ-001에서 확정된 Dashboard 보호 범위와 인증 전 page dispatch·DB 조회·gateway 호출 차단 계약
- OQ-002에서 확정된 자격 증명 source, 입력·검증 방식, secret 비노출 및 설정 누락 시 fail-closed 계약
- OQ-003에서 확정된 세션 유지 범위, 재인증 조건과 로그아웃 동작
- OQ-004에서 확정된 Dashboard와 MCP 인증 경계
- 인증 성공·실패·설정 오류의 사용자 관찰 가능 동작과 실패 시 상태 무변경 조건
- 기존 Dashboard 기능의 인증 후 호환성 및 배포 설정·secret 주입 계약
- 정상 인증, 잘못된 credential, 설정 누락, 로그아웃, 인증 전 DB·gateway 미호출에 대한 acceptance criteria
- 근거 Issue `#1`, 관련 설계 문서와 구현 PR `미정`의 추적성
- 제외할 내용:
- 확정 범위 밖의 다중 사용자·역할 기반 권한·사용자별 데이터 분리
- SQLite 가계부 도메인 스키마 변경과 자격 증명의 repository 평문 저장
- 별도 범위로 결정된 MCP 인증, TLS, Ingress 또는 네트워크 경계 전체의 구현
- 근거: pinned source revision의 `docs/requirements/`에는 README와 TEMPLATE만 있어 할당된 요구사항 문서가 없으므로 첫 ID는 `REQ-001`이다. Issue #1과 `dashboard/app.py`, `.env.example`, `docker-compose.yml`, `tests/test_dashboard_app.py`가 위 계약의 필요성을 뒷받침한다.
### 데이터 모델
- 조치: 영향 없음
- 경로: `docs/data-model.md`
- 변경 내용: 현재 확정 범위에서는 가계부 엔터티·필드·관계·불변조건과 SQLite schema 또는 migration을 변경하지 않으므로 데이터 모델 문서를 생성하지 않는다. OQ-002 선택으로 영속 사용자 계정 모델이 필요해지면 구현 전에 Discovery 범위와 이 판정을 갱신해야 한다.
- 근거: 제외 범위가 SQLite 스키마와 가계부 도메인 모델 변경을 명시하며 pinned source revision에도 `docs/data-model.md`가 없다.
### 인터페이스 계약
- 조치: 신규 작성
- 경로: `docs/interface-contract.md`
- 변경 내용: OQ-002에서 확정된 관리자 자격 증명 또는 신뢰 header 입력, 설정·secret source, 인증 결과와 세션 상태 출력, 인증·로그아웃 operation, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 OQ-004의 MCP 보호 경계를 정의한다.
- 근거: `dashboard/app.py`에는 인증 gate가 없고 `.env.example`과 `docker-compose.yml`에는 인증 입력·설정 계약이 없으며 pinned source revision에도 `docs/interface-contract.md`가 없다.
### 프로젝트 구조
- 조치: 신규 작성
- 경로: `docs/project-structure.md`
- 변경 내용: `dashboard/app.py`가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를 `dashboard/` 내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임, `tests/`의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.
- 근거: 현재 `dashboard/app.py`가 DB 경로 취득과 page dispatch를 직접 시작하고 `dashboard/page_views/`와 `tests/test_dashboard_app.py`가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는 `docs/project-structure.md`가 없다.
### 런타임 흐름
- 조치: 신규 작성
- 경로: `docs/runtime-flow.md`
- 변경 내용: Streamlit session 시작과 인증 설정 로드, 설정 오류의 즉시 중단, 미인증 상태의 로그인 화면 전용 렌더링과 DB·gateway 미호출, credential 검증 성공·실패 전이, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.
- 근거: 현재 `dashboard/app.py`는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도 `docs/runtime-flow.md`가 없다.
### 기타 구현 산출물
- `README.md` 갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다.
- `.env.example` 갱신: 실제 secret 값 없이 선택된 인증 설정의 변수 이름과 용도를 문서화한다.
- `docker-compose.yml` 갱신: 선택된 credential source를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.
- Dashboard 인증 gate 및 관련 테스트: 정상 인증, 잘못된 credential, 설정 누락, logout, 인증 전 DB·gateway 미호출을 검증한다.
- 운영 배포 manifest 또는 runbook은 repository 내 관리 위치가 확인되고 선택된 인증 방식에 영향이 있을 때만 갱신한다.
## 미해결 질문
### OQ-001 — Dashboard의 보호 범위를 어디까지 적용할 것인가?
Dashboard에는 금융 데이터 조회 화면과 거래·계좌 변경, import rollback 같은 쓰기 기능이 함께 있다. 조회만 허용하면 금융 정보가 노출되고, 쓰기 작업만 보호하면 공통 진입점이 아닌 페이지별 분기가 필요하다.
- [x] 인증 전에는 로그인 화면만 표시하고 모든 Dashboard 페이지의 조회·쓰기·import를 차단한다. (추천)
- [ ] 조회 화면은 익명으로 허용하고 상태 변경·import 기능만 인증한다.
- [ ] 보호 대상 페이지를 별도 목록으로 지정한다.
### OQ-002 — 관리자 자격 증명을 어떤 방식으로 제공할 것인가?
현재 저장소에는 사용자 모델이나 인증 dependency가 없으며 Issue는 단수 관리자 인증을 요청한다. 최소 변경은 배포 secret에서 단일 관리자 자격 증명을 읽는 방식이지만, 외부 IdP 또는 reverse proxy가 운영 표준이라면 경계를 애플리케이션 밖에 둘 수 있다.
- [x] 배포 secret 또는 Streamlit secrets에서 단일 관리자 ID와 검증용 password secret을 주입하고 애플리케이션에서 검증한다. (추천)
- [ ] Ingress/reverse proxy의 인증을 정본으로 삼고 Dashboard는 신뢰 가능한 인증 header를 검증한다.
- [ ] OIDC/SSO provider와 연동한다.
- [ ] 다중 사용자 계정 저장소와 역할 모델을 도입한다.
### OQ-003 — 인증 세션과 로그아웃 정책은 어떻게 할 것인가?
Streamlit rerun 동안 인증 상태를 유지할 범위와 세션 종료 조건이 정해져야 테스트 가능한 계약을 만들 수 있다. 브라우저를 넘는 장기 세션은 cookie 보호·만료·폐기 설계가 추가로 필요하다.
- [x] 현재 Streamlit browser session에서만 인증 상태를 유지하고 명시적 로그아웃을 제공하며, 새 session에는 재인증을 요구한다. (추천)
- [ ] 유휴 timeout을 적용한 서버 측 세션을 도입한다.
- [ ] 보호된 persistent cookie로 브라우저 재시작 후에도 로그인을 유지한다.
### OQ-004 — MCP endpoint도 이번 요구사항에서 인증할 것인가?
Dashboard와 MCP는 같은 SQLite 파일을 공유하지만 별도 서비스와 포트로 동작한다. Dashboard의 로그인 gate는 MCP `8000` endpoint를 보호하지 않는다.
- [x] 이번 범위는 Streamlit Dashboard로 한정하고 MCP 인증은 별도 Issue/requirement로 분리한다. (추천)
- [ ] Dashboard와 MCP에 동일한 관리자 credential 정책을 함께 적용한다.
- [ ] MCP는 네트워크 경계로만 제한하되 그 전제와 검증 책임을 이번 requirement에 포함한다.
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret 또는 Streamlit secrets에서 단일 관리자 ID와 검증용 password secret을 주입해 애플리케이션에서 검증하며, 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
문제
현재 dashboard/app.py의 main()은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다. docker-compose.yml은 Dashboard를 호스트 40059에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고, .env.example에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.
포함 범위
Streamlit Dashboard 진입 시 적용되는 공통 관리자 인증 경계 정의
인증 전 페이지 dispatch, 금융 데이터 조회, 상태 변경 gateway 호출을 차단하는 동작 정의
인증 성공·실패·설정 누락·로그아웃에 대한 사용자 관찰 가능 동작 정의
관리자 자격 증명의 배포 설정 계약과 secret 취급 원칙 정의
기존 Dashboard 페이지 및 상태 변경 기능에 대한 인증 회귀 테스트 범위 정의
Compose 및 환경설정 예시와 운영 문서에 필요한 변경 정의
선택된 결정을 정식 requirement와 acceptance criteria로 전환하기 위한 문서 산출물 정의
제외 범위
다중 사용자 계정, 역할 기반 권한, 사용자별 데이터 분리의 즉시 구현
회원가입, 비밀번호 찾기, 관리자 UI를 통한 자격 증명 관리
외부 IdP/OIDC/SSO 도입 자체의 즉시 구현
SQLite 스키마 또는 가계부 도메인 모델 변경
MCP 서버 인증은 별도 Issue와 requirement로 분리하며 이번 구현 범위에서 제외
네트워크 경계, TLS, Ingress 인증 프록시 등 인프라 보안 전체의 대체
제약 조건
인증 검사는 dashboard/app.py에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.
단일 관리자 ID와 검증용 password secret은 배포 secret 또는 Streamlit secrets로 주입하며, 설정이 없거나 읽을 수 없거나 유효하지 않으면 보호 기능을 우회하지 않고 fail-closed해야 한다.
비밀번호·token·hash 등 credential material을 로그, 오류 메시지, URL/query parameter, Issue 또는 repository 문서에 평문으로 노출하지 않아야 한다.
인증 상태는 현재 Streamlit browser session에서만 유지하며 명시적 로그아웃 시 폐기하고 새 session에는 재인증을 요구해야 한다.
Dashboard 인증만 구현하며 MCP endpoint가 보호된다고 간주해서는 안 된다. MCP 인증은 별도 Issue와 requirement에서 다룬다.
현재 배포는 동일 이미지의 MCP·Dashboard 컨테이너와 공유 PVC, 단일 replica를 사용한다. 인증 변경은 이 저장 구조를 불필요하게 바꾸지 않아야 한다.
테스트는 실제 운영 금융 DB나 실제 credential을 사용하지 않고 synthetic fixture와 test-only secret을 사용해야 한다.
확정된 결정을 gitea-agent trusted requirements bundle과 gitea-agent.requirement/v1 계약에 따라 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.
확정된 결정
genichin/jarca-ledger-mcp Issue #1은 관리자 인증 기능 추가를 요구한다.
인증 전에는 로그인 화면만 표시하고 모든 Dashboard 페이지의 조회·쓰기·import를 차단한다.
배포 secret 또는 Streamlit secrets에서 단일 관리자 ID와 검증용 password secret을 주입하고 애플리케이션에서 검증한다.
인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃을 제공하며 새 session에는 재인증을 요구한다.
이번 범위는 Streamlit Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
저장소 근거
dashboard/app.py: main()이 get_db_path(), UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.
dashboard/common.py: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은 transactions_gateway.update_ledger_transaction()을 호출하고 선택적으로 분류 규칙도 저장한다.
dashboard/page_views/accounts.py: 계좌 변경 form과 변경 저장 동작이 존재한다.
dashboard/page_views/imports.py: 금융 PDF upload, import 내역 조회, rollback_ledger_import_batch() 호출과 hard-delete 선택지가 존재한다.
README.md: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.
docker-compose.yml: Dashboard는 40059:8501로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다.
.env.example: 현재 예시 설정은 JARCA_LEDGER_DB_PATH만 제공한다.
pyproject.toml: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.
tests/test_dashboard_app.py: streamlit.testing.v1.AppTest와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.
위 근거는 모두 pinned SHA 069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6에서 확인했다.
변경 내용: 관리자 인증 기능을 판정 가능한 요구사항과 acceptance criteria로 정의한다.
포함할 내용:
모든 Dashboard 페이지의 인증 전 로그인 화면 전용 렌더링과 page dispatch·DB 조회·gateway 호출 차단 계약
배포 secret 또는 Streamlit secrets에서 주입하는 단일 관리자 ID·검증용 password secret의 입력·검증, secret 비노출 및 설정 누락 시 fail-closed 계약
현재 Streamlit browser session 범위의 인증 상태, 새 session 재인증 조건과 명시적 로그아웃 동작
Dashboard만 보호하고 MCP 인증을 별도 Issue와 requirement로 분리하는 경계
인증 성공·실패·설정 오류의 사용자 관찰 가능 동작과 실패 시 상태 무변경 조건
기존 Dashboard 기능의 인증 후 호환성 및 배포 설정·secret 주입 계약
정상 인증, 잘못된 credential, 설정 누락, 로그아웃, 인증 전 DB·gateway 미호출에 대한 acceptance criteria
근거 Issue #1, 관련 설계 문서와 구현 PR 미정의 추적성
제외할 내용:
확정 범위 밖의 다중 사용자·역할 기반 권한·사용자별 데이터 분리
SQLite 가계부 도메인 스키마 변경과 자격 증명의 repository 평문 저장
별도 Issue와 requirement로 분리된 MCP 인증, TLS, Ingress 또는 네트워크 경계 전체의 구현
근거: pinned source revision에 실제 req-NNN_.md 요구사항 문서가
없으므로 첫 요구사항 ID는 REQ-001이다. Issue #1과 dashboard/app.py, .env.example, docker-compose.yml, tests/test_dashboard_app.py가 위 계약의 필요성을 뒷받침한다.
데이터 모델
조치: 영향 없음
경로: docs/data-model.md
변경 내용: 단일 관리자 자격 증명은 배포 secret 또는 Streamlit secrets에서 주입하고 영속 사용자 계정을 도입하지 않는다. 가계부 엔터티·필드·관계·불변조건과 SQLite schema 또는 migration을 변경하지 않으므로 데이터 모델 문서를 생성하지 않는다.
근거: 제외 범위가 SQLite 스키마와 가계부 도메인 모델 변경을 명시하며 pinned source revision에도 docs/data-model.md가 없다.
인터페이스 계약
조치: 신규 작성
경로: docs/interface-contract.md
변경 내용: 배포 secret 또는 Streamlit secrets에서 주입한 단일 관리자 ID와 검증용 password secret 입력, 인증 결과와 현재 browser session 상태 출력, 인증·로그아웃 operation, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 별도 범위인 MCP 보호 경계를 정의한다.
근거: dashboard/app.py에는 인증 gate가 없고 .env.example과 docker-compose.yml에는 인증 입력·설정 계약이 없으며 pinned source revision에도 docs/interface-contract.md가 없다.
프로젝트 구조
조치: 신규 작성
경로: docs/project-structure.md
변경 내용: dashboard/app.py가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를 dashboard/ 내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임, tests/의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.
근거: 현재 dashboard/app.py가 DB 경로 취득과 page dispatch를 직접 시작하고 dashboard/page_views/와 tests/test_dashboard_app.py가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는 docs/project-structure.md가 없다.
런타임 흐름
조치: 신규 작성
경로: docs/runtime-flow.md
변경 내용: Streamlit session 시작과 인증 설정 로드, 설정 오류의 즉시 중단, 미인증 상태의 로그인 화면 전용 렌더링과 DB·gateway 미호출, credential 검증 성공·실패 전이, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.
근거: 현재 dashboard/app.py는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도 docs/runtime-flow.md가 없다.
기타 구현 산출물
README.md 갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다.
.env.example 갱신: 실제 secret 값 없이 선택된 인증 설정의 변수 이름과 용도를 문서화한다.
docker-compose.yml 갱신: 선택된 credential source를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.
Dashboard 인증 gate 및 관련 테스트: 정상 인증, 잘못된 credential, 설정 누락, logout, 인증 전 DB·gateway 미호출을 검증한다.
운영 배포 manifest 또는 runbook은 repository 내 관리 위치가 확인되고 선택된 인증 방식에 영향이 있을 때만 갱신한다.
미해결 질문
없음
<!-- gitea-agent:discovery-review-origin/v1 owner=genichin repository=jarca-ledger-mcp issue=1 body-sha256=e5b1c4f69a9ff1480246da60211b072fb16103593f9df20e988fa0b3850d3c76 -->
## Discovery 검토 전 사용자 수정본
<!-- gitea-agent:discovery-draft/v2 -->
## 요약
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret 또는 Streamlit secrets에서 단일 관리자 ID와 검증용 password secret을 주입해 애플리케이션에서 검증하며, 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
## 문제
현재 `dashboard/app.py`의 `main()`은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다. `docker-compose.yml`은 Dashboard를 호스트 `40059`에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고, `.env.example`에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.
## 포함 범위
- Streamlit Dashboard 진입 시 적용되는 공통 관리자 인증 경계 정의
- 인증 전 페이지 dispatch, 금융 데이터 조회, 상태 변경 gateway 호출을 차단하는 동작 정의
- 인증 성공·실패·설정 누락·로그아웃에 대한 사용자 관찰 가능 동작 정의
- 관리자 자격 증명의 배포 설정 계약과 secret 취급 원칙 정의
- 기존 Dashboard 페이지 및 상태 변경 기능에 대한 인증 회귀 테스트 범위 정의
- Compose 및 환경설정 예시와 운영 문서에 필요한 변경 정의
- 선택된 결정을 정식 requirement와 acceptance criteria로 전환하기 위한 문서 산출물 정의
## 제외 범위
- 다중 사용자 계정, 역할 기반 권한, 사용자별 데이터 분리의 즉시 구현
- 회원가입, 비밀번호 찾기, 관리자 UI를 통한 자격 증명 관리
- 외부 IdP/OIDC/SSO 도입 자체의 즉시 구현
- SQLite 스키마 또는 가계부 도메인 모델 변경
- MCP 서버 인증은 별도 Issue와 requirement로 분리하며 이번 구현 범위에서 제외
- 네트워크 경계, TLS, Ingress 인증 프록시 등 인프라 보안 전체의 대체
## 제약 조건
- 인증 검사는 `dashboard/app.py`에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.
- 단일 관리자 ID와 검증용 password secret은 배포 secret 또는 Streamlit secrets로 주입하며, 설정이 없거나 읽을 수 없거나 유효하지 않으면 보호 기능을 우회하지 않고 fail-closed해야 한다.
- 비밀번호·token·hash 등 credential material을 로그, 오류 메시지, URL/query parameter, Issue 또는 repository 문서에 평문으로 노출하지 않아야 한다.
- 인증 상태는 현재 Streamlit browser session에서만 유지하며 명시적 로그아웃 시 폐기하고 새 session에는 재인증을 요구해야 한다.
- Dashboard 인증만 구현하며 MCP endpoint가 보호된다고 간주해서는 안 된다. MCP 인증은 별도 Issue와 requirement에서 다룬다.
- 현재 배포는 동일 이미지의 MCP·Dashboard 컨테이너와 공유 PVC, 단일 replica를 사용한다. 인증 변경은 이 저장 구조를 불필요하게 바꾸지 않아야 한다.
- 테스트는 실제 운영 금융 DB나 실제 credential을 사용하지 않고 synthetic fixture와 test-only secret을 사용해야 한다.
- 확정된 결정을 gitea-agent trusted requirements bundle과 gitea-agent.requirement/v1 계약에 따라 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.
## 확정된 결정
- `genichin/jarca-ledger-mcp` Issue #1은 관리자 인증 기능 추가를 요구한다.
- Discovery 근거 revision은 `main`의 `069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6`으로 고정한다.
- 인증 전에는 로그인 화면만 표시하고 모든 Dashboard 페이지의 조회·쓰기·import를 차단한다.
- 배포 secret 또는 Streamlit secrets에서 단일 관리자 ID와 검증용 password secret을 주입하고 애플리케이션에서 검증한다.
- 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃을 제공하며 새 session에는 재인증을 요구한다.
- 이번 범위는 Streamlit Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
## 저장소 근거
- `dashboard/app.py`: `main()`이 `get_db_path()`, UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.
- `dashboard/common.py`: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은 `transactions_gateway.update_ledger_transaction()`을 호출하고 선택적으로 분류 규칙도 저장한다.
- `dashboard/page_views/accounts.py`: 계좌 변경 form과 `변경 저장` 동작이 존재한다.
- `dashboard/page_views/imports.py`: 금융 PDF upload, import 내역 조회, `rollback_ledger_import_batch()` 호출과 hard-delete 선택지가 존재한다.
- `README.md`: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.
- `docker-compose.yml`: Dashboard는 `40059:8501`로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다.
- `.env.example`: 현재 예시 설정은 `JARCA_LEDGER_DB_PATH`만 제공한다.
- `pyproject.toml`: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.
- `tests/test_dashboard_app.py`: `streamlit.testing.v1.AppTest`와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.
- 위 근거는 모두 pinned SHA `069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6`에서 확인했다.
## 문서 영향 및 산출물
### 요구사항명세서
- 조치: 신규 작성
- 문서 ID: `REQ-001`
- 경로: `docs/requirements/req-001_dashboard-administrator-authentication.md`
- 제목: Dashboard 관리자 인증
- 변경 내용: 관리자 인증 기능을 판정 가능한 요구사항과 acceptance criteria로 정의한다.
- 포함할 내용:
- 모든 Dashboard 페이지의 인증 전 로그인 화면 전용 렌더링과 page dispatch·DB 조회·gateway 호출 차단 계약
- 배포 secret 또는 Streamlit secrets에서 주입하는 단일 관리자 ID·검증용 password secret의 입력·검증, secret 비노출 및 설정 누락 시 fail-closed 계약
- 현재 Streamlit browser session 범위의 인증 상태, 새 session 재인증 조건과 명시적 로그아웃 동작
- Dashboard만 보호하고 MCP 인증을 별도 Issue와 requirement로 분리하는 경계
- 인증 성공·실패·설정 오류의 사용자 관찰 가능 동작과 실패 시 상태 무변경 조건
- 기존 Dashboard 기능의 인증 후 호환성 및 배포 설정·secret 주입 계약
- 정상 인증, 잘못된 credential, 설정 누락, 로그아웃, 인증 전 DB·gateway 미호출에 대한 acceptance criteria
- 근거 Issue `#1`, 관련 설계 문서와 구현 PR `미정`의 추적성
- 제외할 내용:
- 확정 범위 밖의 다중 사용자·역할 기반 권한·사용자별 데이터 분리
- SQLite 가계부 도메인 스키마 변경과 자격 증명의 repository 평문 저장
- 별도 Issue와 requirement로 분리된 MCP 인증, TLS, Ingress 또는 네트워크 경계 전체의 구현
- 근거: pinned source revision에 실제 req-NNN_<slug>.md 요구사항 문서가
없으므로 첫 요구사항 ID는 REQ-001이다. Issue #1과 `dashboard/app.py`, `.env.example`, `docker-compose.yml`, `tests/test_dashboard_app.py`가 위 계약의 필요성을 뒷받침한다.
### 데이터 모델
- 조치: 영향 없음
- 경로: `docs/data-model.md`
- 변경 내용: 단일 관리자 자격 증명은 배포 secret 또는 Streamlit secrets에서 주입하고 영속 사용자 계정을 도입하지 않는다. 가계부 엔터티·필드·관계·불변조건과 SQLite schema 또는 migration을 변경하지 않으므로 데이터 모델 문서를 생성하지 않는다.
- 근거: 제외 범위가 SQLite 스키마와 가계부 도메인 모델 변경을 명시하며 pinned source revision에도 `docs/data-model.md`가 없다.
### 인터페이스 계약
- 조치: 신규 작성
- 경로: `docs/interface-contract.md`
- 변경 내용: 배포 secret 또는 Streamlit secrets에서 주입한 단일 관리자 ID와 검증용 password secret 입력, 인증 결과와 현재 browser session 상태 출력, 인증·로그아웃 operation, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 별도 범위인 MCP 보호 경계를 정의한다.
- 근거: `dashboard/app.py`에는 인증 gate가 없고 `.env.example`과 `docker-compose.yml`에는 인증 입력·설정 계약이 없으며 pinned source revision에도 `docs/interface-contract.md`가 없다.
### 프로젝트 구조
- 조치: 신규 작성
- 경로: `docs/project-structure.md`
- 변경 내용: `dashboard/app.py`가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를 `dashboard/` 내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임, `tests/`의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.
- 근거: 현재 `dashboard/app.py`가 DB 경로 취득과 page dispatch를 직접 시작하고 `dashboard/page_views/`와 `tests/test_dashboard_app.py`가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는 `docs/project-structure.md`가 없다.
### 런타임 흐름
- 조치: 신규 작성
- 경로: `docs/runtime-flow.md`
- 변경 내용: Streamlit session 시작과 인증 설정 로드, 설정 오류의 즉시 중단, 미인증 상태의 로그인 화면 전용 렌더링과 DB·gateway 미호출, credential 검증 성공·실패 전이, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.
- 근거: 현재 `dashboard/app.py`는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도 `docs/runtime-flow.md`가 없다.
### 기타 구현 산출물
- `README.md` 갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다.
- `.env.example` 갱신: 실제 secret 값 없이 선택된 인증 설정의 변수 이름과 용도를 문서화한다.
- `docker-compose.yml` 갱신: 선택된 credential source를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.
- Dashboard 인증 gate 및 관련 테스트: 정상 인증, 잘못된 credential, 설정 누락, logout, 인증 전 DB·gateway 미호출을 검증한다.
- 운영 배포 manifest 또는 runbook은 repository 내 관리 위치가 확인되고 선택된 인증 방식에 영향이 있을 때만 갱신한다.
## 미해결 질문
없음
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret 또는 Streamlit secrets에서 단일 관리자 ID와 password secret을 주입해 애플리케이션에서 검증하며, 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다. Credential source·우선순위와 password verifier 표현은 OQ-005와 OQ-006에서 확정한다.
문제
현재 dashboard/app.py의 main()은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다. docker-compose.yml은 Dashboard를 호스트 40059에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고, .env.example에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.
포함 범위
Streamlit Dashboard 진입 시 적용되는 공통 관리자 인증 경계 정의
인증 전 페이지 dispatch, 금융 데이터 조회, 상태 변경 gateway 호출을 차단하는 동작 정의
인증 성공·실패·설정 누락·로그아웃에 대한 사용자 관찰 가능 동작 정의
관리자 자격 증명의 배포 설정 계약과 secret 취급 원칙 정의
기존 Dashboard 페이지 및 상태 변경 기능에 대한 인증 회귀 테스트 범위 정의
Compose 및 환경설정 예시와 운영 문서에 필요한 변경 정의
선택된 결정을 정식 requirement와 acceptance criteria로 전환하기 위한 문서 산출물 정의
제외 범위
다중 사용자 계정, 역할 기반 권한, 사용자별 데이터 분리의 즉시 구현
회원가입, 비밀번호 찾기, 관리자 UI를 통한 자격 증명 관리
외부 IdP/OIDC/SSO 도입 자체의 즉시 구현
SQLite 스키마 또는 가계부 도메인 모델 변경
MCP 서버 인증은 별도 Issue와 requirement로 분리하며 이번 구현 범위에서 제외
네트워크 경계, TLS, Ingress 인증 프록시 등 인프라 보안 전체의 대체
제약 조건
인증 검사는 dashboard/app.py에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.
단일 관리자 ID와 password secret은 배포 secret 또는 Streamlit secrets 경계를 통해 주입한다. Exact key·source·우선순위와 password verifier 표현은 OQ-005와 OQ-006에서 확정하며, 설정이 없거나 읽을 수 없거나 유효하지 않으면 보호 기능을 우회하지 않고 fail-closed해야 한다.
비밀번호·token·hash 등 credential material을 로그, 오류 메시지, URL/query parameter, Issue 또는 repository 문서에 평문으로 노출하지 않아야 한다.
인증 상태는 현재 Streamlit browser session에서만 유지하며 명시적 로그아웃 시 폐기하고 새 session에는 재인증을 요구해야 한다.
Dashboard 인증만 구현하며 MCP endpoint가 보호된다고 간주해서는 안 된다. MCP 인증은 별도 Issue와 requirement에서 다룬다.
현재 배포는 동일 이미지의 MCP·Dashboard 컨테이너와 공유 PVC, 단일 replica를 사용한다. 인증 변경은 이 저장 구조를 불필요하게 바꾸지 않아야 한다.
테스트는 실제 운영 금융 DB나 실제 credential을 사용하지 않고 synthetic fixture와 test-only secret을 사용해야 한다.
확정된 결정을 gitea-agent trusted requirements bundle과 gitea-agent.requirement/v1 계약에 따라 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.
확정된 결정
genichin/jarca-ledger-mcp Issue #1은 관리자 인증 기능 추가를 요구한다.
인증 전에는 로그인 화면만 표시하고 모든 Dashboard 페이지의 조회·쓰기·import를 차단한다.
배포 secret 또는 Streamlit secrets 경계에서 단일 관리자 ID와 password secret을 주입해 애플리케이션에서 검증한다. Exact source·우선순위와 password verifier 표현은 아직 확정되지 않았다.
인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃을 제공하며 새 session에는 재인증을 요구한다.
이번 범위는 Streamlit Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
저장소 근거
dashboard/app.py: main()이 get_db_path(), UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.
dashboard/common.py: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은 transactions_gateway.update_ledger_transaction()을 호출하고 선택적으로 분류 규칙도 저장한다.
dashboard/page_views/accounts.py: 계좌 변경 form과 변경 저장 동작이 존재한다.
dashboard/page_views/imports.py: 금융 PDF upload, import 내역 조회, rollback_ledger_import_batch() 호출과 hard-delete 선택지가 존재한다.
README.md: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.
docker-compose.yml: Dashboard는 40059:8501로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다.
.env.example: 현재 예시 설정은 JARCA_LEDGER_DB_PATH만 제공한다.
pyproject.toml: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.
tests/test_dashboard_app.py: streamlit.testing.v1.AppTest와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.
위 근거는 모두 pinned SHA 069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6에서 확인했다.
변경 내용: 관리자 인증 기능을 판정 가능한 요구사항과 acceptance criteria로 정의한다.
포함할 내용:
모든 Dashboard 페이지의 인증 전 로그인 화면 전용 렌더링과 page dispatch·DB 조회·gateway 호출 차단 계약
OQ-005에서 확정할 credential source·exact key·우선순위·충돌·fallback 계약
OQ-006에서 확정할 password secret 또는 verifier 표현과 검증 계약
단일 관리자 ID·password 입력, secret 비노출 및 설정 누락·빈 값·읽기 오류·형식 오류 시 fail-closed 계약
현재 Streamlit browser session 범위의 인증 상태, 새 session 재인증 조건과 명시적 로그아웃 동작
Dashboard만 보호하고 MCP 인증을 별도 Issue와 requirement로 분리하는 경계
인증 성공·실패·설정 오류의 사용자 관찰 가능 동작과 실패 시 상태 무변경 조건
기존 Dashboard 기능의 인증 후 호환성 및 배포 설정·secret 주입 계약
미인증·설정 오류·잘못된 credential 상태에서 get_db_path()·page dispatch·DB·gateway·upload·rollback을 호출하지 않고 query parameter로 우회할 수 없다는 acceptance criteria
정상 인증 후 기존 Dashboard 페이지 접근, 같은 browser session의 rerun에서 인증 유지, 로그아웃 직후 상태 폐기, 새 session 재인증에 대한 acceptance criteria
credential 원문 또는 verifier를 화면·로그·traceback·URL/query parameter·st.session_state에 노출하지 않는 acceptance criteria
근거 Issue #1, 관련 설계 문서와 구현 PR 미정의 추적성
제외할 내용:
확정 범위 밖의 다중 사용자·역할 기반 권한·사용자별 데이터 분리
SQLite 가계부 도메인 스키마 변경과 자격 증명의 repository 평문 저장
별도 Issue와 requirement로 분리된 MCP 인증, TLS, Ingress 또는 네트워크 경계 전체의 구현
근거: pinned source revision에 할당된 요구사항 문서가 없으므로 첫 요구사항 ID는 REQ-001이다. Issue #1과 dashboard/app.py, .env.example, docker-compose.yml, tests/test_dashboard_app.py가 위 계약의 필요성을 뒷받침한다.
데이터 모델
조치: 영향 없음
경로: docs/data-model.md
변경 내용: 단일 관리자 자격 증명은 배포 secret 또는 Streamlit secrets에서 주입하고 영속 사용자 계정을 도입하지 않는다. 가계부 엔터티·필드·관계·불변조건과 SQLite schema 또는 migration을 변경하지 않으므로 데이터 모델 문서를 생성하지 않는다.
근거: 제외 범위가 SQLite 스키마와 가계부 도메인 모델 변경을 명시하며 pinned source revision에도 docs/data-model.md가 없다.
인터페이스 계약
조치: 신규 작성
경로: docs/interface-contract.md
변경 내용: OQ-005와 OQ-006에서 확정한 credential source·exact key·우선순위·충돌·fallback과 password verifier 표현, 단일 관리자 ID·password 입력, 인증 결과와 현재 browser session 상태 출력, 인증·로그아웃 operation, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 별도 범위인 MCP 보호 경계를 정의한다.
근거: dashboard/app.py에는 인증 gate가 없고 .env.example과 docker-compose.yml에는 인증 입력·설정 계약이 없으며 pinned source revision에도 docs/interface-contract.md가 없다.
프로젝트 구조
조치: 신규 작성
경로: docs/project-structure.md
변경 내용: dashboard/app.py가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를 dashboard/ 내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임, tests/의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.
근거: 현재 dashboard/app.py가 DB 경로 취득과 page dispatch를 직접 시작하고 dashboard/page_views/와 tests/test_dashboard_app.py가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는 docs/project-structure.md가 없다.
런타임 흐름
조치: 신규 작성
경로: docs/runtime-flow.md
변경 내용: Streamlit session 시작, OQ-005와 OQ-006에서 확정한 credential source resolution과 verifier 검증, 설정 오류의 즉시 중단, 미인증 상태의 로그인 화면 전용 렌더링과 get_db_path()·DB·gateway·upload·rollback 미호출, credential 검증 성공·실패 전이, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.
근거: 현재 dashboard/app.py는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도 docs/runtime-flow.md가 없다.
기타 구현 산출물
README.md 갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다.
.env.example 갱신: 실제 secret 값 없이 OQ-005에서 선택된 환경변수 source가 있을 때 exact key와 용도를 문서화한다.
docker-compose.yml 갱신: OQ-005에서 선택된 credential source를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.
Dashboard 인증 gate 및 관련 테스트: 정상 인증, 잘못된 credential, 설정 누락, logout, 인증 전 DB·gateway 미호출을 검증한다.
운영 배포 manifest 또는 runbook은 repository 내 관리 위치가 확인되고 선택된 인증 방식에 영향이 있을 때만 갱신한다.
미해결 질문
OQ-005 — 관리자 credential의 source와 우선순위를 어떻게 고정할 것인가?
현재 선택은 배포 secret 또는 Streamlit secrets를 허용하지만 exact key, 동시 설정과 fallback 계약이 없다. 구현·Compose·로컬 실행·테스트가 같은 설정 인터페이스를 사용하도록 하나를 확정해야 한다.
배포 secret이 주입하는 환경변수 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD만 사용한다. (추천)
Streamlit secrets의 auth.admin_id와 auth.admin_password만 사용한다.
두 source를 모두 지원하고 환경변수를 우선하며, 환경변수가 존재하지만 빈 값·형식 오류이면 Streamlit secrets로 fallback하지 않고 fail-closed한다.
두 source를 모두 지원하되 동시에 설정되면 값의 동일 여부와 관계없이 fail-closed한다.
OQ-006 — Password secret을 어떤 표현으로 검증할 것인가?
평문 password secret과 encoded verifier는 배포 방법·dependency·검증 및 오류 계약이 다르다. 어느 경우에도 credential 원문이나 verifier를 repository, 로그, URL 또는 st.session_state에 저장하지 않는다.
<!-- gitea-agent:discovery-review-origin/v1 owner=genichin repository=jarca-ledger-mcp issue=1 body-sha256=c4cdd6377fd48c7a974df47f1c764dfd0b238664a8eec124119486e36a24abd1 -->
## Discovery 검토 전 사용자 수정본
<!-- gitea-agent:discovery-draft/v2 -->
## 요약
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret 또는 Streamlit secrets에서 단일 관리자 ID와 password secret을 주입해 애플리케이션에서 검증하며, 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다. Credential source·우선순위와 password verifier 표현은 OQ-005와 OQ-006에서 확정한다.
## 문제
현재 `dashboard/app.py`의 `main()`은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다. `docker-compose.yml`은 Dashboard를 호스트 `40059`에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고, `.env.example`에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.
## 포함 범위
- Streamlit Dashboard 진입 시 적용되는 공통 관리자 인증 경계 정의
- 인증 전 페이지 dispatch, 금융 데이터 조회, 상태 변경 gateway 호출을 차단하는 동작 정의
- 인증 성공·실패·설정 누락·로그아웃에 대한 사용자 관찰 가능 동작 정의
- 관리자 자격 증명의 배포 설정 계약과 secret 취급 원칙 정의
- 기존 Dashboard 페이지 및 상태 변경 기능에 대한 인증 회귀 테스트 범위 정의
- Compose 및 환경설정 예시와 운영 문서에 필요한 변경 정의
- 선택된 결정을 정식 requirement와 acceptance criteria로 전환하기 위한 문서 산출물 정의
## 제외 범위
- 다중 사용자 계정, 역할 기반 권한, 사용자별 데이터 분리의 즉시 구현
- 회원가입, 비밀번호 찾기, 관리자 UI를 통한 자격 증명 관리
- 외부 IdP/OIDC/SSO 도입 자체의 즉시 구현
- SQLite 스키마 또는 가계부 도메인 모델 변경
- MCP 서버 인증은 별도 Issue와 requirement로 분리하며 이번 구현 범위에서 제외
- 네트워크 경계, TLS, Ingress 인증 프록시 등 인프라 보안 전체의 대체
## 제약 조건
- 인증 검사는 `dashboard/app.py`에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.
- 단일 관리자 ID와 password secret은 배포 secret 또는 Streamlit secrets 경계를 통해 주입한다. Exact key·source·우선순위와 password verifier 표현은 OQ-005와 OQ-006에서 확정하며, 설정이 없거나 읽을 수 없거나 유효하지 않으면 보호 기능을 우회하지 않고 fail-closed해야 한다.
- 비밀번호·token·hash 등 credential material을 로그, 오류 메시지, URL/query parameter, Issue 또는 repository 문서에 평문으로 노출하지 않아야 한다.
- 인증 상태는 현재 Streamlit browser session에서만 유지하며 명시적 로그아웃 시 폐기하고 새 session에는 재인증을 요구해야 한다.
- Dashboard 인증만 구현하며 MCP endpoint가 보호된다고 간주해서는 안 된다. MCP 인증은 별도 Issue와 requirement에서 다룬다.
- 현재 배포는 동일 이미지의 MCP·Dashboard 컨테이너와 공유 PVC, 단일 replica를 사용한다. 인증 변경은 이 저장 구조를 불필요하게 바꾸지 않아야 한다.
- 테스트는 실제 운영 금융 DB나 실제 credential을 사용하지 않고 synthetic fixture와 test-only secret을 사용해야 한다.
- 확정된 결정을 gitea-agent trusted requirements bundle과 gitea-agent.requirement/v1 계약에 따라 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.
## 확정된 결정
- `genichin/jarca-ledger-mcp` Issue #1은 관리자 인증 기능 추가를 요구한다.
- Discovery 근거 revision은 `main`의 `069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6`으로 고정한다.
- 인증 전에는 로그인 화면만 표시하고 모든 Dashboard 페이지의 조회·쓰기·import를 차단한다.
- 배포 secret 또는 Streamlit secrets 경계에서 단일 관리자 ID와 password secret을 주입해 애플리케이션에서 검증한다. Exact source·우선순위와 password verifier 표현은 아직 확정되지 않았다.
- 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃을 제공하며 새 session에는 재인증을 요구한다.
- 이번 범위는 Streamlit Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
## 저장소 근거
- `dashboard/app.py`: `main()`이 `get_db_path()`, UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.
- `dashboard/common.py`: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은 `transactions_gateway.update_ledger_transaction()`을 호출하고 선택적으로 분류 규칙도 저장한다.
- `dashboard/page_views/accounts.py`: 계좌 변경 form과 `변경 저장` 동작이 존재한다.
- `dashboard/page_views/imports.py`: 금융 PDF upload, import 내역 조회, `rollback_ledger_import_batch()` 호출과 hard-delete 선택지가 존재한다.
- `README.md`: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.
- `docker-compose.yml`: Dashboard는 `40059:8501`로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다.
- `.env.example`: 현재 예시 설정은 `JARCA_LEDGER_DB_PATH`만 제공한다.
- `pyproject.toml`: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.
- `tests/test_dashboard_app.py`: `streamlit.testing.v1.AppTest`와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.
- 위 근거는 모두 pinned SHA `069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6`에서 확인했다.
## 문서 영향 및 산출물
### 요구사항명세서
- 조치: 신규 작성
- 문서 ID: `REQ-001`
- 경로: `docs/requirements/req-001_dashboard-administrator-authentication.md`
- 제목: Dashboard 관리자 인증
- 변경 내용: 관리자 인증 기능을 판정 가능한 요구사항과 acceptance criteria로 정의한다.
- 포함할 내용:
- 모든 Dashboard 페이지의 인증 전 로그인 화면 전용 렌더링과 page dispatch·DB 조회·gateway 호출 차단 계약
- OQ-005에서 확정할 credential source·exact key·우선순위·충돌·fallback 계약
- OQ-006에서 확정할 password secret 또는 verifier 표현과 검증 계약
- 단일 관리자 ID·password 입력, secret 비노출 및 설정 누락·빈 값·읽기 오류·형식 오류 시 fail-closed 계약
- 현재 Streamlit browser session 범위의 인증 상태, 새 session 재인증 조건과 명시적 로그아웃 동작
- Dashboard만 보호하고 MCP 인증을 별도 Issue와 requirement로 분리하는 경계
- 인증 성공·실패·설정 오류의 사용자 관찰 가능 동작과 실패 시 상태 무변경 조건
- 기존 Dashboard 기능의 인증 후 호환성 및 배포 설정·secret 주입 계약
- 미인증·설정 오류·잘못된 credential 상태에서 `get_db_path()`·page dispatch·DB·gateway·upload·rollback을 호출하지 않고 query parameter로 우회할 수 없다는 acceptance criteria
- 정상 인증 후 기존 Dashboard 페이지 접근, 같은 browser session의 rerun에서 인증 유지, 로그아웃 직후 상태 폐기, 새 session 재인증에 대한 acceptance criteria
- credential 원문 또는 verifier를 화면·로그·traceback·URL/query parameter·`st.session_state`에 노출하지 않는 acceptance criteria
- 근거 Issue `#1`, 관련 설계 문서와 구현 PR `미정`의 추적성
- 제외할 내용:
- 확정 범위 밖의 다중 사용자·역할 기반 권한·사용자별 데이터 분리
- SQLite 가계부 도메인 스키마 변경과 자격 증명의 repository 평문 저장
- 별도 Issue와 requirement로 분리된 MCP 인증, TLS, Ingress 또는 네트워크 경계 전체의 구현
- 근거: pinned source revision에 할당된 요구사항 문서가 없으므로 첫 요구사항 ID는 `REQ-001`이다. Issue #1과 `dashboard/app.py`, `.env.example`, `docker-compose.yml`, `tests/test_dashboard_app.py`가 위 계약의 필요성을 뒷받침한다.
### 데이터 모델
- 조치: 영향 없음
- 경로: `docs/data-model.md`
- 변경 내용: 단일 관리자 자격 증명은 배포 secret 또는 Streamlit secrets에서 주입하고 영속 사용자 계정을 도입하지 않는다. 가계부 엔터티·필드·관계·불변조건과 SQLite schema 또는 migration을 변경하지 않으므로 데이터 모델 문서를 생성하지 않는다.
- 근거: 제외 범위가 SQLite 스키마와 가계부 도메인 모델 변경을 명시하며 pinned source revision에도 `docs/data-model.md`가 없다.
### 인터페이스 계약
- 조치: 신규 작성
- 경로: `docs/interface-contract.md`
- 변경 내용: OQ-005와 OQ-006에서 확정한 credential source·exact key·우선순위·충돌·fallback과 password verifier 표현, 단일 관리자 ID·password 입력, 인증 결과와 현재 browser session 상태 출력, 인증·로그아웃 operation, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 별도 범위인 MCP 보호 경계를 정의한다.
- 근거: `dashboard/app.py`에는 인증 gate가 없고 `.env.example`과 `docker-compose.yml`에는 인증 입력·설정 계약이 없으며 pinned source revision에도 `docs/interface-contract.md`가 없다.
### 프로젝트 구조
- 조치: 신규 작성
- 경로: `docs/project-structure.md`
- 변경 내용: `dashboard/app.py`가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를 `dashboard/` 내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임, `tests/`의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.
- 근거: 현재 `dashboard/app.py`가 DB 경로 취득과 page dispatch를 직접 시작하고 `dashboard/page_views/`와 `tests/test_dashboard_app.py`가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는 `docs/project-structure.md`가 없다.
### 런타임 흐름
- 조치: 신규 작성
- 경로: `docs/runtime-flow.md`
- 변경 내용: Streamlit session 시작, OQ-005와 OQ-006에서 확정한 credential source resolution과 verifier 검증, 설정 오류의 즉시 중단, 미인증 상태의 로그인 화면 전용 렌더링과 `get_db_path()`·DB·gateway·upload·rollback 미호출, credential 검증 성공·실패 전이, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.
- 근거: 현재 `dashboard/app.py`는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도 `docs/runtime-flow.md`가 없다.
### 기타 구현 산출물
- `README.md` 갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다.
- `.env.example` 갱신: 실제 secret 값 없이 OQ-005에서 선택된 환경변수 source가 있을 때 exact key와 용도를 문서화한다.
- `docker-compose.yml` 갱신: OQ-005에서 선택된 credential source를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.
- Dashboard 인증 gate 및 관련 테스트: 정상 인증, 잘못된 credential, 설정 누락, logout, 인증 전 DB·gateway 미호출을 검증한다.
- 운영 배포 manifest 또는 runbook은 repository 내 관리 위치가 확인되고 선택된 인증 방식에 영향이 있을 때만 갱신한다.
## 미해결 질문
### OQ-005 — 관리자 credential의 source와 우선순위를 어떻게 고정할 것인가?
현재 선택은 배포 secret 또는 Streamlit secrets를 허용하지만 exact key, 동시 설정과 fallback 계약이 없다. 구현·Compose·로컬 실행·테스트가 같은 설정 인터페이스를 사용하도록 하나를 확정해야 한다.
- [x] 배포 secret이 주입하는 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`만 사용한다. (추천)
- [ ] Streamlit secrets의 `auth.admin_id`와 `auth.admin_password`만 사용한다.
- [ ] 두 source를 모두 지원하고 환경변수를 우선하며, 환경변수가 존재하지만 빈 값·형식 오류이면 Streamlit secrets로 fallback하지 않고 fail-closed한다.
- [ ] 두 source를 모두 지원하되 동시에 설정되면 값의 동일 여부와 관계없이 fail-closed한다.
### OQ-006 — Password secret을 어떤 표현으로 검증할 것인가?
평문 password secret과 encoded verifier는 배포 방법·dependency·검증 및 오류 계약이 다르다. 어느 경우에도 credential 원문이나 verifier를 repository, 로그, URL 또는 `st.session_state`에 저장하지 않는다.
- [x] Secret store에서 평문 password secret을 주입하고 애플리케이션에서 constant-time 비교한다. (추천)
- [ ] Secret store에서 versioned password verifier를 주입하고 애플리케이션이 verifier 형식과 algorithm을 검증한다.
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret이 주입하는 환경변수 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD에서 단일 관리자 자격 증명을 읽고, password secret을 애플리케이션에서 constant-time 비교한다. 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다. 실패 로그인 반복 시도 제어는 OQ-007에서 확정한다.
문제
현재 dashboard/app.py의 main()은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다. docker-compose.yml은 Dashboard를 호스트 40059에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고, .env.example에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.
포함 범위
Streamlit Dashboard 진입 시 적용되는 공통 관리자 인증 경계 정의
인증 전 페이지 dispatch, 금융 데이터 조회, 상태 변경 gateway 호출을 차단하는 동작 정의
인증 성공·실패·설정 누락·로그아웃에 대한 사용자 관찰 가능 동작 정의
관리자 자격 증명의 배포 설정 계약과 secret 취급 원칙 정의
기존 Dashboard 페이지 및 상태 변경 기능에 대한 인증 회귀 테스트 범위 정의
Compose 및 환경설정 예시와 운영 문서에 필요한 변경 정의
선택된 결정을 정식 requirement와 acceptance criteria로 전환하기 위한 문서 산출물 정의
제외 범위
다중 사용자 계정, 역할 기반 권한, 사용자별 데이터 분리의 즉시 구현
회원가입, 비밀번호 찾기, 관리자 UI를 통한 자격 증명 관리
외부 IdP/OIDC/SSO 도입 자체의 즉시 구현
SQLite 스키마 또는 가계부 도메인 모델 변경
MCP 서버 인증은 별도 Issue와 requirement로 분리하며 이번 구현 범위에서 제외
네트워크 경계, TLS, Ingress 인증 프록시 등 인프라 보안 전체의 대체
제약 조건
인증 검사는 dashboard/app.py에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.
단일 관리자 ID와 평문 password secret은 배포 secret이 주입하는 환경변수 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD에서만 읽고 Streamlit secrets로 fallback하지 않는다. 두 값 중 하나라도 없거나 비어 있거나 읽을 수 없으면 fail-closed하며, password는 애플리케이션에서 constant-time 비교해야 한다.
비밀번호·token·hash 등 credential material을 로그, 오류 메시지, URL/query parameter, Issue 또는 repository 문서에 평문으로 노출하지 않아야 한다.
인증 상태는 현재 Streamlit browser session에서만 유지하며 명시적 로그아웃 시 폐기하고 새 session에는 재인증을 요구해야 한다.
실패 로그인 반복 시도에 대한 지연·잠금·rate-limit 정책은 OQ-007에서 확정하며, 구현 에이전트가 임의의 임계값이나 잠금 범위를 선택해서는 안 된다.
Dashboard 인증만 구현하며 MCP endpoint가 보호된다고 간주해서는 안 된다. MCP 인증은 별도 Issue와 requirement에서 다룬다.
현재 배포는 동일 이미지의 MCP·Dashboard 컨테이너와 공유 PVC, 단일 replica를 사용한다. 인증 변경은 이 저장 구조를 불필요하게 바꾸지 않아야 한다.
테스트는 실제 운영 금융 DB나 실제 credential을 사용하지 않고 synthetic fixture와 test-only secret을 사용해야 한다.
확정된 결정을 gitea-agent trusted requirements bundle과 gitea-agent.requirement/v1 계약에 따라 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.
확정된 결정
genichin/jarca-ledger-mcp Issue #1은 관리자 인증 기능 추가를 요구한다.
인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃을 제공하며 새 session에는 재인증을 요구한다.
이번 범위는 Streamlit Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
저장소 근거
dashboard/app.py: main()이 get_db_path(), UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.
dashboard/common.py: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은 transactions_gateway.update_ledger_transaction()을 호출하고 선택적으로 분류 규칙도 저장한다.
dashboard/page_views/accounts.py: 계좌 변경 form과 변경 저장 동작이 존재한다.
dashboard/page_views/imports.py: 금융 PDF upload, import 내역 조회, rollback_ledger_import_batch() 호출과 hard-delete 선택지가 존재한다.
README.md: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.
docker-compose.yml: Dashboard는 40059:8501로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다.
.env.example: 현재 예시 설정은 JARCA_LEDGER_DB_PATH만 제공한다.
pyproject.toml: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.
tests/test_dashboard_app.py: streamlit.testing.v1.AppTest와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.
위 근거는 모두 pinned SHA 069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6에서 확인했다.
변경 내용: 관리자 인증 기능을 판정 가능한 요구사항과 acceptance criteria로 정의한다.
포함할 내용:
모든 Dashboard 페이지의 인증 전 로그인 화면 전용 렌더링과 page dispatch·DB 조회·gateway 호출 차단 계약
환경변수 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD만 사용하는 credential source 계약과 Streamlit secrets fallback 금지
평문 password secret의 constant-time 비교와 credential material 비보존 계약
OQ-007에서 확정할 실패 로그인 반복 시도 제어, 적용 범위, 임계값, 해제 조건과 사용자 관찰 가능 동작
단일 관리자 ID·password 입력, secret 비노출 및 설정 누락·빈 값·읽기 오류·형식 오류 시 fail-closed 계약
현재 Streamlit browser session 범위의 인증 상태, 새 session 재인증 조건과 명시적 로그아웃 동작
Dashboard만 보호하고 MCP 인증을 별도 Issue와 requirement로 분리하는 경계
인증 성공·실패·설정 오류의 사용자 관찰 가능 동작과 실패 시 상태 무변경 조건
기존 Dashboard 기능의 인증 후 호환성 및 배포 설정·secret 주입 계약
미인증·설정 오류·잘못된 credential 상태에서 get_db_path()·page dispatch·DB·gateway·upload·rollback을 호출하지 않고 query parameter로 우회할 수 없다는 acceptance criteria
정상 인증 후 기존 Dashboard 페이지 접근, 같은 browser session의 rerun에서 인증 유지, 로그아웃 직후 상태 폐기, 새 session 재인증에 대한 acceptance criteria
credential 원문 또는 verifier를 화면·로그·traceback·URL/query parameter·st.session_state에 노출하지 않는 acceptance criteria
근거 Issue #1, 관련 설계 문서와 구현 PR 미정의 추적성
제외할 내용:
확정 범위 밖의 다중 사용자·역할 기반 권한·사용자별 데이터 분리
SQLite 가계부 도메인 스키마 변경과 자격 증명의 repository 평문 저장
별도 Issue와 requirement로 분리된 MCP 인증, TLS, Ingress 또는 네트워크 경계 전체의 구현
근거: pinned source revision에 할당된 요구사항 문서가 없으므로 첫 요구사항 ID는 REQ-001이다. Issue #1과 dashboard/app.py, .env.example, docker-compose.yml, tests/test_dashboard_app.py가 위 계약의 필요성을 뒷받침한다.
데이터 모델
조치: 영향 없음
경로: docs/data-model.md
변경 내용: 단일 관리자 자격 증명은 배포 secret이 주입하는 환경변수에서 읽고 영속 사용자 계정을 도입하지 않는다. 가계부 엔터티·필드·관계·불변조건과 SQLite schema 또는 migration을 변경하지 않으므로 데이터 모델 문서를 생성하지 않는다.
근거: 제외 범위가 SQLite 스키마와 가계부 도메인 모델 변경을 명시하며 pinned source revision에도 docs/data-model.md가 없다.
인터페이스 계약
조치: 신규 작성
경로: docs/interface-contract.md
변경 내용: 환경변수 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD만 사용하는 credential 입력, Streamlit secrets fallback 금지, 평문 password secret의 constant-time 비교, 인증 결과와 현재 browser session 상태 출력, 인증·로그아웃 operation, OQ-007에서 확정한 실패 로그인 반복 시도 제어와 오류, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 별도 범위인 MCP 보호 경계를 정의한다.
근거: dashboard/app.py에는 인증 gate가 없고 .env.example과 docker-compose.yml에는 인증 입력·설정 계약이 없으며 pinned source revision에도 docs/interface-contract.md가 없다.
프로젝트 구조
조치: 신규 작성
경로: docs/project-structure.md
변경 내용: dashboard/app.py가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를 dashboard/ 내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임, tests/의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.
근거: 현재 dashboard/app.py가 DB 경로 취득과 page dispatch를 직접 시작하고 dashboard/page_views/와 tests/test_dashboard_app.py가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는 docs/project-structure.md가 없다.
런타임 흐름
조치: 신규 작성
경로: docs/runtime-flow.md
변경 내용: Streamlit session 시작, 환경변수 credential 로드와 설정 검증, 평문 password secret의 constant-time 비교, 설정 오류의 즉시 중단, 미인증 상태의 로그인 화면 전용 렌더링과 get_db_path()·DB·gateway·upload·rollback 미호출, credential 검증 성공·실패 전이, OQ-007에서 확정한 반복 실패 제어와 해제 전이, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.
근거: 현재 dashboard/app.py는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도 docs/runtime-flow.md가 없다.
기타 구현 산출물
README.md 갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다.
.env.example 갱신: 실제 secret 값 없이 JARCA_DASHBOARD_ADMIN_ID와 JARCA_DASHBOARD_ADMIN_PASSWORD의 이름과 용도를 문서화한다.
docker-compose.yml 갱신: 배포 secret의 두 환경변수를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.
Dashboard 인증 gate 및 관련 테스트: 정상 인증, 잘못된 credential, 설정 누락, logout, 인증 전 DB·gateway 미호출과 OQ-007에서 확정한 반복 실패 제어를 검증한다.
운영 배포 manifest 또는 runbook은 repository 내 관리 위치가 확인되고 선택된 인증 방식에 영향이 있을 때만 갱신한다.
미해결 질문
OQ-007 — 실패 로그인 반복 시도를 어떻게 제어할 것인가?
현재 Discovery는 잘못된 credential의 fail-closed 동작만 정의하고 반복 실패의 지연, 잠금, 적용 범위와 해제 조건은 정하지 않았다. 현재 배포는 단일 replica이지만 새 browser session으로 session-local 상태를 우회할 수 있고, 전역 잠금은 공격자가 관리자를 잠그는 denial-of-service를 만들 수 있으므로 구현 전에 정책을 명시적으로 선택해야 한다.
매 실패 응답을 최소 1초 지연하고 애플리케이션 잠금은 두지 않으며, 이 제한의 우회 가능성과 배포 경계에서 별도 rate limiting이 필요함을 운영 문서에 명시한다. (추천)
서버 process 전체에서 관리자 ID 기준 5회 연속 실패 시 15분 잠그고, 정상 인증 시 실패 횟수를 초기화한다.
현재 browser session에서 5회 연속 실패 시 해당 session을 15분 잠그고, 새 session 우회 가능성을 운영 문서에 명시한다.
애플리케이션 수준의 지연·잠금·rate limiting을 두지 않고 brute-force 위험을 명시적으로 수용한다.
<!-- gitea-agent:discovery-review-origin/v1 owner=genichin repository=jarca-ledger-mcp issue=1 body-sha256=2c9fb2729d3d6169fe3db9525a09179812838f09f19c1eb08ce22db60d866e2d -->
## Discovery 검토 전 사용자 수정본
<!-- gitea-agent:discovery-draft/v2 -->
## 요약
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret이 주입하는 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`에서 단일 관리자 자격 증명을 읽고, password secret을 애플리케이션에서 constant-time 비교한다. 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다. 실패 로그인 반복 시도 제어는 OQ-007에서 확정한다.
## 문제
현재 `dashboard/app.py`의 `main()`은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다. `docker-compose.yml`은 Dashboard를 호스트 `40059`에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고, `.env.example`에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.
## 포함 범위
- Streamlit Dashboard 진입 시 적용되는 공통 관리자 인증 경계 정의
- 인증 전 페이지 dispatch, 금융 데이터 조회, 상태 변경 gateway 호출을 차단하는 동작 정의
- 인증 성공·실패·설정 누락·로그아웃에 대한 사용자 관찰 가능 동작 정의
- 관리자 자격 증명의 배포 설정 계약과 secret 취급 원칙 정의
- 기존 Dashboard 페이지 및 상태 변경 기능에 대한 인증 회귀 테스트 범위 정의
- Compose 및 환경설정 예시와 운영 문서에 필요한 변경 정의
- 선택된 결정을 정식 requirement와 acceptance criteria로 전환하기 위한 문서 산출물 정의
## 제외 범위
- 다중 사용자 계정, 역할 기반 권한, 사용자별 데이터 분리의 즉시 구현
- 회원가입, 비밀번호 찾기, 관리자 UI를 통한 자격 증명 관리
- 외부 IdP/OIDC/SSO 도입 자체의 즉시 구현
- SQLite 스키마 또는 가계부 도메인 모델 변경
- MCP 서버 인증은 별도 Issue와 requirement로 분리하며 이번 구현 범위에서 제외
- 네트워크 경계, TLS, Ingress 인증 프록시 등 인프라 보안 전체의 대체
## 제약 조건
- 인증 검사는 `dashboard/app.py`에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.
- 단일 관리자 ID와 평문 password secret은 배포 secret이 주입하는 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`에서만 읽고 Streamlit secrets로 fallback하지 않는다. 두 값 중 하나라도 없거나 비어 있거나 읽을 수 없으면 fail-closed하며, password는 애플리케이션에서 constant-time 비교해야 한다.
- 비밀번호·token·hash 등 credential material을 로그, 오류 메시지, URL/query parameter, Issue 또는 repository 문서에 평문으로 노출하지 않아야 한다.
- 인증 상태는 현재 Streamlit browser session에서만 유지하며 명시적 로그아웃 시 폐기하고 새 session에는 재인증을 요구해야 한다.
- 실패 로그인 반복 시도에 대한 지연·잠금·rate-limit 정책은 OQ-007에서 확정하며, 구현 에이전트가 임의의 임계값이나 잠금 범위를 선택해서는 안 된다.
- Dashboard 인증만 구현하며 MCP endpoint가 보호된다고 간주해서는 안 된다. MCP 인증은 별도 Issue와 requirement에서 다룬다.
- 현재 배포는 동일 이미지의 MCP·Dashboard 컨테이너와 공유 PVC, 단일 replica를 사용한다. 인증 변경은 이 저장 구조를 불필요하게 바꾸지 않아야 한다.
- 테스트는 실제 운영 금융 DB나 실제 credential을 사용하지 않고 synthetic fixture와 test-only secret을 사용해야 한다.
- 확정된 결정을 gitea-agent trusted requirements bundle과 gitea-agent.requirement/v1 계약에 따라 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.
## 확정된 결정
- `genichin/jarca-ledger-mcp` Issue #1은 관리자 인증 기능 추가를 요구한다.
- Discovery 근거 revision은 `main`의 `069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6`으로 고정한다.
- 인증 전에는 로그인 화면만 표시하고 모든 Dashboard 페이지의 조회·쓰기·import를 차단한다.
- 배포 secret이 주입하는 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`만 credential source로 사용하며 Streamlit secrets로 fallback하지 않는다.
- `JARCA_DASHBOARD_ADMIN_PASSWORD`에는 평문 password secret을 주입하고 애플리케이션에서 constant-time 비교한다.
- 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃을 제공하며 새 session에는 재인증을 요구한다.
- 이번 범위는 Streamlit Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
## 저장소 근거
- `dashboard/app.py`: `main()`이 `get_db_path()`, UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.
- `dashboard/common.py`: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은 `transactions_gateway.update_ledger_transaction()`을 호출하고 선택적으로 분류 규칙도 저장한다.
- `dashboard/page_views/accounts.py`: 계좌 변경 form과 `변경 저장` 동작이 존재한다.
- `dashboard/page_views/imports.py`: 금융 PDF upload, import 내역 조회, `rollback_ledger_import_batch()` 호출과 hard-delete 선택지가 존재한다.
- `README.md`: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.
- `docker-compose.yml`: Dashboard는 `40059:8501`로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다.
- `.env.example`: 현재 예시 설정은 `JARCA_LEDGER_DB_PATH`만 제공한다.
- `pyproject.toml`: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.
- `tests/test_dashboard_app.py`: `streamlit.testing.v1.AppTest`와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.
- 위 근거는 모두 pinned SHA `069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6`에서 확인했다.
## 문서 영향 및 산출물
### 요구사항명세서
- 조치: 신규 작성
- 문서 ID: `REQ-001`
- 경로: `docs/requirements/req-001_dashboard-administrator-authentication.md`
- 제목: Dashboard 관리자 인증
- 변경 내용: 관리자 인증 기능을 판정 가능한 요구사항과 acceptance criteria로 정의한다.
- 포함할 내용:
- 모든 Dashboard 페이지의 인증 전 로그인 화면 전용 렌더링과 page dispatch·DB 조회·gateway 호출 차단 계약
- 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`만 사용하는 credential source 계약과 Streamlit secrets fallback 금지
- 평문 password secret의 constant-time 비교와 credential material 비보존 계약
- OQ-007에서 확정할 실패 로그인 반복 시도 제어, 적용 범위, 임계값, 해제 조건과 사용자 관찰 가능 동작
- 단일 관리자 ID·password 입력, secret 비노출 및 설정 누락·빈 값·읽기 오류·형식 오류 시 fail-closed 계약
- 현재 Streamlit browser session 범위의 인증 상태, 새 session 재인증 조건과 명시적 로그아웃 동작
- Dashboard만 보호하고 MCP 인증을 별도 Issue와 requirement로 분리하는 경계
- 인증 성공·실패·설정 오류의 사용자 관찰 가능 동작과 실패 시 상태 무변경 조건
- 기존 Dashboard 기능의 인증 후 호환성 및 배포 설정·secret 주입 계약
- 미인증·설정 오류·잘못된 credential 상태에서 `get_db_path()`·page dispatch·DB·gateway·upload·rollback을 호출하지 않고 query parameter로 우회할 수 없다는 acceptance criteria
- 정상 인증 후 기존 Dashboard 페이지 접근, 같은 browser session의 rerun에서 인증 유지, 로그아웃 직후 상태 폐기, 새 session 재인증에 대한 acceptance criteria
- credential 원문 또는 verifier를 화면·로그·traceback·URL/query parameter·`st.session_state`에 노출하지 않는 acceptance criteria
- 근거 Issue `#1`, 관련 설계 문서와 구현 PR `미정`의 추적성
- 제외할 내용:
- 확정 범위 밖의 다중 사용자·역할 기반 권한·사용자별 데이터 분리
- SQLite 가계부 도메인 스키마 변경과 자격 증명의 repository 평문 저장
- 별도 Issue와 requirement로 분리된 MCP 인증, TLS, Ingress 또는 네트워크 경계 전체의 구현
- 근거: pinned source revision에 할당된 요구사항 문서가 없으므로 첫 요구사항 ID는 `REQ-001`이다. Issue #1과 `dashboard/app.py`, `.env.example`, `docker-compose.yml`, `tests/test_dashboard_app.py`가 위 계약의 필요성을 뒷받침한다.
### 데이터 모델
- 조치: 영향 없음
- 경로: `docs/data-model.md`
- 변경 내용: 단일 관리자 자격 증명은 배포 secret이 주입하는 환경변수에서 읽고 영속 사용자 계정을 도입하지 않는다. 가계부 엔터티·필드·관계·불변조건과 SQLite schema 또는 migration을 변경하지 않으므로 데이터 모델 문서를 생성하지 않는다.
- 근거: 제외 범위가 SQLite 스키마와 가계부 도메인 모델 변경을 명시하며 pinned source revision에도 `docs/data-model.md`가 없다.
### 인터페이스 계약
- 조치: 신규 작성
- 경로: `docs/interface-contract.md`
- 변경 내용: 환경변수 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`만 사용하는 credential 입력, Streamlit secrets fallback 금지, 평문 password secret의 constant-time 비교, 인증 결과와 현재 browser session 상태 출력, 인증·로그아웃 operation, OQ-007에서 확정한 실패 로그인 반복 시도 제어와 오류, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 별도 범위인 MCP 보호 경계를 정의한다.
- 근거: `dashboard/app.py`에는 인증 gate가 없고 `.env.example`과 `docker-compose.yml`에는 인증 입력·설정 계약이 없으며 pinned source revision에도 `docs/interface-contract.md`가 없다.
### 프로젝트 구조
- 조치: 신규 작성
- 경로: `docs/project-structure.md`
- 변경 내용: `dashboard/app.py`가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를 `dashboard/` 내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임, `tests/`의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.
- 근거: 현재 `dashboard/app.py`가 DB 경로 취득과 page dispatch를 직접 시작하고 `dashboard/page_views/`와 `tests/test_dashboard_app.py`가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는 `docs/project-structure.md`가 없다.
### 런타임 흐름
- 조치: 신규 작성
- 경로: `docs/runtime-flow.md`
- 변경 내용: Streamlit session 시작, 환경변수 credential 로드와 설정 검증, 평문 password secret의 constant-time 비교, 설정 오류의 즉시 중단, 미인증 상태의 로그인 화면 전용 렌더링과 `get_db_path()`·DB·gateway·upload·rollback 미호출, credential 검증 성공·실패 전이, OQ-007에서 확정한 반복 실패 제어와 해제 전이, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.
- 근거: 현재 `dashboard/app.py`는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도 `docs/runtime-flow.md`가 없다.
### 기타 구현 산출물
- `README.md` 갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다.
- `.env.example` 갱신: 실제 secret 값 없이 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`의 이름과 용도를 문서화한다.
- `docker-compose.yml` 갱신: 배포 secret의 두 환경변수를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.
- Dashboard 인증 gate 및 관련 테스트: 정상 인증, 잘못된 credential, 설정 누락, logout, 인증 전 DB·gateway 미호출과 OQ-007에서 확정한 반복 실패 제어를 검증한다.
- 운영 배포 manifest 또는 runbook은 repository 내 관리 위치가 확인되고 선택된 인증 방식에 영향이 있을 때만 갱신한다.
## 미해결 질문
### OQ-007 — 실패 로그인 반복 시도를 어떻게 제어할 것인가?
현재 Discovery는 잘못된 credential의 fail-closed 동작만 정의하고 반복 실패의 지연, 잠금, 적용 범위와 해제 조건은 정하지 않았다. 현재 배포는 단일 replica이지만 새 browser session으로 session-local 상태를 우회할 수 있고, 전역 잠금은 공격자가 관리자를 잠그는 denial-of-service를 만들 수 있으므로 구현 전에 정책을 명시적으로 선택해야 한다.
- [x] 매 실패 응답을 최소 1초 지연하고 애플리케이션 잠금은 두지 않으며, 이 제한의 우회 가능성과 배포 경계에서 별도 rate limiting이 필요함을 운영 문서에 명시한다. (추천)
- [ ] 서버 process 전체에서 관리자 ID 기준 5회 연속 실패 시 15분 잠그고, 정상 인증 시 실패 횟수를 초기화한다.
- [ ] 현재 browser session에서 5회 연속 실패 시 해당 session을 15분 잠그고, 새 session 우회 가능성을 운영 문서에 명시한다.
- [ ] 애플리케이션 수준의 지연·잠금·rate limiting을 두지 않고 brute-force 위험을 명시적으로 수용한다.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
요약
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret이 주입하는 환경변수
JARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD에서 단일 관리자 자격 증명을 읽고, password secret을 애플리케이션에서 constant-time 비교한다. 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 실패 로그인 응답은 최소 1초 지연하되 애플리케이션 잠금은 두지 않고 우회 가능성과 배포 경계의 별도 rate limiting 필요성을 운영 문서에 명시한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.문제
현재
dashboard/app.py의main()은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다.docker-compose.yml은 Dashboard를 호스트40059에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고,.env.example에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.포함 범위
제외 범위
제약 조건
dashboard/app.py에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.JARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD에서만 읽고 Streamlit secrets로 fallback하지 않는다. 두 값 중 하나라도 없거나 비어 있거나 읽을 수 없으면 fail-closed하며, password는 애플리케이션에서 constant-time 비교해야 한다.확정된 결정
genichin/jarca-ledger-mcpIssue #1은 관리자 인증 기능 추가를 요구한다.main의069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6으로 고정한다.JARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD만 credential source로 사용하며 Streamlit secrets로 fallback하지 않는다.JARCA_DASHBOARD_ADMIN_PASSWORD에는 평문 password secret을 주입하고 애플리케이션에서 constant-time 비교한다.저장소 근거
dashboard/app.py:main()이get_db_path(), UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.dashboard/common.py: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은transactions_gateway.update_ledger_transaction()을 호출하고 선택적으로 분류 규칙도 저장한다.dashboard/page_views/accounts.py: 계좌 변경 form과변경 저장동작이 존재한다.dashboard/page_views/imports.py: 금융 PDF upload, import 내역 조회,rollback_ledger_import_batch()호출과 hard-delete 선택지가 존재한다.README.md: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.docker-compose.yml: Dashboard는40059:8501로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다..env.example: 현재 예시 설정은JARCA_LEDGER_DB_PATH만 제공한다.pyproject.toml: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.tests/test_dashboard_app.py:streamlit.testing.v1.AppTest와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6에서 확인했다.문서 영향 및 산출물
요구사항명세서
REQ-001docs/requirements/req-001_dashboard-administrator-authentication.mdJARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD만 사용하는 credential source 계약과 Streamlit secrets fallback 금지get_db_path()·page dispatch·DB·gateway·upload·rollback을 호출하지 않고 query parameter로 우회할 수 없다는 acceptance criteriast.session_state에 노출하지 않는 acceptance criteria#1, 관련 설계 문서와 구현 PR미정의 추적성REQ-001이다. Issue #1과dashboard/app.py,.env.example,docker-compose.yml,tests/test_dashboard_app.py가 위 계약의 필요성을 뒷받침한다.데이터 모델
docs/data-model.mddocs/data-model.md가 없다.인터페이스 계약
docs/interface-contract.mdJARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD만 사용하는 credential 입력, Streamlit secrets fallback 금지, 평문 password secret의 constant-time 비교, 인증 결과와 현재 browser session 상태 출력, 인증·로그아웃 operation, 잘못된 관리자 ID 또는 password의 실패 응답별 최소 1초 지연과 일반 오류, 애플리케이션 잠금 없음, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 별도 범위인 MCP 보호 경계를 정의한다.dashboard/app.py에는 인증 gate가 없고.env.example과docker-compose.yml에는 인증 입력·설정 계약이 없으며 pinned source revision에도docs/interface-contract.md가 없다.프로젝트 구조
docs/project-structure.mddashboard/app.py가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를dashboard/내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임,tests/의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.dashboard/app.py가 DB 경로 취득과 page dispatch를 직접 시작하고dashboard/page_views/와tests/test_dashboard_app.py가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는docs/project-structure.md가 없다.런타임 흐름
docs/runtime-flow.mdget_db_path()·DB·gateway·upload·rollback 미호출, credential 검증 성공·실패 전이, 각 실패 제출부터 일반 오류 응답 완료까지 최소 1초를 보장하는 지연, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.dashboard/app.py는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도docs/runtime-flow.md가 없다.기타 구현 산출물
README.md갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이, 애플리케이션 잠금이 없고 1초 지연은 새 session·병렬 요청으로 우회될 수 있으므로 배포 경계에 별도 rate limiting이 필요하다는 운영 제약을 설명한다..env.example갱신: 실제 secret 값 없이JARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD의 이름과 용도를 문서화한다.docker-compose.yml갱신: 배포 secret의 두 환경변수를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.미해결 질문
없음
Discovery 작성 전 이슈 원본
관리자 인증 기능을 추가한다.
Discovery 검토 전 사용자 수정본
요약
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 현재 Dashboard는 인증 절차 없이 SQLite 데이터를 직접 읽고 쓰며 호스트 포트
40059로 노출될 수 있으므로, 화면 렌더링과 DB 접근보다 앞선 공통 인증 경계가 필요하다. 구체적인 보호 범위, 자격 증명 방식, 세션 정책과 MCP 적용 여부는 아래 미해결 질문에서 결정한다.문제
현재
dashboard/app.py의main()은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다.docker-compose.yml은 Dashboard를 호스트40059에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고,.env.example에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.포함 범위
제외 범위
제약 조건
dashboard/app.py에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행되어야 한다.docs/requirements/TEMPLATE.md형식의 판정 가능한 requirement 및 acceptance criteria로 내려야 한다.확정된 결정
genichin/jarca-ledger-mcpIssue #1은 관리자 인증 기능 추가를 요구한다.main의fc9ada51d6ee67fbe7a4144361608c1096227720으로 고정한다.저장소 근거
dashboard/app.py:main()이get_db_path(), UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.dashboard/common.py: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은transactions_gateway.update_ledger_transaction()을 호출하고 선택적으로 분류 규칙도 저장한다.dashboard/page_views/accounts.py: 계좌 변경 form과변경 저장동작이 존재한다.dashboard/page_views/imports.py: 금융 PDF upload, import 내역 조회,rollback_ledger_import_batch()호출과 hard-delete 선택지가 존재한다.README.md: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.docker-compose.yml: Dashboard는40059:8501로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다..env.example: 현재 예시 설정은JARCA_LEDGER_DB_PATH만 제공한다.pyproject.toml: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.tests/test_dashboard_app.py:streamlit.testing.v1.AppTest와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.docs/requirements/README.md및docs/requirements/TEMPLATE.md: requirement metadata,REQ-NNN.xx식별자, acceptance criterion과 verification/evidence 구조를 정의한다.fc9ada51d6ee67fbe7a4144361608c1096227720에서 확인했다.문서 영향 및 산출물
요구사항명세서
REQ-001docs/requirements/req-001_dashboard-administrator-authentication.md#1, 관련 설계 문서와 구현 PR미정의 추적성docs/requirements/에는 README와 TEMPLATE만 있어 할당된 요구사항 문서가 없으므로 첫 ID는REQ-001이다. Issue #1과dashboard/app.py,.env.example,docker-compose.yml,tests/test_dashboard_app.py가 위 계약의 필요성을 뒷받침한다.데이터 모델
docs/data-model.mddocs/data-model.md가 없다.인터페이스 계약
docs/interface-contract.mddashboard/app.py에는 인증 gate가 없고.env.example과docker-compose.yml에는 인증 입력·설정 계약이 없으며 pinned source revision에도docs/interface-contract.md가 없다.프로젝트 구조
docs/project-structure.mddashboard/app.py가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를dashboard/내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임,tests/의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.dashboard/app.py가 DB 경로 취득과 page dispatch를 직접 시작하고dashboard/page_views/와tests/test_dashboard_app.py가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는docs/project-structure.md가 없다.런타임 흐름
docs/runtime-flow.mddashboard/app.py는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도docs/runtime-flow.md가 없다.기타 구현 산출물
README.md갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다..env.example갱신: 실제 secret 값 없이 선택된 인증 설정의 변수 이름과 용도를 문서화한다.docker-compose.yml갱신: 선택된 credential source를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.미해결 질문
OQ-001 — Dashboard의 보호 범위를 어디까지 적용할 것인가?
Dashboard에는 금융 데이터 조회 화면과 거래·계좌 변경, import rollback 같은 쓰기 기능이 함께 있다. 조회만 허용하면 금융 정보가 노출되고, 쓰기 작업만 보호하면 공통 진입점이 아닌 페이지별 분기가 필요하다.
OQ-002 — 관리자 자격 증명을 어떤 방식으로 제공할 것인가?
현재 저장소에는 사용자 모델이나 인증 dependency가 없으며 Issue는 단수 관리자 인증을 요청한다. 최소 변경은 배포 secret에서 단일 관리자 자격 증명을 읽는 방식이지만, 외부 IdP 또는 reverse proxy가 운영 표준이라면 경계를 애플리케이션 밖에 둘 수 있다.
OQ-003 — 인증 세션과 로그아웃 정책은 어떻게 할 것인가?
Streamlit rerun 동안 인증 상태를 유지할 범위와 세션 종료 조건이 정해져야 테스트 가능한 계약을 만들 수 있다. 브라우저를 넘는 장기 세션은 cookie 보호·만료·폐기 설계가 추가로 필요하다.
OQ-004 — MCP endpoint도 이번 요구사항에서 인증할 것인가?
Dashboard와 MCP는 같은 SQLite 파일을 공유하지만 별도 서비스와 포트로 동작한다. Dashboard의 로그인 gate는 MCP
8000endpoint를 보호하지 않는다.Discovery 검토 전 사용자 수정본
요약
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret 또는 Streamlit secrets에서 단일 관리자 ID와 검증용 password secret을 주입해 애플리케이션에서 검증하며, 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다.
문제
현재
dashboard/app.py의main()은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다.docker-compose.yml은 Dashboard를 호스트40059에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고,.env.example에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.포함 범위
제외 범위
제약 조건
dashboard/app.py에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.확정된 결정
genichin/jarca-ledger-mcpIssue #1은 관리자 인증 기능 추가를 요구한다.main의069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6으로 고정한다.저장소 근거
dashboard/app.py:main()이get_db_path(), UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.dashboard/common.py: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은transactions_gateway.update_ledger_transaction()을 호출하고 선택적으로 분류 규칙도 저장한다.dashboard/page_views/accounts.py: 계좌 변경 form과변경 저장동작이 존재한다.dashboard/page_views/imports.py: 금융 PDF upload, import 내역 조회,rollback_ledger_import_batch()호출과 hard-delete 선택지가 존재한다.README.md: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.docker-compose.yml: Dashboard는40059:8501로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다..env.example: 현재 예시 설정은JARCA_LEDGER_DB_PATH만 제공한다.pyproject.toml: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.tests/test_dashboard_app.py:streamlit.testing.v1.AppTest와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6에서 확인했다.문서 영향 및 산출물
요구사항명세서
REQ-001docs/requirements/req-001_dashboard-administrator-authentication.md#1, 관련 설계 문서와 구현 PR미정의 추적성없으므로 첫 요구사항 ID는 REQ-001이다. Issue #1과
dashboard/app.py,.env.example,docker-compose.yml,tests/test_dashboard_app.py가 위 계약의 필요성을 뒷받침한다.데이터 모델
docs/data-model.mddocs/data-model.md가 없다.인터페이스 계약
docs/interface-contract.mddashboard/app.py에는 인증 gate가 없고.env.example과docker-compose.yml에는 인증 입력·설정 계약이 없으며 pinned source revision에도docs/interface-contract.md가 없다.프로젝트 구조
docs/project-structure.mddashboard/app.py가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를dashboard/내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임,tests/의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.dashboard/app.py가 DB 경로 취득과 page dispatch를 직접 시작하고dashboard/page_views/와tests/test_dashboard_app.py가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는docs/project-structure.md가 없다.런타임 흐름
docs/runtime-flow.mddashboard/app.py는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도docs/runtime-flow.md가 없다.기타 구현 산출물
README.md갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다..env.example갱신: 실제 secret 값 없이 선택된 인증 설정의 변수 이름과 용도를 문서화한다.docker-compose.yml갱신: 선택된 credential source를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.미해결 질문
없음
Discovery 검토 전 사용자 수정본
요약
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret 또는 Streamlit secrets에서 단일 관리자 ID와 password secret을 주입해 애플리케이션에서 검증하며, 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다. Credential source·우선순위와 password verifier 표현은 OQ-005와 OQ-006에서 확정한다.
문제
현재
dashboard/app.py의main()은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다.docker-compose.yml은 Dashboard를 호스트40059에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고,.env.example에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.포함 범위
제외 범위
제약 조건
dashboard/app.py에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.확정된 결정
genichin/jarca-ledger-mcpIssue #1은 관리자 인증 기능 추가를 요구한다.main의069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6으로 고정한다.저장소 근거
dashboard/app.py:main()이get_db_path(), UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.dashboard/common.py: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은transactions_gateway.update_ledger_transaction()을 호출하고 선택적으로 분류 규칙도 저장한다.dashboard/page_views/accounts.py: 계좌 변경 form과변경 저장동작이 존재한다.dashboard/page_views/imports.py: 금융 PDF upload, import 내역 조회,rollback_ledger_import_batch()호출과 hard-delete 선택지가 존재한다.README.md: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.docker-compose.yml: Dashboard는40059:8501로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다..env.example: 현재 예시 설정은JARCA_LEDGER_DB_PATH만 제공한다.pyproject.toml: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.tests/test_dashboard_app.py:streamlit.testing.v1.AppTest와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6에서 확인했다.문서 영향 및 산출물
요구사항명세서
REQ-001docs/requirements/req-001_dashboard-administrator-authentication.mdget_db_path()·page dispatch·DB·gateway·upload·rollback을 호출하지 않고 query parameter로 우회할 수 없다는 acceptance criteriast.session_state에 노출하지 않는 acceptance criteria#1, 관련 설계 문서와 구현 PR미정의 추적성REQ-001이다. Issue #1과dashboard/app.py,.env.example,docker-compose.yml,tests/test_dashboard_app.py가 위 계약의 필요성을 뒷받침한다.데이터 모델
docs/data-model.mddocs/data-model.md가 없다.인터페이스 계약
docs/interface-contract.mddashboard/app.py에는 인증 gate가 없고.env.example과docker-compose.yml에는 인증 입력·설정 계약이 없으며 pinned source revision에도docs/interface-contract.md가 없다.프로젝트 구조
docs/project-structure.mddashboard/app.py가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를dashboard/내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임,tests/의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.dashboard/app.py가 DB 경로 취득과 page dispatch를 직접 시작하고dashboard/page_views/와tests/test_dashboard_app.py가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는docs/project-structure.md가 없다.런타임 흐름
docs/runtime-flow.mdget_db_path()·DB·gateway·upload·rollback 미호출, credential 검증 성공·실패 전이, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.dashboard/app.py는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도docs/runtime-flow.md가 없다.기타 구현 산출물
README.md갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다..env.example갱신: 실제 secret 값 없이 OQ-005에서 선택된 환경변수 source가 있을 때 exact key와 용도를 문서화한다.docker-compose.yml갱신: OQ-005에서 선택된 credential source를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.미해결 질문
OQ-005 — 관리자 credential의 source와 우선순위를 어떻게 고정할 것인가?
현재 선택은 배포 secret 또는 Streamlit secrets를 허용하지만 exact key, 동시 설정과 fallback 계약이 없다. 구현·Compose·로컬 실행·테스트가 같은 설정 인터페이스를 사용하도록 하나를 확정해야 한다.
JARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD만 사용한다. (추천)auth.admin_id와auth.admin_password만 사용한다.OQ-006 — Password secret을 어떤 표현으로 검증할 것인가?
평문 password secret과 encoded verifier는 배포 방법·dependency·검증 및 오류 계약이 다르다. 어느 경우에도 credential 원문이나 verifier를 repository, 로그, URL 또는
st.session_state에 저장하지 않는다.Discovery 검토 전 사용자 수정본
요약
Streamlit Dashboard에 관리자 인증을 도입해 인증되지 않은 사용자가 가계부 데이터와 운영 기능에 접근하지 못하도록 한다. 모든 Dashboard 페이지는 인증 전 로그인 화면만 표시하고 조회·쓰기·import를 차단한다. 배포 secret이 주입하는 환경변수
JARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD에서 단일 관리자 자격 증명을 읽고, password secret을 애플리케이션에서 constant-time 비교한다. 인증 상태는 현재 Streamlit browser session에서만 유지하고 명시적 로그아웃과 새 session 재인증을 제공한다. 이번 범위는 Dashboard로 한정하고 MCP 인증은 별도 Issue와 requirement로 분리한다. 실패 로그인 반복 시도 제어는 OQ-007에서 확정한다.문제
현재
dashboard/app.py의main()은 DB 경로를 얻고 페이지를 즉시 구성·dispatch하며 인증 여부를 확인하지 않는다. Dashboard는 금융 데이터를 표시할 뿐 아니라 거래 분류 저장, 계좌 변경, PDF import, import batch rollback 같은 상태 변경 작업도 수행한다.docker-compose.yml은 Dashboard를 호스트40059에 연결하지만 인증 관련 환경변수나 secret 계약은 정의하지 않고,.env.example에도 DB 경로만 있다. 이 상태에서 Dashboard가 신뢰 경계 밖에 노출되면 민감 데이터 조회와 변경 작업을 제한할 애플리케이션 수준의 통제가 없다.포함 범위
제외 범위
제약 조건
dashboard/app.py에서 어떤 페이지를 dispatch하거나 DB-backed 화면을 렌더링하기 전에 수행하며, 인증 전에는 로그인 화면만 표시해야 한다.JARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD에서만 읽고 Streamlit secrets로 fallback하지 않는다. 두 값 중 하나라도 없거나 비어 있거나 읽을 수 없으면 fail-closed하며, password는 애플리케이션에서 constant-time 비교해야 한다.확정된 결정
genichin/jarca-ledger-mcpIssue #1은 관리자 인증 기능 추가를 요구한다.main의069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6으로 고정한다.JARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD만 credential source로 사용하며 Streamlit secrets로 fallback하지 않는다.JARCA_DASHBOARD_ADMIN_PASSWORD에는 평문 password secret을 주입하고 애플리케이션에서 constant-time 비교한다.저장소 근거
dashboard/app.py:main()이get_db_path(), UI 구성, 페이지 dispatch를 수행하지만 인증 gate가 없다. 페이지 목록에는 요약, 자산/마감, 거래내역, 분류 규칙, 카테고리, 계좌, Import 등이 포함된다.dashboard/common.py: Dashboard가 Streamlit gateway를 통해 조회·수정 서비스를 호출하고 SQLite-backed 데이터를 렌더링한다. 거래 수정 form은transactions_gateway.update_ledger_transaction()을 호출하고 선택적으로 분류 규칙도 저장한다.dashboard/page_views/accounts.py: 계좌 변경 form과변경 저장동작이 존재한다.dashboard/page_views/imports.py: 금융 PDF upload, import 내역 조회,rollback_ledger_import_batch()호출과 hard-delete 선택지가 존재한다.README.md: Dashboard가 실제 기본 DB를 읽고 쓰며, k3s에서는 MCP와 Dashboard가 동일 PVC의 SQLite 파일을 공유한다고 설명한다. 외부 공개 시 일반적으로 frontend만 Ingress 또는 NodePort에 연결한다고 명시한다.docker-compose.yml: Dashboard는40059:8501로 노출되고 DB 경로는 전달하지만 인증 관련 환경변수는 없다..env.example: 현재 예시 설정은JARCA_LEDGER_DB_PATH만 제공한다.pyproject.toml: Dashboard optional dependency는 Streamlit·pandas·plotly이며 별도 인증 library는 선언되어 있지 않다.tests/test_dashboard_app.py:streamlit.testing.v1.AppTest와 synthetic SQLite fixture로 주요 페이지를 검증하는 기존 테스트 기반이 있다.069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6에서 확인했다.문서 영향 및 산출물
요구사항명세서
REQ-001docs/requirements/req-001_dashboard-administrator-authentication.mdJARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD만 사용하는 credential source 계약과 Streamlit secrets fallback 금지get_db_path()·page dispatch·DB·gateway·upload·rollback을 호출하지 않고 query parameter로 우회할 수 없다는 acceptance criteriast.session_state에 노출하지 않는 acceptance criteria#1, 관련 설계 문서와 구현 PR미정의 추적성REQ-001이다. Issue #1과dashboard/app.py,.env.example,docker-compose.yml,tests/test_dashboard_app.py가 위 계약의 필요성을 뒷받침한다.데이터 모델
docs/data-model.mddocs/data-model.md가 없다.인터페이스 계약
docs/interface-contract.mdJARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD만 사용하는 credential 입력, Streamlit secrets fallback 금지, 평문 password secret의 constant-time 비교, 인증 결과와 현재 browser session 상태 출력, 인증·로그아웃 operation, OQ-007에서 확정한 실패 로그인 반복 시도 제어와 오류, 설정 누락·잘못된 credential의 fail-closed 오류와 무변경 조건, 재시도·중복 제출, 인증 후 기존 Dashboard 호출 호환성 및 별도 범위인 MCP 보호 경계를 정의한다.dashboard/app.py에는 인증 gate가 없고.env.example과docker-compose.yml에는 인증 입력·설정 계약이 없으며 pinned source revision에도docs/interface-contract.md가 없다.프로젝트 구조
docs/project-structure.mddashboard/app.py가 공개 Dashboard 진입점으로서 DB 경로 취득과 page dispatch 전에 인증 경계를 호출하는 책임, 인증 구성요소를dashboard/내부에 배치하고 가계부 persistence·gateway와 분리하는 원칙, 설정·Compose·README의 책임,tests/의 인증 테스트 대응 경로, 인증 경계에서 기존 page view 방향으로만 허용되는 의존과 역방향 의존 금지를 정의한다.dashboard/app.py가 DB 경로 취득과 page dispatch를 직접 시작하고dashboard/page_views/와tests/test_dashboard_app.py가 각각 화면 구현과 진입점 검증을 담당하지만 pinned source revision에는docs/project-structure.md가 없다.런타임 흐름
docs/runtime-flow.mdget_db_path()·DB·gateway·upload·rollback 미호출, credential 검증 성공·실패 전이, OQ-007에서 확정한 반복 실패 제어와 해제 전이, 인증 후 기존 page dispatch, 로그아웃 시 인증 상태 폐기와 재인증 전환을 정의한다. 실패 시 추가 쓰기 중단, 사용자 재시도와 중복 제출 처리, browser session 간 상태 격리와 rerun 순서, credential을 제외한 인증 성공·실패·설정 오류 로그 및 상관 식별 기준도 포함한다.dashboard/app.py는 인증 확인 없이 DB-backed 화면으로 진입하고 Streamlit session·실패 복구·관찰 가능성 계약이 없으며 pinned source revision에도docs/runtime-flow.md가 없다.기타 구현 산출물
README.md갱신: 로컬 및 배포 환경의 관리자 인증 설정, fail-closed 동작, secret 주입 방법, Dashboard와 MCP 보호 범위의 차이를 설명한다..env.example갱신: 실제 secret 값 없이JARCA_DASHBOARD_ADMIN_ID와JARCA_DASHBOARD_ADMIN_PASSWORD의 이름과 용도를 문서화한다.docker-compose.yml갱신: 배포 secret의 두 환경변수를 Dashboard 컨테이너에 주입하되 secret 값을 repository에 저장하지 않는다.미해결 질문
OQ-007 — 실패 로그인 반복 시도를 어떻게 제어할 것인가?
현재 Discovery는 잘못된 credential의 fail-closed 동작만 정의하고 반복 실패의 지연, 잠금, 적용 범위와 해제 조건은 정하지 않았다. 현재 배포는 단일 replica이지만 새 browser session으로 session-local 상태를 우회할 수 있고, 전역 잠금은 공격자가 관리자를 잠그는 denial-of-service를 만들 수 있으므로 구현 전에 정책을 명시적으로 선택해야 한다.