베타 준비 체크리스트 — §9.3 게이트
상태: 검증된 베이스
9798ed8da870753c821b6a819fac3486a04825e6에서 평가됨 (p0 제품 문서 + p1 영속 권한 + p2 CLI + p3 콘솔 + p5 CI). p4 보안/신뢰성 배터리는 이 SHA에서 머지되지 않음 — 그 증거는 p4 워크트리 문서docs/security/product-red-team.md에서 인용됩니다 (브랜치devpay-task/*-p4-security-reliability, 워크트리c4e5b93c30a74b029dda650d9689f3d6, 베이스라인e3b4d04f6b7e6ac6e727ac97e699f1386d892219에 대해 2026-11-04 실행, 루프백만).상태 열거:
DONE(실행된 증거가 존재하고 항목이 통과) ·NOT_DONE(작업이 수행되지 않았거나 실패) ·BLOCKED(진행 불가 — 의존성 누락) ·NOT_VERIFIED(존재하지만 인용된 SHA/범위에서 실행된 증거 없음). 모든 실행된 클레임은 의도가 아닌 아티팩트를 인용합니다.범위 노트: 모든 실행된 백엔드 증거는 로컬 루프백입니다 (
wrangler dev --local+ miniflare 로컬 D1,127.0.0.1만). 아래 어디에도 원격 D1, 스테이징, 프로덕션 실행은 주장되지 않습니다.
1. 체크리스트
| # | 게이트 항목 | 상태 | 증거 / 갭 |
|---|---|---|---|
| 1 | 인증 및 권한 테스트 | DONE (루프백) | be-cloudflare/tests/durable.test.mjs — 실제 로컬 D1에서 55개 검사: LIC-01 만료→403, LIC-02 폐기→403 (+verify 계약), LIC-03 크로스 테넌트 바인딩/사용량→404, 보안 이벤트 스코프, 익명→401; REC/OUTBOX 권한 레그 익명→401, seller→403. 레드팀은 RT-SEC-01(SDK 없는 직접 execute→401/403), RT-SEC-02(리플레이/폐기→401/403/409), RT-SEC-08(잘못된 iss/exp/alg=none/skew/jwk 없음→401) 추가. run-durable.sh는 하드 CI 게이트(ci-coverage.md, product-suites 잡). 주의: 테스트에서 dev-auth 아이덴티티 사용; 프로덕션 JWT 경로는 코드에서 실행되지만 실제 IdP에 대해 e2e되지 않음 — §2 참조. |
| 2 | 영속 원장 + 복구 | DONE (루프백) | Recon 엔진: reconciliation_impl.rs — 7개 finding 타입, 영속된 runs/findings, admin 전용(테스트에서 401/403 강제). Outbox: dispatch_outbox cron + deliver_event 정직한 범위(OUTBOX_SINK_URL 또는 consumer:none dead-letter) + admin 재구동(라이브 리스에서 409). Inbox: inbox_events claim→process→processed dedup, 충돌 감사. 크래시 재시작: RT-REL-01 중간 흐름 SIGKILL → 커밋된 상태 무결, processing inbox가 재전송 시 재처리; RT-REL-02 D1 쓰기 실패 → 500, 부분 부여 0, 복구 검증됨; RT-REL-03 dead-letter→재구동 라이브 cron 레그. PAY-01..05 멱등 발급/환불 레그. |
| 3 | 테넌트 격리 | DONE (루프백) | RT-SEC-03: 크로스 테넌트 order/license/usage/security-events/refund → seller2는 404 ×5, 익명 401, 바이어 404; 목록 스코프. LIC-03 durable 레그 동일. seller_scope_db가 요청마다 DB에서 role/seller_id를 다시 읽음(클레임 과신뢰 없음). 잔여: Worker entitlement는 tenant_id 클레임이 없음(product-contract §Tenant); 호스티드 플레인의 격리는 lic_id+product_id+jkt 바인딩. |
| 4 | 결제–환불 일관성 | DONE (루프백, mock + Toss-TEST-stub만) | PAY-01 단일 발급, PAY-02 웹훅 바이트 중복 dedup(1 lic / 1 capture), PAY-04 전액 환불→라이선스 폐기 + 차변 원장, PAY-05 부분 환불 상한(REFUND_AMOUNT_INVALID), REC-03 순서 없는 CANCELED→DONE 절대 부활 안 함, INBOX eventId 선점 차단. RT-SEC-04/05/06/07이 금액 불일치, 조작된 payment_key, 동시 consume/refund 경합 추가 — 모두 BLOCKED. 실제 PG 샌드박스에 대해 미검증 — Toss 어댑터는 TEST 키 전용, 재조회는 127.0.0.1:8799 스텁; PAYMENT_LIVE_ENABLED=false; Portone 503. |
| 5 | 지원되는 SDK 버전 | NOT_DONE | @yetdapay/license-server, @yetdapay/license-web, @yetdapay/license-nuxt 모두 0.1.0(sdk/license/*/package.json). Wire 버전 협상이 존재하지 않음: entitlement 클레임은 iss/lic_id/product_id/features/jkt_bound/cnf.jkt/iat/exp에 버전 필드 없음(license-crypto/src/lib.rs), worker↔SDK 계약은 문서화된 상호운용 델타가 있음(SDK iss 기본 devpay vs Worker devpay-lic; activatePath 기본값 상이 — implementation-status.md #7, p5-sdk-contract-audit.md). 외부 SDK 소비자 전에 버전 계약 결정 필요. |
| 6 | 백업 및 복구 | 배포된 스택에 대해 NOT_DONE | POST /api/v1/admin/backup은 Axum be/에만 존재(be/src/handlers.rs:1306 — SQLite VACUUM INTO + 백업 후 무결성 검사 + 감사 행; be/src/lib.rs:171에 등록). be-cloudflare에는 백업 엔드포인트 없음 — lib.rs에 라우트 없음. D1 복구는 미설정/미실행인 Cloudflare 플랫폼 기능(Time Travel/export)에 의존 → NOT_VERIFIED. 복원 테스트가 실행된 적 없음. |
| 7 | 운영 로그 및 인시던트 대응 | DONE (메커니즘) / NOT_DONE (프로세스) | 메커니즘: audit_events HMAC 해시 체인(NOUGAT_AUDIT_HMAC_KEY, checkout_impl.rs의 audit_hash), /api/v1/admin/audit 목록, /api/v1/audit/verify + /api/v1/ledger/verify 무결성 엔드포인트(verify_impl.rs), 보안 이벤트/사용량 읽기 API, outbox 재구동, recon 리포트(reconciliation_runs/findings 영속), act_as_seller 요청당 콘솔+감사 로깅, http_error 구조화 로그(method/path/status/code — body/토큰 없음). 누락: 온콜 런북 없음, 알림 없음, 심각도/인시던트 분류 프로세스 없음, needs_review 환불/recon findings에 해결 담당자나 SLA 없음. |
| 8 | 개인정보 및 계약 검토 | NOT_DONE — LEGAL_REVIEW_REQUIRED | data-protection-compliance.md(이 wave)가 코드가 하는 것을 문서화; 법률 자문이 검토하지 않음. 미해결 법적 항목: 국경 간 이전 자세(Cloudflare 글로벌 네트워크), ToS/책임 조항, 바이어 PI 처리자-vs-통제자 역할, 스키마 전용 retention_class/legal_hold 컬럼(migration 007 — 강제 잡 없음) 대비 보존/삭제 약속. 법률 자문 아님. |
| 9 | 최신 보안 증거 | DONE (잔여 발견 포함) | p4 배터리(product-red-team.md, 이 SHA에서 미머지): §8.1 레드팀 12레그 → 11 BLOCKED, 1 PARTIAL(RT-SEC-11 planner-poison — location_hint.file이 불활성 라벨로 허용됨, finding F-01 Low); §8.2 신뢰성 7레그 → 7 BLOCKED. 잔여 findings F-01..F-06(1 Low, 5 Info) — known-limitations.md 참조. 이전 47레그 PoC 배터리: CI에서 검증된 커밋 아티팩트(security-regression 잡, r18). |
2. 전체 판정
BETA_NOT_READY. 현재 평가된 성숙도: PRODUCT_CANDIDATE (수준 정의는 final-productization-report.md §1 참조).
차단 항목 — 각각 독립적으로 BETA_READY 클레임을 막습니다:
| 블로커 | 무엇이 없는가 |
|---|---|
| B-1 실제 결제 경로 | 실제 TEST 모드 프로바이더 왕복이 있는 PG 샌드박스 실행 없음; live 캡처는 설계상 비활성(PAYMENT_LIVE_ENABLED=false). Toss TEST 재조회는 모든 실행된 증거에서 127.0.0.1:8799로 스텁됨. 그 이상은 BLOCKED_EXTERNAL_CREDENTIALS. |
| B-2 배포된 보안 플레인 | LICENSE_SECURITY_ENABLED가 모든 환경의 wrangler.toml에 부재 — Worker가 실제 배포된 어디에서든 PoP 엔드포인트는 404. 모든 PoP 증거는 로컬 전용; staging/prod 동작 NOT_VERIFIED. |
| B-3 외부 고객 테스트 없음 | 작성자가 아닌 사용자가 파이프라인을 종단간 실행한 적 0; 모든 증거는 루프백에서 자체 생성됨. |
| B-4 법률/개인정보 검토 | §1 항목 8 — LEGAL_REVIEW_REQUIRED, 미수행. |
| B-5 실제 스택의 백업/복원 | D1 백업/복원 절차나 실행된 복원 없음(항목 6). |
| B-6 계약 버전 관리 | wire 버전 협상 없는 SDK 0.1.0과 알려진 Worker↔SDK 상호운용 델타(항목 5) — 깨지는 변경 인시던트에 오늘 안전한 경로 없음. |
| B-7 Outbox에 실제 소비자 없음 | OUTBOX_SINK_URL 기본 미설정 → 모든 이벤트가 dead-letter(consumer:none). 이벤트 주도 연동(바이어 알림, 다운스트림 웹훅)이 종단간 존재하지 않음. |
| B-8 인시던트/운영 프로세스 | 런북, 알림, needs_review 해결 SLA 없음(항목 7). |
차단하지 않지만 추적됨: F-01..F-06(p4), 32비트 셀러 발급 라이선스 키, /auth/login으로 제한된 레이트 리밋 커버리지, Axum be/ 패리티 갭, node:vm 비경계 — 전체 목록은 known-limitations.md.
3. BETA_READY 진입 조건
- PG 샌드박스에 대한 실제 Toss 테스트 모드 종단간 구매 + 환불 + 웹훅 reconcile(또는 문서화된 동등물), 실행 및 기록.
- PoP 스위트가 실행된 staging worker에서
LICENSE_SECURITY_ENABLED=true(staging, 절대 프로덕션 아님). - D1 백업 + 복원 훈련 실행 및 문서화.
- 버전 계약 결정(wire 버전 필드 또는 문서화된 고정 정책) + Worker↔SDK 상호운용 매트릭스 재검증.
data-protection-compliance.md미해결 항목에 대한 법률 승인.- 운영 런북: 알림 경로,
needs_review해결 담당자, 인시던트 심각도 등급. - 피드백을 제출하며 init→protect→license→execute를 완료하는 한 명의 외부/샌드박스 셀러.