Beta Readiness Checklist — §9.3 Gate
Status: evaluated at validated base
9798ed8da870753c821b6a819fac3486a04825e6(p0 product docs + p1 durable authority + p2 CLI + p3 console + p5 CI). The p4 security/reliability battery is not merged at this SHA — its evidence is cited from the p4 worktree documentdocs/security/product-red-team.md(branchdevpay-task/*-p4-security-reliability, worktreec4e5b93c30a74b029dda650d9689f3d6, executed 2026-11-04 against baselinee3b4d04f6b7e6ac6e727ac97e699f1386d892219, loopback only).Status enum:
DONE(executed evidence exists and the item passes) ·NOT_DONE(work not performed or failed) ·BLOCKED(cannot proceed — dependency missing) ·NOT_VERIFIED(exists but no executed evidence at the cited SHA/scope). Every executed claim cites the artifact, not intent.Scope note: all executed backend evidence is local loopback (
wrangler dev --local+ miniflare local D1,127.0.0.1only). No remote D1, staging, or production execution is claimed anywhere below.
1. Checklist
| # | Gate item | Status | Evidence / gap |
|---|---|---|---|
| 1 | Auth & permission tests | DONE (loopback) | be-cloudflare/tests/durable.test.mjs — 55 checks on real local D1: LIC-01 expired→403, LIC-02 revoked→403 (+verify contract), LIC-03 cross-tenant bindings/usage→404, security-events scope, anonymous→401; REC/OUTBOX permission legs anonymous→401, seller→403. Red-team adds RT-SEC-01 (no-SDK direct execute→401/403), RT-SEC-02 (replay/revoked→401/403/409), RT-SEC-08 (wrong iss/exp/alg=none/skew/no-jwk→401). run-durable.sh is a hard CI gate (ci-coverage.md, product-suites job). Caveat: dev-auth identities used in tests; production JWT path exercised in code but not e2e'd against a real IdP — see §2. |
| 2 | Durable ledger + recovery | DONE (loopback) | Recon engine: reconciliation_impl.rs — 7 finding types, persisted runs/findings, admin-only (401/403 enforced in test). Outbox: dispatch_outbox cron + deliver_event honest scope (OUTBOX_SINK_URL or consumer:none dead-letter) + admin redrive (409 on live lease). Inbox: inbox_events claim→process→processed dedup, conflict audit. Crash-restart: RT-REL-01 SIGKILL mid-flow → committed state intact, processing inbox re-processed on resend; RT-REL-02 D1 write failure → 500, zero partial grants, recovery verified; RT-REL-03 dead-letter→redrive live cron leg. PAY-01..05 idempotent issuance/refund legs. |
| 3 | Tenant isolation | DONE (loopback) | RT-SEC-03: cross-tenant order/license/usage/security-events/refund → seller2 sees 404 ×5, anonymous 401, buyer 404; lists scoped. LIC-03 durable legs same. seller_scope_db re-reads role/seller_id from DB per request (no claim over-trust). Residual: Worker entitlement carries no tenant_id claim (product-contract §Tenant); isolation on hosted plane is lic_id+product_id+jkt binding. |
| 4 | Payment–refund consistency | DONE (loopback, mock + Toss-TEST-stub only) | PAY-01 single issuance, PAY-02 webhook byte-dup dedup (1 lic / 1 capture), PAY-04 full refund→license revoked + debit ledger, PAY-05 partial refunds cap (REFUND_AMOUNT_INVALID), REC-03 out-of-order CANCELED→DONE never resurrects, INBOX eventId pre-emption blocked. RT-SEC-04/05/06/07 add amount-mismatch, fabricated payment_key, concurrent consume/refund races — all BLOCKED. Not verified against a real PG sandbox — Toss adapter is TEST-keys-only, refetch hits a 127.0.0.1:8799 stub; PAYMENT_LIVE_ENABLED=false; Portone 503. |
| 5 | SDK versions supported | NOT_DONE | @yetdapay/license-server, @yetdapay/license-web, @yetdapay/license-nuxt all 0.1.0 (sdk/license/*/package.json). No wire-version negotiation exists: entitlement claims are iss/lic_id/product_id/features/jkt_bound/cnf.jkt/iat/exp with no version field (license-crypto/src/lib.rs), and the worker↔SDK contract has documented interop deltas (iss default devpay SDK vs devpay-lic Worker; activatePath defaults differ — implementation-status.md #7, p5-sdk-contract-audit.md). A versioned-contract decision is required before external SDK consumers. |
| 6 | Backup & recovery | NOT_DONE for the deployed stack | POST /api/v1/admin/backup exists only in Axum be/ (be/src/handlers.rs:1306 — SQLite VACUUM INTO + post-backup integrity check + audit row; registered be/src/lib.rs:171). be-cloudflare has no backup endpoint — no route in lib.rs. D1 recovery depends on Cloudflare platform features (Time Travel/export) that are unconfigured and unexercised → NOT_VERIFIED. No restore test has ever been executed. |
| 7 | Ops logs & incident response | DONE (mechanism) / NOT_DONE (process) | Mechanism: audit_events HMAC hash chain (NOUGAT_AUDIT_HMAC_KEY, audit_hash in checkout_impl.rs), /api/v1/admin/audit list, /api/v1/audit/verify + /api/v1/ledger/verify integrity endpoints (verify_impl.rs), security-events/usage read APIs, outbox redrive, recon reports (reconciliation_runs/findings persisted), act_as_seller per-request console+audit logging, http_error structured logs (method/path/status/code — no bodies/tokens). Missing: no on-call runbook, no alerting, no severity/incident classification process, needs_review refund/recon findings have no resolution owner or SLA. |
| 8 | Privacy & contract review | NOT_DONE — LEGAL_REVIEW_REQUIRED | data-protection-compliance.md (this wave) documents what the code does; no legal counsel has reviewed it. Open legal items: cross-border transfer posture (Cloudflare global network), ToS/liability terms, buyer-PI processor-vs-controller role, retention/deletion commitments vs. the schema-only retention_class/legal_hold columns (migration 007 — no enforcement job). Not legal advice. |
| 9 | Latest security evidence | DONE (with residual findings) | p4 battery (product-red-team.md, unmerged at this SHA): §8.1 red-team 12 legs → 11 BLOCKED, 1 PARTIAL (RT-SEC-11 planner-poison — location_hint.file tolerated as inert label, finding F-01 Low); §8.2 reliability 7 legs → 7 BLOCKED. Residual findings F-01..F-06 (1 Low, 5 Info) — see known-limitations.md. Prior 47-leg PoC battery: committed artifacts validated in CI (security-regression job, r18). |
2. Overall verdict
BETA_NOT_READY. Current assessed maturity: PRODUCT_CANDIDATE (see final-productization-report.md §1 for the level definitions).
Blocking items — each independently prevents a BETA_READY claim:
| Blocker | What is missing |
|---|---|
| B-1 Real payment path | No PG sandbox run with real TEST-mode provider roundtrip; live capture disabled by design (PAYMENT_LIVE_ENABLED=false). Toss TEST refetch is stubbed at 127.0.0.1:8799 in all executed evidence. BLOCKED_EXTERNAL_CREDENTIALS for anything beyond that. |
| B-2 Deployed security plane | LICENSE_SECURITY_ENABLED is absent from wrangler.toml in every environment — the PoP endpoints 404 wherever the worker is actually deployed. All PoP evidence is local-only; staging/prod behavior NOT_VERIFIED. |
| B-3 No external customer test | Zero non-author users have run the pipeline end-to-end; all evidence is self-generated on loopback. |
| B-4 Legal/privacy review | §1 item 8 — LEGAL_REVIEW_REQUIRED, unperformed. |
| B-5 Backup/restore on the real stack | No D1 backup/restore procedure or executed restore (item 6). |
| B-6 Contract versioning | SDK 0.1.0 with no wire-version negotiation and known Worker↔SDK interop deltas (item 5) — a breaking-change incident has no safe path today. |
| B-7 Outbox has no real consumer | OUTBOX_SINK_URL unset by default → every event dead-letters (consumer:none). Event-driven integrations (buyer notification, downstream webhooks) do not exist end-to-end. |
| B-8 Incident/ops process | No runbook, alerting, or needs_review resolution SLA (item 7). |
Non-blocking but tracked: F-01..F-06 (p4), 32-bit seller-issued license keys, rate-limit coverage limited to /auth/login, Axum be/ parity gap, node:vm non-boundary — full list in known-limitations.md.
3. Entry conditions for BETA_READY
- Real Toss test-mode end-to-end purchase + refund + webhook reconcile against the PG sandbox (or documented equivalent), executed and recorded.
LICENSE_SECURITY_ENABLED=trueon a staging worker with the PoP suite executed against it (staging, never production).- D1 backup + restore drill executed and documented.
- Versioned contract decision (wire version field or documented pinning policy) + Worker↔SDK interop matrix re-verified.
- Legal sign-off on
data-protection-compliance.mdopen items. - Ops runbook: alert path,
needs_reviewresolution owner, incident severity classes. - One external/sandbox seller completing init→protect→license→execute with filed feedback.