feat: plan states and caps for billing, off until BILLING_STARTS_AT by gnapse · Pull Request #144 · klipstack/klipstack · GitHub
Skip to content

feat: plan states and caps for billing, off until BILLING_STARTS_AT - #144

Merged
gnapse merged 3 commits into
mainfrom
feat/billing-groundwork
Oct 20, 2026
Merged

gnapse merged 3 commits into
mainfrom
feat/billing-groundwork

Conversation

@gnapse

@gnapse gnapse commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Why

Klipstack will charge for hosted accounts: a 30-day trial, then a paid plan, with existing accounts free for a while. This lays the ground in the database and the caps before any payment provider (Polar comes later). Nothing changes until BILLING_STARTS_AT is set.

What changes

  • From BILLING_STARTS_AT, each account has a plan: a 30-day trial for accounts made after it, the paid plan free for a year from it for older ones, always free for admins, or an admin's "free until" date, which replaces either. Worked out in SQL (packages/db/src/plan.ts), so web and worker agree.
  • Caps follow the plan: trial (300 saves, 500 MB, 50 AI…) or paid (2,000 saves, 2 GB, 300 AI…), each its own LIMIT_TRIAL_* / LIMIT_PAID_* setting.
  • A lapsed account reads, exports, organizes and deletes, and its public pages stay up. Anything new (saves, feeds, AI, publishing or updating editions and shares, highlights, notes) gets a new error, 402 subscription_required, from the commands, so web, API and MCP agree. The worker stops its feeds and topic runs.
  • Admin account page: a Plan card with the state and a "free until" field, audited.
  • A start date instead of an on/off switch: it also dates the founding year, so nothing is stored per account.
  • Tags, saved searches and edition drafts stay open to lapsed accounts (owner's call): both already have per-account maximums.

Verification

  • pnpm verify passes. New tests cover plan dates and caps, every refusal above, the API's 402, the admin change and its audit event, and the worker's filters; each was seen failing without its code.
  • Demo app with billing on: the admin page in the trial, founding-year and lapsed states; setting and clearing "free until" moved an account from lapsed to free and back, with both audit events.
  • A review (task, commands, security, migrations, i18n, public repo, tests) found that highlight notes, edition and share-link updates and non-monthly topic runs were still open to lapsed accounts, that an admin's past date did not end a trial, and that day arithmetic depended on the database's time zone; all fixed, with tests. Dismissed: re-extracting a failed save (keeps what the account has), the trimming difference in parsing the date.
  • Not run: Playwright specs.

Risk and follow-ups

  • Migration: one new, empty table, account_billing.
  • Self-hosters: nothing to do; the docs say to leave BILLING_STARTS_AT unset.
  • The web app, extension and iPhone app have no message of their own for subscription_required yet: the add-feed dialog would say the link is invalid. Needed before billing starts, with the trial banner.
  • Later: Polar checkout and webhooks, deleting lapsed accounts after the readable period, terms of payment.

gnapse and others added 3 commits October 20, 2026 01:16
Accounts get a plan worked out from their sign-up date: a 30-day trial for
new ones, a free year for those made before billing starts, or an admin's
free-until date. Caps follow the plan; a lapsed account keeps reading and
exporting but is refused anything new with 402 subscription_required, and
the worker stops its feeds and monthly topics. Unset, nothing changes.

Amp-Thread-ID: https://ampcode.com/threads/T-01a151f5-d1bb-745b-9889-2cc115dd4227
Co-authored-by: Amp <amp@ampcode.com>
…and topic runs

Also lets an admin's date end a trial, counts plan days as 24 hours
whatever the session's time zone, and refuses a lapsed feed follow
before fetching it.

Amp-Thread-ID: https://ampcode.com/threads/T-01a151f5-d1bb-745b-9889-2cc115dd4227
Co-authored-by: Amp <amp@ampcode.com>
@gnapse
gnapse enabled auto-merge (squash) October 20, 2026 01:42
@gnapse
gnapse merged commit 6ff88a8 into main Oct 20, 2026
10 checks passed
@gnapse
gnapse deleted the feat/billing-groundwork branch October 20, 2026 01:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant