Files
vantage/docs/superpowers/specs
mrhid6andClaude Opus 5 ff0caf5a90 docs: spec 7, metered licensing and the two-by-three plan matrix
Two deployments times three tiers, servers metered per month, console and
SSO opted into individually. plans is re-keyed on (deployment, tier); every
Paddle price ID moves out of plans into a new catalogue collection; a new
entitlements collection holds desired beside granted, and a licence is only
ever signed from granted.

Spec 5's plan is revised rather than followed: it assumes one price per
subscription and a metered plan has three or more. Nothing of it has
shipped, so the revision costs a rewrite of an unstarted plan.

Free stops being cloud-only by construction, which means the plan/instance
deployment comparison in licensing.Issue no longer enforces it and
checkFreeLimit has to count per deployment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 23:16:35 +01:00
..

Vantage Licensing Programme — Spec Index

Build in this order. Specs 0a5 were designed 2026-07-24; spec 6 on 2026-07-26.

# Spec Plan Status
0a shared-module plan shipped
0b instance-rename plan shipped, migration verified on live
1 licensing-core plan shipped
2 instance-licensing plan shipped, no grandfathering — existing cloud instances are read-only until admin backfills
3 admin-backend plan shipped, verified end to end against scratch databases
4 admin-site ready to start
5 paddle-billing plan ready to start, but revised by 7 — its "signup migration off sitesvc" section is superseded by 6, and its single-price-per-subscription assumption by 7
6 cloud-instance-creation ready to start
7 metered-licensing designed; build before 5, whose plan it revises

Specs 1 and 2 together give working licensing with licences cut by hand with lkctl — no admin service needed. 4 and 5 can run in parallel once 3 lands.

7 lands before 5. It re-keys plans on (deployment, tier), moves every Paddle price ID out of plans into a new catalogue collection, and adds the entitlements collection that both a subscription and a licence are derived from — all of which plan 5 builds on top of, so building 5 first would mean writing its billing code twice.

The shape

Account (admin only)
 ├── Instance 1  cloud        vantage.hostxtra.co.uk/<slug>   licence auto-injected
 ├── Instance 2  cloud                                        licence auto-injected
 └── Instance 3  self-hosted  customer's own deployment       licence pasted by hand

The control plane knows only Instance. Accounts exist solely in the admin service, because a self-hosted instance has no row in the cloud database at all.

Decisions that everything else follows from

Licences are offline-verified signed blobs. ECDSA P-384 with SHA-256 via github.com/hyperboloide/lk, public key compiled into the server, no phone-home anywhere. This buys air-gapped self-hosting and means no Vantage instance ever depends on the licensing service being up. It costs revocation: a licence is valid until it expires whatever Paddle later says. Self Hosted is annual-only to bound that window.

Every licence is bound to one instance UUID. Self-hosted customers link their UUID before the licence is signed, so there is no unbound licence and no claim protocol.

Expiry degrades, it does not break. Monitors keep executing, alerts keep firing, agents keep their keys, in-flight workflow runs finish. Mutations stop. Deletes and OS-update application stay open so a customer is never trapped over-limit or unpatched.

Tiers are data, not code. The server reads Limits and Features and never branches on tier name. Tier contents live in the admin plans table and are snapshotted into each issued licence, so editing a plan never rewrites history — the same rule as workflow_runs.steps_snapshot.

Spec 7 replaces the three-tier table below with two deployments times three tiers, and makes the server count a metered quantity rather than a fixed allowance. See metered-licensing for the current grid. As shipped through spec 3, the table is:

Free Professional Self Hosted
deployment cloud only cloud self-hosted
max servers 3 unlimited unlimited
max secret groups 1 unlimited unlimited
max channels 1 unlimited unlimited
console no yes yes
OIDC no yes yes
term monthly, £0 monthly or annual annual only

Free is cloud-only by construction: it is only ever signed with deployment: "cloud", and verification rejects a deployment mismatch. There is no server-side flag to edit. One Free instance per account.

Spec 7 ends that construction-level guarantee — there is a self-hosted Free plan, so plan.Deployment != inst.Deployment no longer implies it, and the Free limit becomes one per account per deployment.

Existing cloud tenants are not grandfathered. The migration that would have done it was removed before plan 2 shipped, so every existing cloud instance is read-only until it is licensed by hand through the admin service: attach it to an account with POST /api/staff/instances, then POST /api/staff/instances/:id/issue. That flow is verified in plan 3, so it works today via the API and is the first job the admin UI is used for.