diff --git a/PRODUCT-SPEC-DECISIONS.md b/PRODUCT-SPEC-DECISIONS.md new file mode 100644 index 0000000..1d84883 --- /dev/null +++ b/PRODUCT-SPEC-DECISIONS.md @@ -0,0 +1,80 @@ +# DashCaddy Product-Spec Decisions — Locked 2026-07-20 + +> All decisions captured from clarifying questions with the operator. This +> file is the source of truth for what gets built next. The narrative +> PRODUCT-SPEC.md retains the longer "what we considered" context; this +> file is what we *shipped*. + +## 1. Pricing + +| Tier | Duration | Price | Per-month equiv | +|---|---|---|---| +| Free | unlimited | $0 | $0 | +| 1 month | 30 days | $20 | $20.00 | +| 3 months | 90 days | $50 | $16.67 (17% off) | +| 6 months | 180 days | $70 | $11.67 (42% off) | +| 12 months | 365 days | $99 | $8.25 (59% off) | + +- Stripe Checkout only (no Paddle for v1.0) +- USD only (defer multi-currency to v1.1) +- Stripe-standard 30-day refund +- No launch pricing — list prices as-is + +## 2. Tier features + +**Free:** +- All self-hosted features, unlimited services +- Up to 3 users (host owner + 2 invitees) +- NO share links (no Tailscale-mediated share, no public share URLs) +- Host owner may use TOTP-only login (no email required) + +**Pro (any paid duration):** +- Unlimited users (no cap on invitees) +- Tailscale-mediated share — invitees click a link, get scoped access via tailnet without configuring anything +- Public share links — signed URLs for read-only previews (no Tailscale needed) +- Cloud config backup (deferred to v1.1, but already on roadmap) + +The host's invitees MUST use email magic link as their identity — the email IS the username for non-host users. The host themselves can stay TOTP-only. + +## 3. Account / license model + +- **Use existing `license-keygen.js`** (HMAC-signed 16-byte codes; VALID_DURATIONS = [30, 90, 180, 365]). +- License keys are per-host. One license = one host. Multi-host deferred to post-v1.0. +- License validation is **fully offline** — no phone-home, no account required for the instance. +- Purchase flow: + 1. User picks tier on dashcaddy.net/pricing + 2. Stripe Checkout → success page shows license key + 3. Receipt email includes the license key as backup + 4. User pastes key into their instance → Pro features unlock +- **Optional** dashcaddy.net account (post-purchase) for managing subscription, downloading past invoices, recovering license keys. Deferred to v1.1. + +## 4. Invitee auth flow + +When host enables email auth via `siteConfig.authProviders.email.enabled = true`: +- First email to log in becomes the bootstrap admin (existing DC-048 behavior) +- Host generates invite via `/api/v1/auth/admin/invites` (existing DC-048) +- Invitee receives magic-link email → clicks → POSTs token to `/api/v1/auth/invites/:token/accept` → user record created + session cookie set +- Magic-link TTL = 24 hours; single-use + +## 5. What we deferred to post-v1.0 + +- Multi-host support (one license = one host for v1.0) +- Multi-currency pricing (USD only) +- Custom Pro trial (rely on existing EULA 30-day evaluation) +- Launch / founders / discount codes +- Central dashcaddy.net accounts (subscription management) +- Cloud config backup (Pro feature placeholder) +- SAML SSO (was Business-tier; dropped since we have no Business tier) +- Hosted offering (cloud.dashcaddy.net — separate ops burden, deferred entirely) + +## 6. Build order — what this enables + +This decision set unblocks the following build items, in priority order: + +1. **License-tier enforcement in the API.** Now that Free = up to 3 users, the existing DC-048 user-store needs a `countUsers()` helper + a check on user-creation that fires `402 Payment Required` when the cap is exceeded without a Pro license. (DC-052) +2. **Pro-gated share-link routes.** Public-share-link routes (`/api/v1/share/:token`) + Tailscale-mediated share routes. Both gated on `licenseManager.isPro()`. (DC-053) +3. **License-keygen CLI improvements.** The existing tool already supports the 4 durations. Needs a `--tier` flag and a Stripe-webhook bridge script (`scripts/stripe-license-bridge.js`) that converts a Stripe Checkout success → license key + email. (DC-054) +4. **dashcaddy.net pricing page.** Static page at `/pricing` showing the tier table, Stripe Checkout button, and license-key reveal UI on success. (DC-055) +5. **Compliance minimums.** ToS + Privacy Policy at `/legal/tos` and `/legal/privacy`. GDPR-aware, no SOC2/HIPAA. (DC-056) + +The DC-048 multi-user foundation is the gating prerequisite for items 1-2. That foundation already shipped. \ No newline at end of file