Documentation
Overview of what ships in the kit. The full guides — docs/setup.md, docs/licensing.md, docs/publishing.md — are in the repository you receive after purchase.
What you need
- Node 22+ and a GitHub account.
- A Cloudflare account (free tier is enough).
- A Paddle account. Sandbox is free and instant; production needs Paddle's approval, typically 1–3 days — apply early.
- A domain (optional for development, needed before you publish).
- Optionally a Resend account to email license keys (free tier: 3,000 emails/month).
Repository layout
paidextension.config.json single source of truth: product, company, URLs, plans, seats, trial, feature gates
extension/ MV3 extension (TypeScript, webpack, vitest)
worker/ licensing Worker (JS ESM + JSDoc, node:test, wrangler)
site/docs/ GitHub Pages root: landing, checkout, key, devices, privacy, terms, support
scripts/ init (rebrand), validate-config, generate-listing (+ tests)
docs/ setup, licensing, publishing guides
.agents/skills/ agent skills: setup, publish, test
.github/workflows/ ci.yml, pages.yml, release.yml
The setup path (about an hour)
- Rebrand.
npm run initasks for product name, company, support email, site URL, seats and trial days, then rewrites the config and every file mirroring it: manifests,wrangler.toml, the site's HTML and CSP,CNAME, tests. Non-interactive mode for agents:npm run init -- --from answers.json. - Signing keypair.
cd worker && npm run keygen -- --writegenerates an ES256 keypair, writes the public half into the extension and prints the private half for the Worker secret. Production builds refuse to ship while the development key is still in place. - Paddle. Create one product with monthly, yearly and one-time (lifetime) prices, a server API key, a client token and a webhook destination pointing at
/webhook. The guide lists the exact seven events to subscribe to. - Deploy the Worker.
wrangler kv namespace create, fourwrangler secret putcalls,npm run deploy.GET /healthreports which secrets and prices are configured. - Email (optional). Verify a sending domain in Resend and set the API key. Without it buyers still get Pro — the extension polls for its key after checkout.
- Site. Enable GitHub Pages for
site/docs, add a CNAME record, enforce HTTPS. - Walk the golden path in sandbox. Trial → upgrade → Paddle overlay → test card → Pro within a minute → key in Settings → activate on a second profile → release a seat.
- Go live. Swap in production Paddle ids and tokens;
npm run buildrefuses sandbox values. Then followdocs/publishing.mdfor the Chrome Web Store, Edge Add-ons and Firefox AMO.
Using an AI coding agent? Point it at .agents/skills/setup/SKILL.md. It walks the same steps and knows how to verify each one.
Licensing architecture
License keys are JWTs signed with ES256 (P-256). The private key lives only in Worker secrets; the public key ships in the extension, so a key verifies with no network call.
- Plans:
monthly,yearly,lifetime,trial. Subscriptions use the Paddle subscription id as subject; lifetime purchases use the transaction id. Unknown Paddle price ids fail closed. - Entitlement tokens are short-lived (3 days by default), bound to the install id and renewed by an alarm, so a refund or cancellation reaches the device within days even though keys verify offline. Lifetime keys still use them, so lifetime purchases remain revocable.
- Seats: a key activates on up to
licensing.maxSeatsinstalls (default 3).POST /seatslists devices andPOST /deactivatereleases one or all; possession of the signed key is the credential. - Trials:
POST /trialissues a time-boxed key per visitor id with per-IP limits.trialDays: 0disables trials. - Offline grace: if the Worker is unreachable, Pro stays on for
offlineGraceDayspast token expiry. - Revocation:
subscription.canceled,past_due,paused, refunds and chargebacks (adjustment.*) flip the stored status; the next token renewal fails and the extension drops to Free.
Worker routes
GET /health configuration booleans, never secrets
POST /checkout {plan} → Paddle transaction id
POST /trial {visitorId} → trial key
GET /license?txn= poll for the key after checkout
GET /status license status for a key
POST /activate {key, installId} → entitlement token
POST /deactivate {key, installId} | {key, all: true}
POST /seats {key} → plan, status, expiry, devices
POST /webhook Paddle (signature-verified)
POST /resend-webhook Resend delivery events (Svix-verified)
Costs
| Item | Cost |
|---|---|
| Cloudflare Workers + KV | Free tier: 100k requests/day, 1k KV writes/day |
| GitHub Pages | Free |
| Paddle | 5% + 50¢ per sale, no monthly fee; handles VAT/sales tax, invoices, refunds |
| Resend | Free tier: 3,000 emails/month |
| Domain | ~$12/year |
Commands
| Where | Command | What |
|---|---|---|
| root | npm run init | Rebrand from the sample product to yours |
| root | npm run validate:config | Config schema + cross-file consistency |
| root | npm run generate:listing | Store listing, permission justifications, data-use disclosure |
| root | npm run check | Everything below, in order |
| extension | npm run build / build:firefox / build:dev | Production, Firefox or dev bundle |
| extension | npm run package | Zips for store upload |
| worker | npm run keygen -- --write | New ES256 keypair |
| worker | npm run dev / deploy / tail | Wrangler |
| site | npm run check | ESLint, html-validate, link check, CSP check |
Agent skills
- setup — fresh clone to working paid extension: rebrand, keypair, Paddle sandbox, Worker deploy, Pages, golden-path verification.
- publish — bump version, run every check, build production packages, regenerate listing copy, tag a release, fill in the Chrome / Edge / Firefox dashboards.
- test — unit suites, build, load in Chrome, exercise trial / purchase / activation / seat limit / deactivate / offline grace / refund revocation against a local or deployed Worker.
AGENTS.md records the invariants: private keys only in Worker secrets, licensing code outside the service worker's hot path, strict per-page CSP on the site, unknown Paddle prices fail closed.
Support
Buyers get support through GitHub Issues on the private repository and by email at hello@paidextension.dev. Pre-sales questions are welcome at the same address.