Files
dashcaddy/PRODUCT-SPEC-DECISIONS.md
hermes b105d5abae
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled
PRODUCT-SPEC: clarify Free has no trial; LIFETIME is creator-only
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.
2026-07-20 21:22:34 -07:00

4.6 KiB

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
  • 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

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.