From b105d5abae4ebeac3930e9c90e5b50ec60743ca0 Mon Sep 17 00:00:00 2001 From: hermes Date: Mon, 20 Jul 2026 21:22:34 -0700 Subject: [PATCH] PRODUCT-SPEC: clarify Free has no trial; LIFETIME is creator-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sami's explicit decisions: - Free is completely free — no Pro trial, no time-limited upsells. Pro is a deliberate paid choice. Update pricing language in PRODUCT-SPEC-DECISIONS.md so anyone reading it doesn't assume there's a hidden trial period. - Lifetime keys are creator-only (Sami runs license-keygen.js --lifetime on his dev machine; the API rejects LIFETIME codes at verifyCode time). No paid customer can ever buy or receive a lifetime key — they get 30/90/180/365-day keys. Update both PRODUCT-SPEC-DECISIONS.md and DC-052 in BACKLOG.md to spell this out so the Stripe webhook (DC-054) doesn't accidentally generate a LIFETIME for a buyer. --- BACKLOG.md | 2 +- PRODUCT-SPEC-DECISIONS.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/BACKLOG.md b/BACKLOG.md index be4e9df..71a60ea 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -309,7 +309,7 @@ Sami explicitly stated he wants email auth as an OPTION alongside TOTP, not a re ### DC-052: License-tier enforcement — Free caps user count at 3, gates share features on Pro - **status:** todo - **owner:** unclaimed -- **details:** Per `/root/dashcaddy/PRODUCT-SPEC-DECISIONS.md` (locked 2026-07-20): Free = up to 3 users, Pro = unlimited. The DC-048 user-store needs a `countUsers()` helper. The `/api/v1/auth/admin/invites` POST handler must check `if (users.count() >= 3 && !licenseManager.isPro()) throw new ValidationError('upgrade required', 'tier')`. Same check on `POST /admin/users` (pre-authorize). Share-link creation routes (DC-053) gate on `licenseManager.isPro()`. LIFETIME keys are admin-only — the license-keygen CLI does not expose `--lifetime` publicly; only Sami runs the `isLifetime` path on his dev machine. The API never accepts a LIFETIME code in production (rejects at `verifyCode` time if duration is 0 outside dev). +- **details:** Per `/root/dashcaddy/PRODUCT-SPEC-DECISIONS.md` (locked 2026-07-20): Free = up to 3 users, Pro = unlimited. The DC-048 user-store needs a `countUsers()` helper. The `/api/v1/auth/admin/invites` POST handler must check `if (users.count() >= 3 && !licenseManager.isPro()) throw new ValidationError('upgrade required', 'tier')`. Same check on `POST /admin/users` (pre-authorize). Share-link creation routes (DC-053) gate on `licenseManager.isPro()`. **Free has NO trial path** — there is no automatic Pro trial, no time-limited upsell. The user picks Free or Pro deliberately. **LIFETIME keys are creator-only**: the API rejects any LIFETIME code at `verifyCode` time in production. The `license-keygen.js --lifetime` path stays on Sami's dev machine only; it's never wired to Stripe Checkout. - **impact:** First pricing enforcement. Without this, Pro is just a label. With this, every upgrade path has a clear moment to upsell. - **prerequisite:** DC-048 (shipped). diff --git a/PRODUCT-SPEC-DECISIONS.md b/PRODUCT-SPEC-DECISIONS.md index 1d84883..a72cdad 100644 --- a/PRODUCT-SPEC-DECISIONS.md +++ b/PRODUCT-SPEC-DECISIONS.md @@ -19,6 +19,8 @@ - USD only (defer multi-currency to v1.1) - Stripe-standard 30-day refund - No launch pricing — list prices as-is +- **Free is completely free. No Pro trial. Pro is a deliberate paid choice.** +- **Lifetime keys are creator-only.** Only Sami (the creator) can issue a LIFETIME key via `license-keygen.js --lifetime` on his dev machine. The production API rejects any LIFETIME code at `verifyCode` time. No one else ever gets a permanent key — every other paid customer gets a 30/90/180/365-day key. ## 2. Tier features