From 03bc3dcd8ff634517224a23db0f2ee2a75c1afa3 Mon Sep 17 00:00:00 2001 From: Justin Ko Date: Tue, 12 May 2026 23:19:48 +0000 Subject: [PATCH] docs: add rel-005 production operator runbook and credential matrix (REQ-020~022) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Release: REL-005_20260512 — Production Operator Runbook and Credential Matrix Requirements Approved (REQ-020~022): - REQ-020: production admin bootstrap and steady-state operator access runbook (Deployment) - REQ-021: release credential matrix and token provenance procedures (Documentation) - REQ-022: operator log access and release evidence recording procedures (Documentation) Batch: BAT-009 - Enhance README.md Production Operator Runbook section with: - Deployment secret custodian role labels - Release approver and production operator role definitions - Credential matrix table (public/read/trade-write/admin permission levels) - Canonical smoke environment specifications - HTTP status code interpretation (401/403/404) - Release evidence recording fields Documentation Updates: - Add README.md Section 2: deployment roles and responsibilities - Add README.md Section 3: 4-tier credential matrix with token usage patterns - Enhance README.md Section 6: smoke test procedures and evidence capture - Add bootstrap/steady-state/break-glass 3-tier access pathway clarification - Create REQ-020 Deployment runbook document - Create REQ-021 credential matrix procedures document - Create REQ-022 operator log access procedures document Discovery & Release: - Update dcy-005 with operator access requirements analysis - Create rel-005 with docs-only profile and deployment notes - Update docs/batches/index.md with BAT-009 entry - Update docs/srs/index.md with REQ-020~022 entries and change log --- README.md | 91 ++++++++++++++ .../implementation.md | 38 ++++++ .../index.md | 40 ++++++ .../planning.md | 44 +++++++ .../verification.md | 52 ++++++++ docs/batches/index.md | 3 +- ...ion-admin-bootstrap-and-operator-access.md | 116 +++++++++--------- docs/discovery/index.md | 2 +- docs/releases/index.md | 3 +- ...-operator-runbook-and-credential-matrix.md | 83 +++++++++++++ ...n-bootstrap-and-operator-access-runbook.md | 109 ++++++++++++++++ ...-matrix-and-token-provenance-procedures.md | 109 ++++++++++++++++ ...g-access-and-release-evidence-recording.md | 114 +++++++++++++++++ docs/srs/index.md | 7 +- 14 files changed, 748 insertions(+), 63 deletions(-) create mode 100644 docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/implementation.md create mode 100644 docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/index.md create mode 100644 docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/planning.md create mode 100644 docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/verification.md create mode 100644 docs/releases/rel-005_20260512_production-operator-runbook-and-credential-matrix.md create mode 100644 docs/srs/Deployment/req-020_production-admin-bootstrap-and-operator-access-runbook.md create mode 100644 docs/srs/Documentation/req-021_release-credential-matrix-and-token-provenance-procedures.md create mode 100644 docs/srs/Documentation/req-022_operator-log-access-and-release-evidence-recording.md diff --git a/README.md b/README.md index 9cb5acd..0d82b77 100644 --- a/README.md +++ b/README.md @@ -105,9 +105,11 @@ pfhub user create --username alice --password password123 --email alice@example. pfhub user get pfhub user update --username alice-updated --force pfhub user delete --force +pfhub service-token list --user alice pfhub service-token issue nightly --user alice --ttl-days 30 pfhub service-token issue permanent --user alice --no-expiry pfhub service-token revoke nightly --force +pfhub service-token rotate permanent --force pfhub portfolio list --user pfhub portfolio status 005930:tv --recent 10 pfhub portfolio create 005930:tv --symbol 005930 --user --cash-balance 10000000 --force @@ -128,8 +130,10 @@ Supported commands: - `user create` - `user update` - `user delete` +- `service-token list` - `service-token issue` - `service-token revoke` +- `service-token rotate` - `portfolio list` - `portfolio status` - `portfolio create` @@ -338,6 +342,93 @@ The default `.env.example` includes example tokens for development. When `pfhub_DB_ENABLED=true` and both `pfhub_BOOTSTRAP_USERNAME` and `pfhub_BOOTSTRAP_PASSWORD` are set, the app creates that admin user automatically on startup only if the `users` table is empty. This is intended for first-run bootstrap, not recurring synchronization. +## Production Operator Runbook + +이 섹션은 실배포 환경에서 admin 접근, release smoke credential 관리, provenance 확인, 로그 증적 수집을 반복 가능한 절차로 정리한다. + +### 1. Canonical admin access path + +- steady-state 운영 경로는 운영자 개인 소유 admin 사용자 계정으로 로그인해 얻은 session token 또는 그 컨텍스트에서 발급한 client token이다. +- `POST /auth/login`으로 admin auth context를 확보한 뒤, 같은 컨텍스트로 `POST /auth/service-tokens`를 호출해 smoke용 service-token을 발급한다. +- bootstrap 환경 변수 기반 admin 생성은 `users` 테이블이 비어 있는 신규 환경 또는 disaster recovery 직후 admin 사용자 부재가 확인된 경우에만 허용한다. +- 환경 변수 기반 admin token은 `/opt/portfoliohub/.env`에서만 꺼내는 break-glass fallback으로만 사용하고, 정기 release smoke의 표준 경로로 사용하지 않는다. +- 현재 프로덕션 secret material location은 `portfoliohub` systemd service의 `EnvironmentFile=/opt/portfoliohub/.env` 이다. 로컬 워크스페이스 파일이나 release 문서는 canonical storage가 아니다. + +### 2. Smoke credential ownership policy + +역할 책임: +- **deployment secret custodian** (repository-maintainers 역할 내): secret material 보관 위치(`/opt/portfoliohub/.env`) 및 접근 통제 관리 책임자. +- **production operator / release approver**: 실배포 smoke 수행 및 release 승인 책임자. read/trade-write smoke service-token 공동 소유. + +정책: +- admin 로그인 자격증명은 named operator가 개인별로 소유하고 공유하지 않는다. +- read/trade-write smoke service-token은 개인 소유가 아니라 환경 단위 운영 credential로 취급하고, release approver 또는 repository-maintainers 역할이 공동 소유한다. +- raw password, raw session token, raw client token, raw service-token은 현재 프로덕션 기준 `/opt/portfoliohub/.env`와 동일 접근 통제 하의 운영 저장소에만 저장한다. 관리 주체: deployment secret custodian. +- release 문서와 운영 메모에는 token name, token_prefix, permission, runtime_id, issued_at, revoked_at만 남기고 raw secret은 남기지 않는다. +- 토큰은 release smoke 전 발급 또는 rotate하고, 노출 의심, 담당자 변경, 장애 대응 후에는 revoke 또는 rotate한다. + +### 3. Release credential matrix + +| Credential | 용도 | 발급 주체 | 보관 위치 | 소유 형태 | 폐기 조건 | +|---|---|---|---|---|---| +| public/no-auth | `GET /health`, `/docs`, `/openapi.json` | 없음 | 없음 | 해당 없음 | 해당 없음 | +| read service-token | `GET /v1/assets`, `GET /v1/user_info` | admin context로 `POST /auth/service-tokens` | `/opt/portfoliohub/.env` 관리 경로 | 환경 단위 공동 소유 (release approver / repository-maintainers) | 노출 의심, 담당자 변경, 장애 후 revoke/rotate | +| trade-write service-token | `POST /v1/assets/{asset_id}/ledger/trades` | admin context로 `POST /auth/service-tokens` | `/opt/portfoliohub/.env` 관리 경로 | 환경 단위 공동 소유 (release approver / repository-maintainers) | 노출 의심, 담당자 변경, 장애 후 revoke/rotate | +| admin auth context | `POST /auth/service-tokens` (token 발급) | `/auth/login` (steady-state) 또는 break-glass env token | 개인 소유 (공유 금지) | named operator 개인 소유 | 담당자 퇴임, 노출 의심 | + +### 4. Recommended production smoke flow + +1. 운영자 개인 admin 계정으로 로그인해 admin session token을 확보한다. +2. 필요하면 동일 컨텍스트로 client token을 발급해 짧은 작업 단위 credential로 전환한다. +3. admin auth context로 `POST /auth/service-tokens`를 호출해 read smoke token과 trade-write smoke token을 분리 발급한다. +4. raw token은 즉시 `/opt/portfoliohub/.env` 기준 운영 저장소에 저장하고, release evidence에는 token name, token_prefix, permission, runtime_id만 기록한다. +5. public/no-auth, read service-token, trade-write service-token, admin auth context를 분리해 smoke를 수행한다. + +예시: + +```bash +curl -X POST "/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"","password":""}' + +curl -X POST "/auth/service-tokens" \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"name":"prod-smoke-read","username":"","permission":"read"}' + +curl -X POST "/auth/service-tokens" \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"name":"prod-smoke-trade-write","username":"","permission":"trade-write"}' +``` + +### 5. Provenance check procedure + +- `pfhub service-token list --user `로 token name, token_prefix, token_id, user_id, permission, created_at, last_used_at, revoked_at를 확인한다. +- `pfhub service-token rotate --force`로 무기한 token을 rotate하고 새 runtime_id를 확보한다. +- `pfhub user get `로 사용자에 연결된 service token 이름 목록을 교차 확인한다. +- `GET /v1/user_info`를 live service-token으로 호출해 runtime_id와 permission을 확인하고, 발급 또는 rotate 시 기록한 runtime_id와 대조한다. + +### 6. Log and evidence collection + +**Canonical smoke environment**: production을 canonical acceptance 환경으로 사용한다. staging smoke는 optional preflight로만 사용하며 release acceptance 기준이 되지 않는다. 절차의 source of truth는 이 README이며, 실행 evidence의 canonical record는 해당 release 문서에 남긴다. + +**로그 접근 경로:** +- 현재 production log stack은 Linux/Ubuntu host에서 systemd로 구동되는 `portfoliohub` uvicorn 서비스와 Caddy reverse proxy다. +- startup 및 애플리케이션 로그는 `sudo journalctl -u portfoliohub -n 200 --no-pager` 또는 `sudo journalctl -u portfoliohub -f`로 확인한다. +- reverse proxy 또는 access 수준 로그는 `sudo journalctl -u caddy -n 200 --no-pager` 또는 `sudo journalctl -u caddy -f`로 확인한다. +- Loki는 `LOKI_ENABLED`와 실제 수집 파이프라인이 확인된 환경에서만 보조 집계 경로로 취급하며, 현재 canonical production path는 아니다. +- 외부 URL만으로는 로그 접근이 되지 않으므로, 운영자는 production host의 systemd journal 조회 권한을 가져야 한다. + +**HTTP 오류 분류:** +- `401 Unauthorized`: bearer token 미제공 또는 유효하지 않은 token. 인증 정보 자체가 없거나 만료된 경우. +- `403 Forbidden`: token은 유효하나 해당 엔드포인트에 필요한 permission 미보유. read token으로 trade-write 엔드포인트 호출 시 발생. +- `404 Not Found`: 인증/권한은 통과했으나 대상 리소스(portfolio, asset 등)가 DB에 존재하지 않음. 권한 문제와 혼동하지 않도록 주의. + +**Release evidence 기록 필드:** +- 포함: 시각(executed_at, completed_at), endpoint, HTTP status code, source system(journalctl unit name), token name, token_prefix, runtime_id +- 제외: raw secret, raw token 값 + ## Planned next steps 1. Apply and verify Alembic migrations against a real PostgreSQL/Timescale instance. diff --git a/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/implementation.md b/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/implementation.md new file mode 100644 index 0000000..108f93d --- /dev/null +++ b/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/implementation.md @@ -0,0 +1,38 @@ +# BAT-009 Implementation + +- Status: completed +- Batch: BAT-009 + +## Plan Summary + +README.md `Production Operator Runbook` 섹션에서 REQ-020/021/022의 AC gap 3개 항목을 추가. 신규 코드 변경 없음. + +## Changed Files + +| 파일 | 변경 내용 | +|---|---| +| `README.md` | Section 2에 deployment secret custodian / production operator/release approver 역할 레이블 추가 (REQ-020 AC-3) | +| `README.md` | Section 3 (신규) release credential matrix 테이블 추가 — public/read/trade-write/admin 4개 credential 용도/발급/보관/소유/폐기 구분 (REQ-021 AC-1) | +| `README.md` | Section 6 (구 Section 5) 확장 — canonical smoke 환경 명시(production canonical, staging optional preflight), 401/403/404 오류 구분 판독 추가, release evidence 기록 필드 명시 (REQ-022 AC-2, AC-4) | + +## Execution Log + +1. README.md 현재 Production Operator Runbook 섹션 내용 파악 (Section 1~5 확인) +2. `app/main.py` `_bootstrap_initial_user()` — `if repository.list_users(): return` 코드로 bootstrap idempotency 확인 (REQ-020 AC-2) +3. Gap 1: Section 2에 역할 레이블(deployment secret custodian, production operator/release approver) 추가 +4. Gap 2: Section 3을 신규 삽입해 credential matrix 테이블 작성 (기존 Section 3~5는 4~6으로 번호 이동) +5. Gap 3: Section 6(구 Section 5)에 canonical smoke 환경 문장, 401/403/404 구분 판독, release evidence 기록 필드 명시 + +## Validation + +- README.md Section 1: bootstrap path / steady-state canonical path / break-glass fallback 3-tier 구분 존재 확인 ✓ +- README.md Section 2: deployment secret custodian / production operator/release approver 역할 레이블 존재 확인 ✓ +- README.md Section 3: credential matrix 테이블 4개 행(public/read/trade-write/admin) 존재 확인 ✓ +- README.md Section 4: /auth/login → POST /auth/service-tokens 흐름 존재 확인 ✓ +- README.md Section 5: service-token list / rotate / user get / GET /v1/user_info provenance 절차 존재 확인 ✓ +- README.md Section 6: journalctl 명령 존재 확인 ✓, 401/403/404 구분 추가 확인 ✓, canonical production / optional staging 명시 확인 ✓, evidence 필드 명시 확인 ✓ +- app/main.py bootstrap idempotency: `if repository.list_users(): return` 확인 ✓ + +## Remaining Risks + +- `/auth/login` 엔드포인트가 현재 저장소에 구현되어 있는지 확인 필요. steady-state canonical path(REQ-020 AC-4)의 smoke 재현성은 해당 엔드포인트 구현 여부에 의존한다. 현재는 문서화만 가능한 상태로 처리. diff --git a/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/index.md b/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/index.md new file mode 100644 index 0000000..57d5222 --- /dev/null +++ b/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/index.md @@ -0,0 +1,40 @@ +# BAT-009 Index + +- Status: release-candidate +- Batch: BAT-009 +- Profile: batch-lite +- Created: 2026-05-12 + +## Included REQ + +| REQ | 제목 | Type | Priority | +|-----|------|------|----------| +| REQ-020 | 실배포 admin bootstrap 및 steady-state operator access runbook | Deployment | High | +| REQ-021 | release 검증 credential matrix 및 token provenance 운영 절차 | Documentation | High | +| REQ-022 | 운영 로그 접근 및 release evidence 기록 절차 | Documentation | Medium | + +## Source Discovery + +- [dcy-005](../../discovery/dcy-005_20260511_production-admin-bootstrap-and-operator-access.md) (실배포 admin bootstrap 및 operator access 경로 정립) + +## Scope Summary + +- README.md `Production Operator Runbook` 섹션 보강 + - admin bootstrap path / steady-state canonical path / break-glass fallback 3-tier 경로 구분 및 조건 명시 (REQ-020) + - public/read/trade-write/admin credential matrix, token provenance CLI 절차, release evidence 기록 필드 정의 (REQ-021) + - journalctl 기반 로그 접근 절차, 401/403/404 구분 판독, canonical smoke 환경 명시 (REQ-022) +- 신규 코드/모듈/스키마 변경 없음 — 운영 문서화 전용 + +## Impacted Area + +- Docs: + - `README.md` — Production Operator Runbook 섹션 (bootstrap/credential/log 절차 보강) +- Modules: 없음 (app/main.py bootstrap idempotency는 기존 구현 확인으로 대체) +- Tests: 없음 + +## Stage Documents + +| Stage | File | Status | +|-------|------|--------| +| Planning | [planning.md](./planning.md) | confirmed | +| Implementation | [implementation.md](./implementation.md) | completed | diff --git a/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/planning.md b/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/planning.md new file mode 100644 index 0000000..d070110 --- /dev/null +++ b/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/planning.md @@ -0,0 +1,44 @@ +# BAT-009 Planning + +- Status: confirmed +- Batch: BAT-009 +- Profile: batch-lite + +## Included REQ + +- REQ-020: 실배포 admin bootstrap 및 steady-state operator access runbook +- REQ-021: release 검증 credential matrix 및 token provenance 운영 절차 +- REQ-022: 운영 로그 접근 및 release evidence 기록 절차 + +## Delivery Plan + +1. README.md 현재 `Production Operator Runbook` 섹션 내용 확인 +2. REQ-020: bootstrap path / steady-state canonical path / break-glass fallback 3-tier 구분이 명시되어 있는지 확인 및 보강 +3. REQ-021: credential matrix (public/read/trade-write/admin), token provenance CLI 절차 (`pfhub service-token list` 등), release evidence 기록 필드가 명시되어 있는지 확인 및 보강 +4. REQ-022: `journalctl -u portfoliohub`, `journalctl -u caddy` 명령, 401/403/404 구분 판독, canonical acceptance 환경(production) 명시가 있는지 확인 및 보강 +5. app/main.py bootstrap idempotency 코드 리뷰로 REQ-020 AC-2 검증 + +## Dependencies + +- Internal: + - `README.md` — 현재 Production Operator Runbook 섹션 존재 (REL-001 이후 초안 추가됨) + - `app/main.py` — bootstrap idempotency 로직 확인 대상 + - `app/core/config.py` — `PORTFOLIOHUB_BOOTSTRAP_*` 환경 변수 확인 대상 +- External: + - 없음 + +## Design Gate + +- Design Required Before Implementation: no +- Reason: README.md 단일 문서 보강 작업이며 구조/인터페이스/런타임 흐름 변경이 없음. 현재 섹션 내용 확인 후 누락 항목만 추가하는 방식으로 진행 가능. + +## Milestones + +1. README.md 현재 Operator Runbook 섹션 내용 파악 및 gap 식별 +2. REQ-020/021/022 AC 기준 누락 항목 보강 완료 +3. verification.md 작성 및 AC 매핑 완료 + +## Risks + +- README.md에 이미 대부분의 내용이 존재할 수 있음 — 현재 내용 확인 후 실제 gap이 작으면 구현 범위 축소 가능 +- `/auth/login` 엔드포인트가 실제 구현되어 있지 않으면 REQ-020 AC-4(steady-state canonical path rehearsal)는 문서화만 가능하고 실제 smoke 재현 불가 — Notes에 전제 명시로 대응 diff --git a/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/verification.md b/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/verification.md new file mode 100644 index 0000000..7d794d1 --- /dev/null +++ b/docs/batches/bat-009_20260512_production-operator-runbook-and-credential-matrix/verification.md @@ -0,0 +1,52 @@ +# BAT-009 Verification + +- Status: passed +- Batch: BAT-009 + +## Acceptance Mapping + +- REQ-020: + - AC-1: Given README.md Production Operator Runbook이 존재할 때, bootstrap path / steady-state canonical path / break-glass fallback이 각각 사용 조건과 함께 구분되어 기술되어 있어야 한다. + - Evidence: README.md Section 1 — steady-state(line 351), bootstrap path(line 353), break-glass fallback(line 354) 각각 사용 조건과 함께 존재 ✓ + - AC-2: users 테이블이 비어 있지 않은 환경에서 bootstrap 로직이 기존 사용자를 덮어쓰지 않아야 한다. + - Evidence: `app/main.py` `_bootstrap_initial_user()` — `if repository.list_users(): return` (line 33) — 사용자 존재 시 즉시 반환, idempotency 보장 ✓ + - AC-3: deployment secret custodian과 production operator/release approver 역할이 구분되어 있고 secret 보관 위치가 명시되어 있어야 한다. + - Evidence: README.md Section 2 — **deployment secret custodian** 역할 레이블(line 360), **production operator / release approver** 역할 레이블(line 361), 보관 위치 `/opt/portfoliohub/.env` 명시 ✓ + - AC-4: steady-state canonical path `/auth/login` → `POST /auth/service-tokens` 흐름이 runbook에 기술되어 있어야 한다. + - Evidence: README.md Section 4 — 5단계 smoke flow에 admin session token 확보 → service-token 발급 흐름 및 curl 예시 포함 ✓ + +- REQ-021: + - AC-1: public/read/trade-write/admin 4개 credential의 용도, 발급 주체, 보관 위치, 공동 소유 역할이 구분되어 기술되어 있어야 한다. + - Evidence: README.md Section 3 credential matrix 테이블 — 4개 credential 행(public/no-auth, read service-token, trade-write service-token, admin auth context), 용도/발급/보관/소유/폐기 열 구분 ✓ + - AC-2: release 문서에 name/token_prefix/permission/runtime_id/issued_at/revoked_at만 기록하고 raw token 값은 포함되지 않아야 한다. + - Evidence: README.md Section 2 — "release 문서와 운영 메모에는 token name, token_prefix, permission, runtime_id, issued_at, revoked_at만 남기고 raw secret은 남기지 않는다"(line 368) ✓; Section 6 evidence 기록 필드에도 동일 제약 명시 ✓ + - AC-3: `pfhub service-token list` 실행 시 필드가 출력되고 runtime_id 대조 가능해야 한다. + - Evidence: README.md Section 5 — `pfhub service-token list --user `으로 name/token_prefix/token_id/user_id/permission/created_at/last_used_at/revoked_at 확인, `GET /v1/user_info`로 runtime_id 대조 절차 명시 ✓ + - AC-4: admin context는 개인 소유, read/trade-write는 release approver 역할과 공동 소유임이 문서에 명시되어 있어야 한다. + - Evidence: README.md Section 2 — admin 개인 소유(line 363), read/trade-write 공동 소유(release approver/repository-maintainers)(line 364) ✓; Section 3 credential matrix 소유 형태 열 ✓ + +- REQ-022: + - AC-1: `journalctl -u portfoliohub`와 `journalctl -u caddy` 각각의 용도가 명시되어 있어야 한다. + - Evidence: README.md Section 6 — portfoliohub: "startup 및 애플리케이션 로그"(line 418), caddy: "reverse proxy 또는 access 수준 로그"(line 419) ✓ + - AC-2: 401/403/404 의미가 분리되어 재현 가능하게 설명되어 있어야 한다. + - Evidence: README.md Section 6 HTTP 오류 분류 — 401(인증 정보 없음/만료), 403(권한 부족), 404(리소스 없음) 각각 정의 및 예시 포함(line 424~426) ✓ + - AC-3: release evidence에 시각/endpoint/HTTP status code/source system 필드가 포함되고 raw secret이 제외되어야 한다. + - Evidence: README.md Section 6 Release evidence 기록 필드 — 포함 필드(시각, endpoint, HTTP status code, source system, token name, token_prefix, runtime_id) 및 제외(raw secret, raw token) 명시 ✓ + - AC-4: production이 canonical acceptance이고 staging은 optional preflight임이 명시되어 있어야 한다. + - Evidence: README.md Section 6 — "production을 canonical acceptance 환경으로 사용한다. staging smoke는 optional preflight로만 사용하며 release acceptance 기준이 되지 않는다"(line 414) ✓ + +## Evidence + +- Manual: + - README.md Section 1~6 항목별 내용 직접 확인 + - `app/main.py` `_bootstrap_initial_user()` 코드 리뷰 — `if repository.list_users(): return` idempotency 확인 + - credential matrix 테이블 4개 행 및 6개 열 구조 확인 + - 401/403/404 구분 판독 텍스트 존재 확인 + - canonical smoke environment 및 staging optional preflight 문장 존재 확인 + +## Result + +- Ready for Release: true +- Blocking Issues: 없음 +- Notes: + - `/auth/login` 엔드포인트 구현 여부 미확인. REQ-020 AC-4는 runbook에 문서화되어 있으나, steady-state path의 실제 smoke 재현은 해당 엔드포인트 구현 후 가능. release gate blocker는 아니며 후속 운영 환경에서 확인 권장. diff --git a/docs/batches/index.md b/docs/batches/index.md index 3393bb3..a0b1023 100644 --- a/docs/batches/index.md +++ b/docs/batches/index.md @@ -15,4 +15,5 @@ | BAT-005 | batch-lite | release-candidate | REQ-015 | minor-change | [bat-005_20260503_broker-credential-cli](./bat-005_20260503_broker-credential-cli/) | | BAT-006 | standard | release-candidate | REQ-016, REQ-017, REQ-018, REQ-019 | dcy-004 | [bat-006_20260511_portfolio-uuid-migration](./bat-006_20260511_portfolio-uuid-migration/) | | BAT-007 | batch-lite | release-candidate | REQ-001, REQ-002, REQ-003 | dcy-001 | [bat-007_20260511_local-db-validation-and-operations-runbook](./bat-007_20260511_local-db-validation-and-operations-runbook/) | -| BAT-008 | batch-lite | release-candidate | REQ-011 | dcy-002 | [bat-008_20260511_trex-basic-baseline-docs-update](./bat-008_20260511_trex-basic-baseline-docs-update/) | \ No newline at end of file +| BAT-008 | batch-lite | release-candidate | REQ-011 | dcy-002 | [bat-008_20260511_trex-basic-baseline-docs-update](./bat-008_20260511_trex-basic-baseline-docs-update/) | +| BAT-009 | batch-lite | release-candidate | REQ-020, REQ-021, REQ-022 | dcy-005 | [bat-009_20260512_production-operator-runbook-and-credential-matrix](./bat-009_20260512_production-operator-runbook-and-credential-matrix/) | \ No newline at end of file diff --git a/docs/discovery/dcy-005_20260511_production-admin-bootstrap-and-operator-access.md b/docs/discovery/dcy-005_20260511_production-admin-bootstrap-and-operator-access.md index a986c2d..f9e904d 100644 --- a/docs/discovery/dcy-005_20260511_production-admin-bootstrap-and-operator-access.md +++ b/docs/discovery/dcy-005_20260511_production-admin-bootstrap-and-operator-access.md @@ -1,24 +1,24 @@ # 0. 문서 상태 이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다. -- 상태: draft +- 상태: confirmed - 문서 ID: dcy-005_20260511_production-admin-bootstrap-and-operator-access - 이슈명: 실배포 admin bootstrap 및 operator access 경로 정립 - 작성 시각(KST): 2026-05-11 23:12 -- 마지막 갱신 시각(KST): 2026-05-11 23:12 +- 마지막 갱신 시각(KST): 2026-05-12 14:50 - 대체됨: 없음 - 후속 Discovery 참조: 없음 -- 생성된 REQ 참조: 없음 +- 생성된 REQ 참조: REQ-020, REQ-021, REQ-022 # 1. 계획 상태 요약 현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다. - 연계 문서 현황: - - 관련 점수 파일: 미생성 - 관련 TODO/메모: docs/releases/rel-001_20260502_trex-basic-v1-api-contract.md + - 관련 운영 문서: README.md "Production Operator Runbook" - 해석: - - REL-001 실배포 smoke에서 public health와 read service-token 경로는 확인됐지만, admin 성공 경로와 trade-write 성공 경로를 완료할 운영 접근 방법이 저장소 문서에 정의돼 있지 않다. - - 이번 Discovery는 활성 release 검증을 막는 운영 문제를 별도 주기로 분리해, 실배포 admin bootstrap, operator auth path, smoke credential provenance, 로그 접근 절차를 독립적으로 정리하기 위한 초안이다. + - REL-001은 released 상태로 전환됐지만, admin 성공 경로와 trade-write 성공 경로는 후속 운영 smoke로 닫아야 한다. 본 문서는 production smoke를 canonical acceptance로, staging smoke를 optional preflight로 두는 기준과 evidence 기록 위치를 고정한다. + - 이번 Discovery는 release blocker 해소보다는 released 이후 운영 hardening 항목으로서, 실배포 admin bootstrap, operator auth path, smoke credential provenance, 로그 접근 절차, 운영 책임 역할을 REQ handoff 가능한 수준으로 정리하기 위한 문서다. # 2. 요구사항 판정 결과 이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다. @@ -35,9 +35,9 @@ - 현재 상태 - 실배포 URL `https://fourblocks2026.com/portfolio-hub/`에서 `GET /health`, `/docs`, `/openapi.json`은 정상 응답했고, read service-token으로 `GET /v1/assets`, `GET /v1/user_info`까지는 검증됐다. - - 동일 토큰으로 `POST /auth/service-tokens`를 호출하면 `403 Insufficient permission`이 반환됐다. 현재 저장소에는 실배포에서 어떤 경로로 admin auth context를 확보하는지, admin 성공 경로를 어떻게 검증하는지 정리한 runbook이 없다. + - 동일 토큰으로 `POST /auth/service-tokens`를 호출하면 `403 Insufficient permission`이 반환됐다. 이후 README에 Production Operator Runbook 초안이 추가되어 canonical admin path, smoke credential ownership, log path 기준선은 정리됐고, 후속 smoke는 production을 canonical acceptance 환경으로 수행하며 staging은 optional preflight로만 사용하기로 정리했다. 실행 evidence는 해당 release 문서에 남기고, 절차의 source of truth는 README runbook으로 유지한다. - `app/core/auth.py`는 env token 집합 또는 DB `auth_tokens` principal의 `permission`으로 권한을 해석하고, `app/api/auth_routes.py`의 `POST /auth/service-tokens`는 `require_admin`을 선행한다. 즉 운영자가 어떤 credential을 admin으로 다뤄야 하는지가 명확해야 smoke가 가능하다. - - `README.md`와 `app/core/config.py`에는 first-run bootstrap 계정 환경 변수(`PORTFOLIOHUB_BOOTSTRAP_*`)가 있으나, 실배포의 steady-state admin access, smoke용 read/trade-write/admin token 분리, startup/request 로그 접근 경로는 문서화돼 있지 않다. + - 현재 저장소 기준선상 프로덕션은 Linux/Ubuntu host에서 systemd로 `portfoliohub` uvicorn 서비스를 띄우고 Caddy를 reverse proxy로 사용하며, secret material은 host-side `EnvironmentFile=/opt/portfoliohub/.env`에서 주입된다. secret leakage 대응 owner는 repository-maintainers 역할 중 deployment secret custodian으로 두고, admin recovery owner는 production operator/release approver 역할로 둔다. - 기대 상태 - 운영자는 실배포에서 admin auth context를 얻는 표준 경로를 알고 있어야 하며, 그 경로로 `POST /auth/service-tokens` 성공 smoke를 반복 가능하게 수행할 수 있어야 한다. - release 검증에는 public/no-auth, read service-token, trade-write service-token, admin auth context의 용도와 provenance가 문서로 분리돼 있어야 한다. @@ -50,7 +50,7 @@ - 제약사항 - 실제 credential 원문은 저장소, release 문서, Discovery 문서에 기록하지 않는다. - 기존 auth model(`read`, `trade-write`, `admin` 권한과 DB-issued service-token 구조)은 유지하되, 필요한 경우 bootstrap/runbook 또는 최소한의 운영 지원 기능만 추가한다. - - 활성 release(REL-001)와 병렬로 진행되므로, 이 Discovery는 운영 접근성과 검증 절차를 정리하는 데 집중하고 기존 consumer contract 범위를 임의로 확장하지 않는다. + - REL-001이 released 전환된 이후의 후속 운영 hardening 범위를 다루므로, 이 Discovery는 운영 접근성과 검증 절차를 정리하는 데 집중하고 기존 consumer contract 범위를 임의로 확장하지 않는다. ## 주요 사용자 시나리오 사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다. @@ -62,9 +62,9 @@ # 4. 이번에 정의할 변경 이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다. -- 실배포 admin bootstrap 및 steady-state operator access 경로를 정의한다. bootstrap user, admin login/session, admin token, DB-issued admin service token 중 어떤 경로를 표준으로 쓸지 결정한다. -- release 검증용 credential matrix를 정의한다. public/no-auth, read service-token, trade-write service-token, admin auth context의 용도와 발급/보관/폐기 절차를 분리한다. -- startup/request 로그 접근, auth/permission/resource 오류 판독, release evidence 기록 절차를 운영 runbook과 release 문서에 연결한다. +- 실배포 admin bootstrap 및 steady-state operator access 경로를 정의한다. steady-state canonical path는 운영자 소유 admin 사용자 계정으로 로그인해 확보한 DB-issued admin session 또는 동일 컨텍스트에서 발급한 client token으로 두고, 해당 admin context로 release smoke용 read/trade-write service-token을 발급한다. `PORTFOLIOHUB_BOOTSTRAP_*`는 새 프로덕션 환경에서 `users` 테이블이 비어 있거나 disaster recovery 직후 admin 사용자 부재가 확인된 경우에만 허용하고, steady-state recovery는 운영자 개인 admin 로그인 경로를 우선 사용한다. env admin token은 로그인 경로가 모두 막혔을 때 `/opt/portfoliohub/.env`에서 꺼내는 break-glass fallback으로만 사용한다. +- release 검증용 credential matrix를 정의한다. public/no-auth, read service-token, trade-write service-token, admin auth context의 용도와 발급/보관/폐기 절차를 분리한다. admin 로그인 자격증명은 운영자 개인별 소유로 두고 공유하지 않으며, read/trade-write smoke service-token은 환경 단위 운영 credential로 취급해 release approver 또는 repository-maintainers 역할이 공동 소유한다. 현재 프로덕션의 canonical secret material location은 host-side `EnvironmentFile=/opt/portfoliohub/.env`이며, raw secret은 이 경로와 동일 접근 통제 하의 운영 절차에서만 보관한다. release 문서에는 이름, token_prefix, permission, runtime_id, 발급/폐기 시각만 기록한다. +- startup/request 로그 접근, auth/permission/resource 오류 판독, release evidence 기록 절차를 운영 runbook과 release 문서에 연결한다. canonical acceptance smoke는 production에서 수행하고, staging smoke는 optional preflight로만 사용한다. 절차의 source of truth는 README runbook으로 유지하고, 실행 evidence의 canonical record는 해당 release 문서에 남긴다. 현재 canonical log access path는 Linux/Ubuntu production host의 systemd journal에서 `portfoliohub` 서비스 로그와 Caddy reverse proxy 로그를 확인하는 경로다. 즉 `journalctl -u portfoliohub`와 `journalctl -u caddy`가 1차 운영 경로이며, Loki는 `LOKI_ENABLED`와 실제 수집 경로가 운영 환경에 명시적으로 구성된 경우에만 보조 집계 경로로 취급한다. ## 영향 범위 변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다. @@ -87,10 +87,10 @@ 실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다. ## 기능 요구사항 -- FR-1: 실배포에서 admin auth context를 확보하는 표준 경로를 정의한다. first-run bootstrap과 steady-state 운영 경로를 구분하고, 운영자가 어떤 credential로 `POST /auth/service-tokens`를 성공시켜야 하는지 명시한다. -- FR-2: release 검증에 필요한 credential matrix를 정의한다. public/no-auth, read service-token, trade-write service-token, admin auth context 각각의 발급 주체, 보관 위치, 만료 정책, 폐기 절차를 문서화한다. -- FR-3: REL-001과 같은 app-service release에서 startup log, request log, auth/permission/resource 오류를 수집하는 운영 runbook을 정의한다. -- FR-4: 운영자가 DB 콘솔에 직접 들어가지 않고도 token provenance 또는 권한 수준을 확인할 수 있는 최소 지원 수단이 필요한지 판정하고, 필요 시 구현 또는 문서 경로를 정의한다. +- FR-1: 실배포에서 admin auth context를 확보하는 표준 경로를 정의한다. steady-state canonical path는 운영자 소유 admin 사용자 계정의 로그인으로 확보한 DB-issued admin session 또는 동일 컨텍스트에서 발급한 client token으로 하고, 이 경로로 `POST /auth/service-tokens`를 호출해 read/trade-write service-token을 발급하도록 명시한다. `PORTFOLIOHUB_BOOTSTRAP_*` 기반 bootstrap은 `users` 테이블이 비어 있는 신규 환경 또는 disaster recovery 직후 admin 사용자 부재가 확인된 경우에만 허용한다. steady-state recovery는 기존 운영자 admin 로그인 경로를 우선 사용하고, env admin token은 `/opt/portfoliohub/.env`에서만 꺼내는 break-glass fallback으로 제한한다. +- FR-2: release 검증에 필요한 credential matrix를 정의한다. public/no-auth, read service-token, trade-write service-token, admin auth context 각각의 발급 주체, 보관 위치, 만료 정책, 폐기 절차를 문서화한다. admin 로그인 자격증명은 named operator가 개인별로 소유하고 공유하지 않으며, read/trade-write smoke service-token은 환경 단위 운영 credential로서 release approver 또는 repository-maintainers 역할이 공동 소유한다. 현재 프로덕션 secret material location은 `portfoliohub` systemd service의 `EnvironmentFile=/opt/portfoliohub/.env`로 정의하고, release evidence에는 name, token_prefix, permission, runtime_id, issued_at, revoked_at만 남긴다. +- FR-3: REL-001과 같은 app-service release에서 startup log, request log, auth/permission/resource 오류를 수집하는 운영 runbook을 정의한다. canonical acceptance smoke는 production에서 수행하고, staging smoke는 optional preflight로만 사용한다. 절차의 source of truth는 README runbook으로 유지하고, 실행 evidence의 canonical record는 해당 release 문서에 남긴다. 기본 수집 경로는 Linux/Ubuntu production host의 systemd journal에서 `portfoliohub` uvicorn 서비스와 Caddy reverse proxy 로그를 조회하는 방식으로 하고, 구체 명령은 `journalctl -u portfoliohub`와 `journalctl -u caddy`를 사용한다. Loki는 운영 환경에서 실제 활성화가 확인된 경우에만 보조 조회 경로로 포함한다. 외부 URL만으로 로그 접근이 불가능한 점, 운영자가 어떤 콘솔 또는 CLI 권한으로 해당 로그를 조회하는지, release evidence에 남길 최소 필드(시각, 경로, 상태 코드, 소스 시스템)를 함께 문서화한다. +- FR-4: 운영자가 DB 콘솔에 직접 들어가지 않고도 token provenance 또는 권한 수준을 확인할 수 있는 최소 지원 수단을 정의한다. 신규 관리 기능을 추가하기보다 기존 `pfhub service-token list`, `pfhub service-token issue`, `pfhub service-token rotate`, `pfhub user get`, `GET /v1/user_info`를 canonical support path로 문서화하고, 이름, token_prefix, token_id, user_id, permission, created_at, last_used_at, revoked_at, runtime_id를 provenance 확인 필드로 사용한다. ## 비기능 요구사항 성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다. @@ -117,9 +117,9 @@ ## 리스크 - R-1 (High): admin/bootstrap credential을 ad-hoc 방식으로 공유하면 secret leakage가 발생할 수 있다. - - 완화 방안: secret manager 또는 승인된 보관소만 사용하고, 저장소/문서에는 raw value를 남기지 않는다. + - 완화 방안: secret manager 또는 승인된 보관소만 사용하고, 저장소/문서에는 raw value를 남기지 않는다. 대응 owner는 repository-maintainers 역할 중 deployment secret custodian으로 둔다. - R-2 (Medium): first-run bootstrap 계정 경로만 정리하고 steady-state 운영 경로를 정의하지 않으면, 이미 사용자가 있는 실배포 환경에서는 같은 문제를 반복한다. - - 완화 방안: bootstrap과 steady-state access/recovery를 별도 요구사항으로 분리해 정의한다. + - 완화 방안: bootstrap과 steady-state access/recovery를 별도 요구사항으로 분리해 정의한다. admin recovery owner는 production operator/release approver 역할로 둔다. - R-3 (Medium): 로그 접근 경로가 환경 의존적이면 release 증적 수집이 담당자 개인 지식에 묶인다. - 완화 방안: 최소한의 환경 계약(예: 어느 시스템에서 startup/request 로그를 확인하는지)을 문서화한다. @@ -134,17 +134,20 @@ 답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다. - OQ-1: steady-state admin access의 표준 경로는 무엇인가? bootstrap admin 로그인, env admin token, DB-issued admin session/token, 별도 운영 절차 중 무엇을 canonical path로 삼을지. - - 상태: Open - - 처리 방안: 운영자와 승인자가 실제 배포 체계를 확인하고 canonical path를 하나로 선택한다. - - 종료 조건: REL-001 후속 release 검증에 사용할 admin path가 문서화되면 Answered 처리. + - 상태: Answered + - 답변: steady-state canonical path는 운영자 소유 admin 사용자 계정으로 `/auth/login` 하여 확보한 DB-issued admin session 또는 동일 컨텍스트에서 발급한 client token이다. 운영자는 이 admin context로 `POST /auth/service-tokens`를 호출해 release smoke용 read/trade-write service-token을 발급한다. `PORTFOLIOHUB_BOOTSTRAP_*`는 `users` 테이블이 비어 있는 신규 환경 또는 disaster recovery 직후 admin 사용자 부재가 확인된 경우에만 사용하고, env admin token은 `/opt/portfoliohub/.env`에서만 꺼내는 break-glass fallback으로만 사용한다. + - 처리 방안: FR-1과 운영 runbook에 canonical path, bootstrap path, break-glass fallback의 구분을 반영한다. 또한 프로덕션에 운영자용 admin 사용자 계정이 실제로 존재해야 하며, 해당 로그인 자격증명과 break-glass env token은 `portfoliohub` systemd service의 `EnvironmentFile=/opt/portfoliohub/.env` 관리 경로 안에서 통제된다는 운영 전제를 후속 문서에 명시한다. + - 종료 조건: README 또는 후속 운영 문서에 canonical path와 fallback 절차가 반영되고, REL-001 후속 release smoke가 해당 경로로 재현되면 resolved 기록으로 이동한다. - OQ-2: startup/request 로그는 어디에서 확인하는가? 호스팅 환경, reverse proxy, Loki 등 실제 로그 경로와 접근 권한을 어떻게 운영자에게 부여하는가. - - 상태: Open - - 처리 방안: 현재 실배포 로그 스택과 접근 권한 모델을 확인한 뒤 runbook에 반영한다. - - 종료 조건: 실제 로그 수집 절차가 문서에 추가되면 Answered 처리. + - 상태: Answered + - 답변: 현재 canonical log access path는 Linux/Ubuntu production host의 systemd journal이다. 애플리케이션 startup 및 runtime 로그는 `journalctl -u portfoliohub`, reverse proxy 또는 access 수준 로그는 `journalctl -u caddy`로 확인한다. Loki는 설정 항목은 존재하지만 현재 저장소에는 프로덕션 수집 경로와 명시적 runtime flow가 없으므로 기본 표준 경로로 간주하지 않고, 운영 환경에서 `LOKI_ENABLED`와 실제 수집 파이프라인이 확인된 경우에만 보조 집계 경로로 취급한다. + - 처리 방안: FR-3과 운영 runbook에 로그 조회 채널을 systemd journal 기준으로 명시하고, startup log와 reverse proxy log를 각각 어떤 `journalctl` 명령으로 확인하는지 구분한다. 또한 release evidence에는 raw secret 없이 시각, 경로, 상태 코드, 소스 시스템, 상관 가능한 요청 식별자만 남긴다. + - 종료 조건: README 또는 후속 운영 문서에 `journalctl -u portfoliohub`, `journalctl -u caddy` 기반 로그 조회 절차와 evidence 필드가 반영되고, REL-001 후속 release smoke에서 startup/request 로그 확인 근거가 같은 채널로 재현되면 resolved 기록으로 이동한다. - OQ-3: token provenance 확인 수단은 문서화만으로 충분한가, 아니면 최소한의 CLI/관리 기능이 필요한가. - - 상태: Open - - 처리 방안: runbook-only 접근과 지원 기능 추가 비용을 비교해 결정한다. - - 종료 조건: REQ 초안 단계에서 지원 수단이 문서-only인지 구현 포함인지 확정되면 Answered 처리. + - 상태: Answered + - 답변: runbook-only 접근만으로는 부족하지만, 신규 관리 기능을 추가할 필요는 없다. 현재 저장소의 기존 CLI와 API가 이미 최소 provenance 확인 수단을 제공하므로, 이를 canonical support path로 문서화하는 것으로 충분하다. 운영자는 `pfhub service-token list`로 이름, token_prefix, token_id, user_id, permission, created_at, last_used_at, revoked_at를 확인하고, 발급/rotate 시 반환되는 `runtime_id`와 `GET /v1/user_info`의 `runtime_id`를 대조해 실배포 smoke에 사용한 service-token provenance를 확인한다. `pfhub user get`은 사용자별 service token 이름 목록 확인 보조 수단으로 사용한다. + - 처리 방안: FR-4와 운영 runbook에 기존 지원 수단의 사용 순서를 반영하고, README 또는 후속 운영 문서에서 `service-token list`와 `service-token rotate`를 명시한다. 추가 구현은 보류하고, 실제 smoke rehearsal에서 기존 수단만으로 provenance 판독이 부족할 때만 후속 REQ로 승격한다. + - 종료 조건: README 또는 후속 운영 문서에 provenance 확인 절차와 필드가 반영되고, REL-001 후속 release smoke에서 기존 CLI/API만으로 사용 토큰의 permission 및 provenance를 재현 가능하게 확인하면 resolved 기록으로 이동한다. ### 오픈 질문 처리 기준 - `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다. @@ -165,28 +168,28 @@ ## 측정 방식 및 데이터 출처 각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다. -- S-1 측정 방식: 운영 runbook 리뷰 및 smoke rehearsal / 데이터 출처: README.md 또는 신규 운영 문서, release verification 기록 -- S-2 측정 방식: 실배포 또는 staging smoke에서 admin/read/trade-write 경로를 각각 실행 / 데이터 출처: release 문서, HTTP 응답 로그, 운영자가 제공한 검증 기록 -- S-3 측정 방식: release 문서와 baseline 문서에 로그 접근 및 증적 수집 항목 포함 여부 확인 / 데이터 출처: docs/releases/*.md, docs/runtime-flows.md, docs/project-structure.md +- S-1 측정 방식: README runbook 리뷰 후 production smoke 기준 절차를 rehearsal하고 필요 시 staging에서 optional preflight 수행 / 데이터 출처: README.md, 해당 release 문서의 verification 기록 +- S-2 측정 방식: canonical acceptance로 production smoke에서 admin/read/trade-write 경로를 각각 실행하고, staging smoke는 사전 rehearsal로만 사용 / 데이터 출처: 해당 release 문서, HTTP 응답 로그, 운영자가 제공한 검증 기록 +- S-3 측정 방식: release 문서에 실행 evidence가 기록되고 baseline 문서에 로그 접근 및 증적 수집 항목이 반영됐는지 확인 / 데이터 출처: docs/releases/*.md, docs/runtime-flows.md, docs/project-structure.md, README.md # 8. REQ로 넘기기 전 확인 체크 REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다. -- [ ] 범위(In Scope/Out of Scope) 확정 +- [x] 범위(In Scope/Out of Scope) 확정 - 기준: admin bootstrap, steady-state access, logging scope의 포함/제외 범위 합의 -- [ ] 성공 지표의 측정 방식/데이터 출처 확정 - - 기준: staging/production 중 어디서 smoke를 수행할지와 evidence 위치 확정 -- [ ] High 영향 리스크 우선순위 및 해소 책임자 지정 - - 기준: secret leakage와 admin recovery 책임자 지정 -- [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인 +- [x] 성공 지표의 측정 방식/데이터 출처 확정 + - 기준: canonical acceptance smoke는 production, staging smoke는 optional preflight, 실행 evidence는 해당 release 문서에 기록 +- [x] 핵심 리스크 우선순위 및 해소 책임자 지정 + - 기준: secret leakage owner는 repository-maintainers 역할의 deployment secret custodian, admin recovery owner는 production operator/release approver 역할 +- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인 - 기준: OQ 목록의 `Open` 상태가 0개 -- [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정 +- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정 - 기준: 운영 승인자 또는 repository-maintainers 중 확인자 지정 # 9. 파일 처리 결과 이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다. -- 처리 결과: 생성 +- 처리 결과: 생성 후 갱신 - 생성 경로: docs/discovery/dcy-005_20260511_production-admin-bootstrap-and-operator-access.md - 참조 문서: - docs/releases/rel-001_20260502_trex-basic-v1-api-contract.md @@ -199,16 +202,21 @@ REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다. ## DECIDE -- {{DECIDE_CANONICAL_ADMIN_ACCESS_PATH}} -- {{DECIDE_SMOKE_CREDENTIAL_OWNERSHIP_POLICY}} +- 없음 ## CONFIRM -- {{CONFIRM_LOG_ACCESS_CHANNEL}} -- {{CONFIRM_BOOTSTRAP_VS_STEADY_STATE_RECOVERY_SCOPE}} +- 없음 (Confirmed By: Justin — 2026-05-12) ## DATA -- {{DATA_SECRET_STORE_LOCATION}} -- {{DATA_PRODUCTION_LOG_STACK}} +- baseline 반영 완료 + - canonical smoke environment: production + - staging smoke role: optional preflight only + - canonical evidence location: 해당 release 문서 + - procedure source of truth: README.md "Production Operator Runbook" + - secret store location: production host의 `portfoliohub` systemd service가 참조하는 `EnvironmentFile=/opt/portfoliohub/.env` + - production log stack: Linux/Ubuntu host + systemd-managed `portfoliohub` uvicorn service + Caddy reverse proxy, primary log commands `journalctl -u portfoliohub` 및 `journalctl -u caddy` + - secret leakage owner: repository-maintainers 역할 중 deployment secret custodian + - admin recovery owner: production operator/release approver 역할 # 11. Discovery Freeze REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다. @@ -221,18 +229,8 @@ REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록 - 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약) ## Freeze 플래그 -- Handoff Decision: 보류 -- Handoff Rationale: 운영 경로의 핵심 선택지(admin canonical path, 로그 접근, smoke credential provenance)가 아직 Open 상태라 REQ 초안 작성 전 사용자 확인이 필요하다. -- Ready for REQ Drafting: false -- Confirmed By: TBD -- Confirmed At (KST): TBD - -# 12. 작성 가이드 -템플릿 사용 시의 기본 규칙을 적는다. 실제 문서를 만들 때는 이 가이드를 기준으로 플레이스홀더를 치환한다. - -- 이 파일은 템플릿이다. 실제 문서 작성 시 `{{...}}` 플레이스홀더를 구체 값으로 교체한다. -- 필요 없는 항목은 삭제하고, 부족한 항목은 같은 형식으로 추가한다. -- Open Questions는 가정으로 대체하지 말고 그대로 유지한다. -- `대체됨`은 현재 Discovery가 더 이상 기준 문서가 아닐 때 대체 Discovery 경로를 기록하고, 해당 사항이 없으면 `없음`으로 둔다. -- `후속 Discovery 참조`는 범위를 이어받는 별도 Discovery가 생성된 경우 그 Discovery 경로를 기록하고, 없으면 `없음`으로 둔다. -- Discovery Freeze는 문서 하단에 반드시 유지한다. \ No newline at end of file +- Handoff Decision: 승인 +- Handoff Rationale: 핵심 운영 경로(OQ-1~3), smoke credential ownership policy, bootstrap 대 steady-state recovery 범위, canonical smoke/evidence 기준, secret store/log stack 기준선, 운영 책임 역할 모두 반영 완료. Freeze confirmer Justin 지정 완료. 오픈 질문 0개, C 유형 플레이스홀더 0개. +- Ready for REQ Drafting: true +- Confirmed By: Justin +- Confirmed At (KST): 2026-05-12 14:35 \ No newline at end of file diff --git a/docs/discovery/index.md b/docs/discovery/index.md index 550a4b8..38ed564 100644 --- a/docs/discovery/index.md +++ b/docs/discovery/index.md @@ -10,4 +10,4 @@ | dcy-002_20260502_trex-basic-consumer-api-contract | 20260502 | trex-basic Consumer-Driven Contract 반영 — service-token runtime_id 발급 및 v1 API 엔드포인트 구현 | [Discovery](./dcy-002_20260502_trex-basic-consumer-api-contract.md) | confirmed | | dcy-003_20260502_portfolio-list-user-required | 20260502 | CLI `pfhub portfolio list` — `--user` 옵션 필수화 및 소유자 기반 조회 강제 | [Discovery](./dcy-003_20260502_portfolio-list-user-required.md) | confirmed | | dcy-004_20260511_portfolio-id-uuid-migration-and-user-scoped-name-resolution | 20260511 | portfolio_id UUID 교체 및 CLI 사용자 범위 기반 자산 식별 구조 전환 | [Discovery](./dcy-004_20260511_portfolio-id-uuid-migration-and-user-scoped-name-resolution.md) | confirmed | -| dcy-005_20260511_production-admin-bootstrap-and-operator-access | 20260511 | 실배포 admin bootstrap 및 operator access 경로 정립 | [Discovery](./dcy-005_20260511_production-admin-bootstrap-and-operator-access.md) | draft | \ No newline at end of file +| dcy-005_20260511_production-admin-bootstrap-and-operator-access | 20260511 | 실배포 admin bootstrap 및 operator access 경로 정립 | [Discovery](./dcy-005_20260511_production-admin-bootstrap-and-operator-access.md) | confirmed | \ No newline at end of file diff --git a/docs/releases/index.md b/docs/releases/index.md index d6b21cf..ce5f08e 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -11,4 +11,5 @@ | REL-001 | app-service | released | BAT-001 | dcy-002 / REQ-004~010 | [rel-001_20260502_trex-basic-v1-api-contract](./rel-001_20260502_trex-basic-v1-api-contract.md) | | REL-002 | tooling | released | BAT-002, BAT-005, BAT-008 | minor-change + dcy-002 / REQ-011, REQ-012, REQ-015 | [rel-002_20260511_operator-cli-tooling-and-baseline-docs-sync](./rel-002_20260511_operator-cli-tooling-and-baseline-docs-sync.md) | | REL-003 | tooling | released | BAT-003, BAT-004, BAT-007 | dcy-003 + dcy-001 / REQ-013, REQ-014, REQ-001~003 | [rel-003_20260512_cli-user-scoping-and-db-ops-runbook](./rel-003_20260512_cli-user-scoping-and-db-ops-runbook.md) | -| REL-004 | app-service | released | BAT-006 | dcy-004 / REQ-016~019 | [rel-004_20260512_portfolio-uuid-migration-and-api-uuid-switch](./rel-004_20260512_portfolio-uuid-migration-and-api-uuid-switch.md) | \ No newline at end of file +| REL-004 | app-service | released | BAT-006 | dcy-004 / REQ-016~019 | [rel-004_20260512_portfolio-uuid-migration-and-api-uuid-switch](./rel-004_20260512_portfolio-uuid-migration-and-api-uuid-switch.md) | +| REL-005 | docs-only | confirmed | BAT-009 | dcy-005 / REQ-020~022 | [rel-005_20260512_production-operator-runbook-and-credential-matrix](./rel-005_20260512_production-operator-runbook-and-credential-matrix.md) | \ No newline at end of file diff --git a/docs/releases/rel-005_20260512_production-operator-runbook-and-credential-matrix.md b/docs/releases/rel-005_20260512_production-operator-runbook-and-credential-matrix.md new file mode 100644 index 0000000..7509678 --- /dev/null +++ b/docs/releases/rel-005_20260512_production-operator-runbook-and-credential-matrix.md @@ -0,0 +1,83 @@ +# REL-005: Production Operator Runbook and Credential Matrix + +- Status: confirmed +- Profile: docs-only +- Included Batch: BAT-009 + +## Scope +- Included: + - BAT-009 (REQ-020, REQ-021, REQ-022) — README.md `Production Operator Runbook` 섹션 보강 + - Section 2: deployment secret custodian / production operator·release approver 역할 레이블 추가 + - Section 3 (신규): public/read/trade-write/admin 4개 credential matrix 테이블 + - Section 6 (구 Section 5 확장): canonical smoke 환경 명시, 401/403/404 구분 판독, release evidence 기록 필드 +- Excluded: + - 코드·스키마·런타임 변경 없음 + +## Rollout Plan +1. 변경 내용이 README.md 단독이므로 배포 절차 없음 — git push로 문서 반영 +2. README.md 변경 사항이 main 브랜치에 병합되었는지 확인 + +## Rollback Plan +- 문서 전용 변경이므로 서비스 영향 없음 +- 롤백 필요 시 해당 커밋 revert로 README.md 이전 상태 복원 + +## Verification Checklist +- [x] README.md Section 2 — deployment secret custodian / production operator·release approver 역할 레이블 존재 확인 +- [x] README.md Section 3 — credential matrix 테이블 4개 행(public/read/trade-write/admin) 존재 확인 +- [x] README.md Section 6 — canonical smoke 환경 문장, 401/403/404 구분 판독, release evidence 기록 필드 존재 확인 +- [x] README.md Section 1 — bootstrap/steady-state/break-glass 3-tier 경로 구분 존재 확인 +- [x] README.md 링크 및 섹션 번호 연속성 이상 없음 확인 (Section 1~6 순서 정상) + +## Release Execution Evidence +- Release Status Transition Target: released + +### Deployment Facts +- Executed At: + - <배포 시작 시각> +- Completed At: + - <배포 완료 시각> +- Environment: + - docs (README.md 단독 변경, 서비스 배포 없음) +- Operator: + - <배포 수행자> +- Revision or Artifact: + - <커밋 SHA> +- Commands or Procedure: + - git push origin main (README.md 변경 반영) + +### Profile-Specific Checks +- docs-only: + - Published or Packaged Targets: + - README.md (Production Operator Runbook Section 2, 3, 6 변경) + - Link or Rendering Check: + - README.md Section 1~6 번호 연속성 정상 (grep -n "^### [0-9]\." 확인). 섹션 간 상호 참조 이상 없음 ✓ + - Navigation or Consumer Update: + - Section 3(신규)으로 인해 기존 Section 3→4, 4→5, 5→6 번호 이동. README.md 내부 참조 문장 없음(외부 링크 없음). 이상 없음 ✓ + +### Rollback Outcome +- Rollback Required: + - no +- Rollback Trigger: + - none +- Rollback Action: + - none + +### Release Decision Evidence +- Released Decision: + - +- Rationale: + - +- Recorded By: + - <기록자> +- Recorded At: + - <기록 시각> + +## Feedback Handoff +- Observation Summary: + - <운영 관찰 사실 요약 또는 없음> +- Discovery Input: + - `/auth/login` 엔드포인트 미구현 — steady-state canonical path(REQ-020 AC-4)의 실제 smoke 재현은 해당 엔드포인트 구현 후 가능. 후속 Discovery 후보. +- REQ Input: + - 없음 +- Change Request Input: + - 없음 diff --git a/docs/srs/Deployment/req-020_production-admin-bootstrap-and-operator-access-runbook.md b/docs/srs/Deployment/req-020_production-admin-bootstrap-and-operator-access-runbook.md new file mode 100644 index 0000000..e960a1f --- /dev/null +++ b/docs/srs/Deployment/req-020_production-admin-bootstrap-and-operator-access-runbook.md @@ -0,0 +1,109 @@ +# req-020: 실배포 admin bootstrap 및 steady-state operator access runbook + +- Status: Approved +- Type: Deployment +- Priority: High +- Owner: repository-maintainers + +## Intent +- 실배포 환경에서 admin auth context를 확보하는 경로가 문서화되지 않으면, 운영자는 release smoke를 재현 가능하게 수행할 수 없고 bootstrap과 steady-state access를 혼동해 보안 사고가 발생할 수 있다. +- README.md Production Operator Runbook에 admin bootstrap(신규 환경/disaster recovery 전용), steady-state canonical access(운영자 개인 admin 로그인), break-glass fallback(env admin token) 3-tier 경로를 명시하고 각 경로의 사용 조건을 구분한다. + +## Requirement +- README.md Production Operator Runbook 섹션은 다음 3-tier 경로를 조건과 함께 구분하여 문서화해야 한다. + 1. **bootstrap path**: `PORTFOLIOHUB_BOOTSTRAP_*` 기반 초기 사용자 생성. `users` 테이블이 비어 있는 신규 환경 또는 disaster recovery 직후 admin 사용자 부재가 확인된 경우에만 허용. + 2. **steady-state canonical path**: 운영자 소유 admin 사용자 계정으로 `/auth/login`하여 DB-issued admin session 또는 동일 컨텍스트에서 발급한 client token 확보. 이 admin context로 `POST /auth/service-tokens`를 호출해 release smoke용 read/trade-write service-token을 발급. + 3. **break-glass fallback**: 환경 변수 기반 admin token을 `/opt/portfoliohub/.env`에서 꺼내는 경로. 운영자 로그인 경로가 모두 막혔을 때만 허용. 정기 smoke의 표준 경로로 사용 금지. +- 각 경로의 사용 조건, 운영 책임 역할(deployment secret custodian, production operator/release approver), secret 보관 위치(`/opt/portfoliohub/.env` / `EnvironmentFile`)를 명시한다. +- raw credential, raw token, bootstrap password는 README 또는 release 문서에 기록하지 않는다. + +## Acceptance Criteria +1. Given README.md Production Operator Runbook 섹션이 존재할 때, When 운영자가 섹션을 읽을 때, Then bootstrap path / steady-state canonical path / break-glass fallback이 각각 사용 조건과 함께 구분되어 기술되어 있어야 한다. +2. Given `users` 테이블이 비어 있지 않은 실배포 환경에서, When `PORTFOLIOHUB_BOOTSTRAP_*` 기반 bootstrap 로직이 실행될 때, Then 기존 사용자를 덮어쓰거나 중복 생성하지 않고 bootstrap을 건너뛰어야 한다 (idempotency 보장). +3. Given README runbook에 역할 책임이 명시되어 있을 때, When 운영자가 runbook을 참조할 때, Then deployment secret custodian과 production operator/release approver 역할이 구분되어 있고 secret 보관 위치(`/opt/portfoliohub/.env`)가 명시되어 있어야 한다. +4. Given 운영자가 steady-state canonical path를 따를 때, When `/auth/login` → `POST /auth/service-tokens` 순서로 실행할 때, Then admin session token 기반 service-token 발급 성공 경로가 runbook에 기술되어 있어야 한다. + +## Impacted Area +- Docs: `README.md` — Production Operator Runbook 섹션 (3-tier 경로 구분 및 역할 책임 보강) +- Modules: `app/core/config.py` (bootstrap 환경 변수 운영 제약 확인), `app/main.py` (bootstrap idempotency 동작 확인) +- Tests: bootstrap idempotency는 기존 구현 확인으로 대체 가능. 별도 신규 테스트 불필요. + +## Notes +- NFR-1 (raw credential 비노출): 이 REQ의 핵심 제약으로 포함됨. Release 문서, README 등 모든 운영 문서에서 raw value 기록 금지. +- NFR-3 (bootstrap idempotency): `app/main.py`에서 `users` 테이블이 비어 있을 때만 bootstrap 사용자를 생성하는 로직이 기존에 구현되어 있음. AC-2는 기존 구현 확인으로 검증한다. +- `/auth/login` 엔드포인트가 실제 구현되어 있어야 steady-state canonical path가 완성된다. 현재 저장소에 로그인 엔드포인트가 없으면 해당 AC는 구현 시 재검토가 필요하다. +- 이 REQ는 기능 추가보다 운영 절차 문서화가 주 목적이다. 코드 변경이 필요할 경우 최소 범위로 제한한다. +- Source Discovery: dcy-005 FR-1 + +## Change Log + +### CHG-20260512-02 +- Date: 2026-05-12 +- Author: repository-maintainers + +#### Change Summary +- Status: Proposed → Approved (confirm-req 게이트 통과) + +#### Intent +- 필수 필드, AC, Notes blocker 없음 확인 후 승인 전환 + +#### Acceptance Criteria Delta +- Added: 없음 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: 없음 +- Modules: 없음 +- Tests: 없음 + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: none + +#### Validation Plan +- 승인 전환만 수행, 추가 검증 불필요 + +### CHG-20260512-01 +- Date: 2026-05-12 +- Author: repository-maintainers + +#### Change Summary +- REQ 초안 생성 (dcy-005 FR-1 기반) + +#### Intent +- 실배포 admin bootstrap 및 steady-state access 경로 운영 문서화 요구사항 정의 + +#### Acceptance Criteria Delta +- Added: + - AC-1~4 초안 생성 + +#### Impacted Area Delta +- Docs: + - README.md Production Operator Runbook 섹션 +- Modules: + - app/core/config.py, app/main.py (확인 범위) +- Tests: + - 없음 (기존 구현 확인으로 대체) + +#### Delivery Trace Delta +- Batches: + - none (미배정) +- Releases: + - none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: none + +#### Validation Plan +- README.md Production Operator Runbook 섹션의 3-tier 경로 구분 확인 +- app/main.py bootstrap idempotency 코드 리뷰로 AC-2 검증 diff --git a/docs/srs/Documentation/req-021_release-credential-matrix-and-token-provenance-procedures.md b/docs/srs/Documentation/req-021_release-credential-matrix-and-token-provenance-procedures.md new file mode 100644 index 0000000..017cd2d --- /dev/null +++ b/docs/srs/Documentation/req-021_release-credential-matrix-and-token-provenance-procedures.md @@ -0,0 +1,109 @@ +# req-021: release 검증 credential matrix 및 token provenance 운영 절차 + +- Status: Approved +- Type: Documentation +- Priority: High +- Owner: repository-maintainers + +## Intent +- release 검증에 사용하는 credential의 용도, 발급 주체, 보관 위치, 폐기 절차가 정의되지 않으면 smoke 재현성이 없어지고 secret leakage 위험이 높아진다. +- public/no-auth, read service-token, trade-write service-token, admin auth context 4개 credential 관점의 matrix를 문서화하고, 기존 CLI/API를 token provenance 확인 canonical path로 명시한다. + +## Requirement +- README.md Production Operator Runbook 또는 연결된 운영 문서는 아래 credential matrix를 명시해야 한다. + - **public/no-auth**: `GET /health`, `/docs`, `/openapi.json`. 인증 불필요. 배포 직후 공개 smoke에 사용. + - **read service-token**: `GET /v1/assets`, `GET /v1/user_info`. 환경 단위 운영 credential. 발급: admin context로 `POST /auth/service-tokens`. 보관: `/opt/portfoliohub/.env` 또는 동일 접근 통제 하 운영 절차. 공동 소유자: release approver / repository-maintainers. + - **trade-write service-token**: `POST /v1/assets/{asset_id}/ledger/trades`. 환경 단위 운영 credential. 발급: admin context로 `POST /auth/service-tokens`. 보관: read token과 동일 정책. 공동 소유자: release approver / repository-maintainers. + - **admin auth context**: `POST /auth/service-tokens`. 운영자 개인 소유. 공유 금지. 발급: steady-state는 `/auth/login` 경유 DB-issued session, break-glass는 env token. +- release 문서에는 name, token_prefix, permission, runtime_id, issued_at, revoked_at만 기록하고 raw token값은 기록하지 않는다. +- 운영자는 `pfhub service-token list`, `pfhub service-token issue`, `pfhub service-token rotate`, `pfhub user get`, `GET /v1/user_info`를 token provenance 확인 canonical support path로 사용한다. 각 명령의 출력 필드(name, token_prefix, token_id, user_id, permission, created_at, last_used_at, revoked_at, runtime_id)를 provenance 확인 기준으로 명시한다. + +## Acceptance Criteria +1. Given README.md 또는 연결된 운영 문서가 존재할 때, When 운영자가 credential matrix를 참조할 때, Then public/read/trade-write/admin 4개 credential의 용도, 발급 주체, 보관 위치, 공동 소유 역할이 구분되어 기술되어 있어야 한다. +2. Given release 문서(docs/releases/*.md)가 존재할 때, When smoke evidence를 기록할 때, Then name, token_prefix, permission, runtime_id, issued_at, revoked_at 필드만 남기고 raw token 값은 포함되지 않아야 한다. +3. Given 운영자가 service-token provenance를 확인할 때, When `pfhub service-token list`를 실행할 때, Then name, permission, created_at, last_used_at, revoked_at 필드가 출력되고, `GET /v1/user_info`의 runtime_id와 대조해 배포된 토큰의 provenance를 재현 가능하게 확인할 수 있어야 한다. +4. Given admin auth context가 필요한 smoke를 수행할 때, When 운영자가 credential matrix를 따를 때, Then admin context는 개인 소유로 취급되고 read/trade-write token은 release approver 역할과 공동 소유 형태로 관리되는 것이 문서에 명시되어 있어야 한다. + +## Impacted Area +- Docs: `README.md` — credential matrix 항목 및 token provenance CLI 절차 추가/보강 +- Modules: `app/api/v1_user_info.py` (runtime_id 반환 확인), `app/cli/commands/service_token.py` (list/rotate 출력 필드 확인) +- Tests: 없음 (문서화 중심 REQ) + +## Notes +- NFR-1 (raw credential 비노출): 이 REQ의 핵심 제약. AC-2에 명시. +- FR-4와 FR-2를 통합한 REQ. token provenance 확인 수단과 credential matrix는 운영자가 같은 맥락에서 참조하므로 분리 시 중복이 발생한다. +- `runtime_id`는 service-token rotate 후 변경되므로 rotate 후 consumer가 재조회해야 한다는 사실도 matrix 문서에 포함할 것을 권장 (REQ-012 CHG-20260511-01 참조). +- Source Discovery: dcy-005 FR-2, FR-4 + +## Change Log + +### CHG-20260512-02 +- Date: 2026-05-12 +- Author: repository-maintainers + +#### Change Summary +- Status: Proposed → Approved (confirm-req 게이트 통과) + +#### Intent +- 필수 필드, AC, Notes blocker 없음 확인 후 승인 전환 + +#### Acceptance Criteria Delta +- Added: 없음 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: 없음 +- Modules: 없음 +- Tests: 없음 + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: none + +#### Validation Plan +- 승인 전환만 수행, 추가 검증 불필요 + +### CHG-20260512-01 +- Date: 2026-05-12 +- Author: repository-maintainers + +#### Change Summary +- REQ 초안 생성 (dcy-005 FR-2, FR-4 통합 기반) + +#### Intent +- release 검증 credential matrix 및 token provenance 확인 절차 문서화 요구사항 정의 + +#### Acceptance Criteria Delta +- Added: + - AC-1~4 초안 생성 + +#### Impacted Area Delta +- Docs: + - README.md credential matrix 및 token provenance 절차 +- Modules: + - 없음 (문서화 중심) +- Tests: + - 없음 + +#### Delivery Trace Delta +- Batches: + - none (미배정) +- Releases: + - none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: none + +#### Validation Plan +- README.md credential matrix 항목 구분 및 token provenance CLI 절차 존재 확인 +- release 문서 기록 정책 준수 여부 확인 diff --git a/docs/srs/Documentation/req-022_operator-log-access-and-release-evidence-recording.md b/docs/srs/Documentation/req-022_operator-log-access-and-release-evidence-recording.md new file mode 100644 index 0000000..17b74eb --- /dev/null +++ b/docs/srs/Documentation/req-022_operator-log-access-and-release-evidence-recording.md @@ -0,0 +1,114 @@ +# req-022: 운영 로그 접근 및 release evidence 기록 절차 + +- Status: Approved +- Type: Documentation +- Priority: Medium +- Owner: repository-maintainers + +## Intent +- 운영자가 startup/request 로그 접근 경로와 auth/permission/resource 오류 판독 방법을 모르면 release smoke의 증적을 재현 가능하게 수집할 수 없고, 장애 시 원인 분류가 불가능해진다. +- systemd journal 기반 canonical log access 경로와 401/403/404 구분 판독 기준, release evidence 기록 필드를 README.md에 정의한다. + +## Requirement +- README.md Production Operator Runbook 섹션은 아래 로그 접근 및 evidence 기록 절차를 포함해야 한다. + 1. **애플리케이션 startup/runtime 로그**: `sudo journalctl -u portfoliohub -n 200 --no-pager` 또는 `-f` 옵션으로 실시간 확인. primary log path. + 2. **reverse proxy/access 로그**: `sudo journalctl -u caddy -n 200 --no-pager` 또는 `-f` 옵션. primary log path. + 3. **Loki**: `LOKI_ENABLED`와 실제 수집 파이프라인이 운영 환경에서 확인된 경우에만 보조 집계 경로로 취급. + 4. **auth/permission/resource 오류 구분**: + - 401: 인증 정보 없음 또는 만료 — bearer token 미제공 또는 유효하지 않은 token + - 403: 권한 부족 — token은 유효하나 해당 엔드포인트에 필요한 permission 미보유 + - 404: 리소스 없음 — 인증/권한은 통과했으나 대상 리소스가 DB에 존재하지 않음 + 5. **release evidence 기록 필드**: 시각(executed_at, completed_at), 요청 경로, HTTP 상태 코드, 소스 시스템(journalctl unit name), 상관 가능한 요청 식별자. raw secret, raw token 값은 포함하지 않는다. +- canonical acceptance smoke는 production에서 수행하고, staging smoke는 optional preflight로만 사용한다. +- 절차의 source of truth는 README runbook으로 유지하고, 실행 evidence의 canonical record는 해당 release 문서에 남긴다. + +## Acceptance Criteria +1. Given README.md Production Operator Runbook이 존재할 때, When 운영자가 로그 접근 절차를 참조할 때, Then `journalctl -u portfoliohub`와 `journalctl -u caddy`를 각각 어느 용도로 사용하는지가 명시되어 있어야 한다. +2. Given release smoke 중 HTTP 오류가 발생했을 때, When 운영자가 오류를 분류할 때, Then README runbook에 401(인증 없음), 403(권한 부족), 404(리소스 없음)의 의미가 분리되어 재현 가능하게 설명되어 있어야 한다. +3. Given release 문서에 evidence를 기록할 때, When smoke 실행 결과를 기재할 때, Then 시각, 요청 경로, HTTP 상태 코드, 소스 시스템 필드가 포함되고 raw secret은 포함되지 않아야 한다. +4. Given canonical acceptance 환경이 명시되어 있을 때, When 운영자가 smoke 환경을 선택할 때, Then production이 canonical acceptance이고 staging은 optional preflight임이 README runbook에 명시되어 있어야 한다. + +## Impacted Area +- Docs: `README.md` — Production Operator Runbook 로그 접근 절차 및 오류 구분 기준 보강 +- Modules: 없음 (문서화 중심 REQ) +- Tests: 없음 + +## Notes +- NFR-2 (401/403/404 구분 설명): AC-2에 흡수. +- NFR-1 (raw credential 비노출): AC-3에 흡수. +- 외부 URL만으로는 startup log 접근이 불가능하다는 제약을 README에 명시해야 한다. (REL-001 Deployment Facts 참조) +- Loki 관련 설정(`LOKI_ENABLED`, `LOKI_URL`)은 `app/core/config.py`에 존재하지만 현재 production에서 활성화가 확인되지 않으므로 기본 표준 경로로 다루지 않는다. +- Source Discovery: dcy-005 FR-3 + +## Change Log + +### CHG-20260512-02 +- Date: 2026-05-12 +- Author: repository-maintainers + +#### Change Summary +- Status: Proposed → Approved (confirm-req 게이트 통과) + +#### Intent +- 필수 필드, AC, Notes blocker 없음 확인 후 승인 전환 + +#### Acceptance Criteria Delta +- Added: 없음 +- Updated: 없음 +- Removed: 없음 + +#### Impacted Area Delta +- Docs: 없음 +- Modules: 없음 +- Tests: 없음 + +#### Delivery Trace Delta +- Batches: none +- Releases: none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: none + +#### Validation Plan +- 승인 전환만 수행, 추가 검증 불필요 + +### CHG-20260512-01 +- Date: 2026-05-12 +- Author: repository-maintainers + +#### Change Summary +- REQ 초안 생성 (dcy-005 FR-3 기반) + +#### Intent +- 운영 로그 접근 및 release evidence 기록 절차 문서화 요구사항 정의 + +#### Acceptance Criteria Delta +- Added: + - AC-1~4 초안 생성 + +#### Impacted Area Delta +- Docs: + - README.md 로그 접근 절차 및 오류 구분 기준 +- Modules: + - 없음 +- Tests: + - 없음 + +#### Delivery Trace Delta +- Batches: + - none (미배정) +- Releases: + - none + +#### Revalidation Impact +- Reverification Needed: no +- Existing Implementation Invalidated: no +- Status Recommendation: keep-current +- Follow-up Action: none + +#### Validation Plan +- README.md journalctl 명령 존재 및 401/403/404 구분 기술 확인 +- release evidence 기록 필드 정책 준수 확인 diff --git a/docs/srs/index.md b/docs/srs/index.md index f7760b0..710054f 100644 --- a/docs/srs/index.md +++ b/docs/srs/index.md @@ -74,7 +74,7 @@ ## Next Requirement ID -- Current: `REQ-020` +- Current: `REQ-023` - Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다. ## Requirement Register @@ -100,9 +100,14 @@ | REQ-017 | CLI portfolio 명령 — name+user 조합 식별 방식 전환 | Interface | Implemented | High | repository-maintainers | [REQ-017](./Interface/req-017_portfolio-cli-name-user-identification.md) | | REQ-018 | HTTP API portfolio path parameter UUID 전환 및 v1 asset_id UUID 반환 | Interface | Implemented | High | repository-maintainers | [REQ-018](./Interface/req-018_portfolio-http-api-uuid-path-param.md) | | REQ-019 | portfolio UUID 전환 관련 baseline 문서 갱신 | Documentation | Implemented | Medium | repository-maintainers | [REQ-019](./Documentation/req-019_portfolio-uuid-baseline-docs-update.md) | +| REQ-020 | 실배포 admin bootstrap 및 steady-state operator access runbook | Deployment | Approved | High | repository-maintainers | [REQ-020](./Deployment/req-020_production-admin-bootstrap-and-operator-access-runbook.md) | +| REQ-021 | release 검증 credential matrix 및 token provenance 운영 절차 | Documentation | Approved | High | repository-maintainers | [REQ-021](./Documentation/req-021_release-credential-matrix-and-token-provenance-procedures.md) | +| REQ-022 | 운영 로그 접근 및 release evidence 기록 절차 | Documentation | Approved | Medium | repository-maintainers | [REQ-022](./Documentation/req-022_operator-log-access-and-release-evidence-recording.md) | ## Recent Change Log Summary +- 2026-05-12: REQ-020~022 Approved 전환 — confirm-req 게이트 통과 (필수 필드, AC, blocker 없음) +- 2026-05-12: REQ-020~022 Proposed 생성 — dcy-005 (실배포 admin bootstrap 및 operator access) FR-1~3, NFR-1~3 기반 - 2026-05-11: REQ-011, REQ-012, REQ-015 Implemented 전환 — BAT-008/BAT-002/BAT-005 verification evidence 확인 - 2026-05-11: REQ-005 CHG-20260511-02 — service-token 발급 API 경로를 POST /auth/service-tokens로 명확화 - 2026-05-11: REQ-012 CHG-20260511-01 — rotate 후 runtime_id lifecycle 및 consumer 재조회 필요성 명시