docs: verify pro quota store release notes (#364)

Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98
This commit is contained in:
Jasper Van
2026-05-05 17:13:54 -04:00
committed by GitHub
parent 5eac0f4642
commit 05b14a4991
2 changed files with 167 additions and 27 deletions
+80 -19
View File
@@ -1,14 +1,16 @@
# v2.6 — Pro Launch
Introduce ZPan Pro. Ship the cloud-account binding flow, the entitlement verification layer, the feature-gating framework, and a single visible Pro feature — white-label — so there is something concrete to sell on day one.
Introduce ZPan Pro. Ship the cloud-account binding flow, the entitlement verification layer, the feature-gating framework, a self-service quota store, and a visible Pro feature — white-label — so there is something concrete to sell on day one.
**Activation is never local.** Purchase, subscription management, and redemption-code activation all happen on `cloud.zpan.app`. ZPan instances only *bind* to a cloud account; their Pro status mirrors the bound account's entitlement automatically. There is no token to paste and no redemption code field inside ZPan.
**Generic Pro activation remains Cloud-owned.** Subscription purchase, subscription management, generic Pro activation, membership redemption codes, and entitlement source-of-truth all live on `cloud.zpan.space`. ZPan instances only _bind_ to a cloud account for Pro status; their Pro status mirrors the bound account's entitlement automatically. There is no generic Pro token to paste into ZPan.
**Product direction update:** v2.6 also adds a scoped storage-quota store inside ZPan. This does not move generic Pro activation into ZPan. It lets a Pro instance operator define a per-instance storage package catalog, while Cloud remains the merchant of record and Stripe integration owner. Terminal storage buyers do not need ZPan Cloud accounts.
Pro features can be added continuously in later versions. This version's job is to make the **machinery** for paid features exist.
## Depends on
- [cloud v1.0](https://github.com/saltbo/zpan-cloud/blob/main/docs/roadmap/v1.0.md) — cloud accounts, instance pairing, cloud dashboard (purchase + redemption + subscription management), entitlement API must be live
- [cloud v1.0](https://github.com/saltbo/zpan-cloud/blob/main/docs/roadmap/v1.0.md) — cloud accounts, instance pairing, cloud dashboard (subscription purchase + membership redemption + subscription management), entitlement API, anonymous storage checkout, storage-code redemption, and signed quota delivery callbacks must be live
## Major Features
@@ -19,16 +21,17 @@ The only licensing action exposed in ZPan is "Connect to ZPan Cloud". Everything
**Binding flow (device-code style)**:
1. Admin clicks "Connect to Cloud" in `Settings → Billing`
2. ZPan calls `POST cloud.zpan.app/api/pairings` — receives a short human-readable code (e.g. `ABC-123`) and a pairing URL
3. ZPan displays: "Visit **cloud.zpan.app/pair** and enter code `ABC-123`"
4. User signs in (or signs up) on cloud.zpan.app, enters the code, confirms the pairing (instance host + instance name shown for verification)
5. ZPan polls `GET cloud.zpan.app/api/pairings/{code}` every 5 seconds
2. ZPan calls `POST cloud.zpan.space/api/pairings` — receives a short human-readable code (e.g. `ABC-123`) and a pairing URL
3. ZPan displays: "Visit **cloud.zpan.space/pair** and enter code `ABC-123`"
4. User signs in (or signs up) on cloud.zpan.space, enters the code, confirms the pairing (instance host + instance name shown for verification)
5. ZPan polls `GET cloud.zpan.space/api/pairings/{code}` every 5 seconds
6. Once the user approves, cloud returns a long-lived refresh token + the first entitlement certificate
7. ZPan stores the refresh token in its database (admin-only read)
Device code is chosen over OAuth redirect because ZPan instances often run on internal networks, homelab IPs, or behind NAT — requiring a public return URL would break those deployments.
**Disconnect** (either side can initiate):
- In ZPan: "Disconnect from Cloud" — deletes local credentials, entitlement certificate is discarded, feature gates lock immediately
- In cloud dashboard: "Unbind instance" — ZPan's next refresh returns `401 Unbound`, feature gates lock on that refresh
@@ -58,21 +61,63 @@ Frontend components:
### Billing Dashboard
`Settings → Billing` page. Two states only — bound or unbound. Zero purchase UI, zero redemption UI.
`Settings → Billing` page. Two states only — bound or unbound. Zero generic Pro purchase UI, zero membership redemption UI.
**If unbound (Free)**:
- Comparison table: Free vs Pro feature list
- Primary CTA: "Connect to ZPan Cloud" → launches the device-code binding flow
- Short explainer: "Create or sign in to your ZPan Cloud account, then activate Pro there. This instance will mirror your account's Pro status automatically."
**If bound**:
- Cloud account identity (email or username, fetched from cloud)
- Plan name + current entitlement expiry (next refresh time shown for transparency)
- Feature list (every feature unlocked under the current plan)
- Primary CTA: "Manage on Cloud" → opens `cloud.zpan.app/dashboard` in a new tab (this is where the user does purchase, renewal, payment method changes, invoices, redemption-code entry)
- Primary CTA: "Manage on Cloud" → opens `cloud.zpan.space/dashboard` in a new tab (this is where the user does Pro purchase, renewal, payment method changes, invoices, and membership redemption-code entry)
- Secondary CTA: "Disconnect from Cloud"
- Last-refresh timestamp + manual "Refresh now" button (for support scenarios where a just-completed purchase on cloud should reflect immediately)
### Pro Quota Store
The quota store is a scoped addition to the original v2.6 Pro plan. It gives Pro operators a storage upsell path for their own instance users without making those users create Cloud accounts.
Ownership:
- **Cloud is merchant of record in v2.6.** Cloud owns Stripe Checkout, Stripe paid webhooks, storage-code inventory, and delivery retries.
- **The ZPan operator defines the package catalog per instance.** Package name, description, bytes, amount, currency, active state, and sort order are configured in ZPan admin.
- **Terminal buyers do not need Cloud accounts.** ZPan creates signed purchase or redemption sessions and sends the user to Cloud only for checkout/redeem handling.
- **Quota grants are permanent additive credits in v2.6.** For finite base quotas, effective quota is `base org quota + active quota grants`; admin base quota edits do not erase paid grants. The existing `0` base quota sentinel still means unlimited.
- **Store requires a public HTTPS Cloud callback endpoint while enabled.** Admin configures the public ZPan URL and the shared signing secret so Cloud can call `POST /api/quota-store/webhooks/cloud`.
Admin flow:
1. Pro admin opens `Settings -> Quota Store`
2. Admin enables the store, sets Cloud base URL, public instance URL, and webhook signing secret
3. ZPan displays the callback URL Cloud must reach over public HTTPS
4. Admin creates active storage packages
5. ZPan syncs the catalog to Cloud through `POST /api/store/packages/sync` using `x-zpan-store-*` signatures
6. Admin confirms each package has synced Cloud status before it becomes purchasable
Terminal-user flow:
1. User opens the ZPan Store from the quota meter without signing in to Cloud
2. User selects a personal or team target org they belong to
3. Checkout calls `POST /api/quota-store/checkout`, which validates target access and creates a signed Cloud storage session
4. Cloud creates Stripe Checkout from the package snapshot
5. Stripe paid event marks the Cloud order paid
6. Cloud sends a signed `x-zpan-cloud-*` delivery callback to ZPan
7. ZPan verifies the signature, records the delivery event idempotently, appends one quota grant, and increases the target org's effective quota
8. The quota meter and grant history refresh
9. If the user had a draft upload blocked by quota, confirm/upload can be retried while the draft still exists
Storage-code redemption:
- ZPan Store accepts storage codes separately from membership redemption.
- The user selects the target org, submits the code, and ZPan sends a signed redemption session to Cloud.
- Cloud validates storage-code inventory and delivers quota back through the same signed callback path.
- Membership redemption remains Cloud dashboard behavior and does not share this ZPan Store flow.
### First Pro Feature: White-Label
Pure client-side. No cloud dependency beyond the entitlement gate. Demonstrates upgrade value visually.
@@ -85,6 +130,7 @@ Pure client-side. No cloud dependency beyond the entitlement gate. Demonstrates
- **Admin page** — `Settings → Branding`: upload logo, favicon; edit wordmark text; preview live
Deliberately excluded from v2.6 (deferred to later Pro versions):
- Custom brand colors and fonts
- Per-tenant branding (when multi-tenant ships)
- Custom email templates
@@ -96,22 +142,26 @@ Features shipped in earlier versions that are **operator-grade** (help you run Z
Three gates in v2.6:
**1. Open registration mode** (from v2.1)
- Registration modes `closed` and `invite-only` remain **Free**.
- Registration mode `open` (anyone can sign up via the public form) becomes **Pro**.
- Without a Pro license, `open` is rejected; registration mode falls back to `invite-only`.
**2. Team count ≤ 3** (from v2.2)
**2. Team count > 1 extra team** (from v2.2)
- Free instances can create **1 personal workspace plus up to 1 additional team**.
- 3rd org creation is blocked in UI; API returns 402.
- Rationale: a single person or family team doesn't need more than 3 team workspaces; anyone building past that is running ZPan as a platform.
- Rationale: a single person or family team doesn't need more than one shared team workspace; anyone building past that is running ZPan as a platform.
**3. Storage backend count**
**3. Storage backend count**
- Free instances can add up to **3 storages**.
- Adding a 4th storage requires **Pro**.
Everything else from v2.0–v2.5 stays Free:
- Social login, OIDC (single IdP), invite codes (single and bulk generation)
- Team workspaces (up to 3), shared folders, member roles, activity feed, public user profile
- Team workspaces within the Free limit, shared folders, member roles, activity feed, public user profile
- All sharing features (password, expiration, download limits, direct links)
- All image hosting (upload API, PicGo / ShareX integration, per-org custom domain, hotlink protection)
- All 7 deployment targets
@@ -119,9 +169,9 @@ Everything else from v2.0–v2.5 stays Free:
## Non-goals for v2.6
- **Token paste activation** — the old model of "purchase → receive token email → paste into ZPan" is deliberately replaced by account binding
- **In-ZPan purchase UI** — all purchase flows live on `cloud.zpan.app`
- **In-ZPan redemption code input** — redemption also lives on cloud dashboard
- **Metered / usage-based billing** — no cloud-dependent features yet, not needed
- **In-ZPan generic Pro purchase UI** — Pro subscription purchase flows live on `cloud.zpan.space`
- **In-ZPan generic membership redemption input** — membership redemption lives on the cloud dashboard; only storage-code redemption is accepted in the ZPan quota store
- **Metered / usage-based billing** — quota packages are fixed additive credits in v2.6, not usage-metered subscriptions
- **Team / Business tiers** — single Pro SKU only
- **Multi-seat** — one Pro subscription binds one ZPan instance
- **Multi-bind per account** — a single Pro subscription can only bind one active instance. Binding a second instance on the same Pro account requires unbinding the first, or a future Team plan
@@ -131,15 +181,26 @@ Everything else from v2.0–v2.5 stays Free:
## User Scenarios
**Indie dev launching a paid service:**
> I run ZPan on my VPS. I click "Connect to Cloud", see a pairing code, go to cloud.zpan.app/pair on my laptop, sign in with GitHub, confirm "Bind instance at files.mycompany.com". Back in ZPan the UI shows "Bound to alice@example.com · Plan: Free". I click "Manage on Cloud", subscribe to Pro, and within 30 seconds my ZPan lights up as Pro — I go to Settings → Branding, upload my logo, and my instance looks like my product.
> I run ZPan on my VPS. I click "Connect to Cloud", see a pairing code, go to cloud.zpan.space/pair on my laptop, sign in with GitHub, confirm "Bind instance at files.mycompany.com". Back in ZPan the UI shows "Bound to alice@example.com · Plan: Free". I click "Manage on Cloud", subscribe to Pro, and within 30 seconds my ZPan lights up as Pro — I go to Settings → Branding, upload my logo, and my instance looks like my product.
**Contributor who got a code:**
> I submitted a solid PR. The maintainer sends me a ZPan Pro code good for 1 year. I already have a cloud.zpan.app account, so I sign in there, paste the code on the "Redeem" page. My cloud account is now Pro. I go back to my self-hosted ZPan, click "Connect to Cloud", pair it to my account, Pro unlocks. No code ever touched my ZPan instance.
> I submitted a solid PR. The maintainer sends me a ZPan Pro code good for 1 year. I already have a cloud.zpan.space account, so I sign in there, paste the code on the "Redeem" page. My cloud account is now Pro. I go back to my self-hosted ZPan, click "Connect to Cloud", pair it to my account, Pro unlocks. No code ever touched my ZPan instance.
**Terminal user buying more storage:**
> I am a regular user on someone else's ZPan instance and my upload hits quota. I click Add storage from the quota meter, choose my personal workspace or team, and pick a storage package. Cloud opens Stripe Checkout without asking me to create a Cloud account. After payment, Cloud sends the signed delivery callback and my ZPan quota meter shows the purchased storage. I retry the blocked upload while the draft is still present.
**Terminal user redeeming a storage code:**
> I receive a storage code from the instance operator. In ZPan Store, I select the target team and redeem the code. The storage grant appears in my grant history and the team's effective quota increases. This does not activate Pro membership and does not use the Cloud membership redemption page.
**Free user hits a gate:**
> I try to create a 2nd team workspace. The "New Team" button shows a Pro badge; clicking it opens a dialog: "Free includes one personal workspace plus up to one team. Pro removes the limit. **Connect to Cloud** to upgrade." I click through, sign up for a cloud account, and from the cloud dashboard I either start a trial or redeem a code. The moment my account has Pro, my ZPan instance reflects it.
## v1 Issues Resolved
- **#18** Purchasable storage — reframed as ZPan Pro via bound cloud account (storage is still BYO S3)
- **#18** Purchasable storage — shipped as Pro quota store: Cloud remains merchant of record, ZPan operator defines packages, and storage remains BYO S3
- **#161** Custom logo — shipped as the first Pro feature under white-label