Files
zpan/docs/roadmap/v2.6.md
T

14 KiB
Raw Blame History

v2.6 — Pro Launch

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.

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

Cloud Account Binding

The only licensing action exposed in ZPan is "Connect to ZPan Cloud". Everything else lives on the cloud side.

Binding flow (device-code style):

  1. Admin clicks "Connect to Cloud" in Settings → Billing
  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

Entitlement System

Cloud is the source of truth. ZPan fetches, caches locally, and verifies offline.

  • Entitlement certificate — Ed25519-signed document from cloud containing account_id, plan, features[], instance_id, issued_at, expires_at (24-hour TTL)
  • Ed25519 public key embedded in every ZPan build at compile time — verification is fully local, zero network calls per feature check
  • Refresh cadence — a background task refreshes the certificate every 6 hours using the stored refresh token
  • Offline tolerance — because the certificate is valid for 24 hours, a ZPan instance can stay offline up to 24 hours and still enforce Pro correctly
  • Hard fail after expiry — once the cached certificate's expires_at passes and cloud is unreachable, feature gates fall back to Free until the next successful refresh
  • Revocation is implicit — there is no separate revocation list. Because certificates are short-lived, unbinding on cloud takes effect within 24 hours at worst (or instantly if the ZPan instance is online at the next scheduled refresh)

Feature Gate Framework

New module server/licensing/ with three primitives:

  • verify(cert) — Ed25519 signature check + expiry check against the cached certificate
  • requireFeature(name) — middleware for API routes; returns 402 Payment Required when the feature is not in the current entitlement
  • hasFeature(name) — synchronous boolean for UI conditional rendering

Frontend components:

  • <ProBadge /> — small "Pro" tag rendered next to gated features in menus / settings
  • <UpgradeHint feature="..." /> — block-level upsell shown in place of a gated UI region; CTA goes to "Connect to Cloud" (if unbound) or "Manage on Cloud" (if bound but feature not in plan)

Billing Dashboard

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

  • Custom site logo — replaces ZPan wordmark in nav and auth pages
  • Custom favicon
  • Custom wordmark text — replace "ZPan" in page titles and copy
  • Remove "Powered by ZPan" footer — Free deployments keep it, Pro can hide it
  • Asset storage — uploaded to _system/branding/ in the instance's own S3 bucket
  • Admin pageSettings → 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

Retroactive Pro Gates

Features shipped in earlier versions that are operator-grade (help you run ZPan as a service for others) become Pro-only starting in v2.6. The framework is the same: if you are the administrator of your own instance using ZPan yourself, nothing changes. If you are running ZPan as a platform for unknown third parties, you'll hit these gates.

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 > 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 one shared team workspace; anyone building past that is running ZPan as a platform.

3. Storage backend count

  • Free instances can add up to 3 storages.
  • Adding a 4th storage requires Pro.

Everything else from v2.0v2.5 stays Free:

  • Social login, OIDC (single IdP), invite codes (single and bulk generation)
  • 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

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 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
  • Refund automation — manual via Stripe dashboard on cloud side
  • Chinese local payment (Alipay / WeChat Pay) — Stripe-only at launch, evaluated post-launch

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.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.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 — 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