You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Polar-optional Cloud: what 'billing disabled' means #977
Parent map: #973. With no POLAR_ACCESS_TOKEN, billing is disabled and every Organization runs unlimited (decided). Decide: which capability checks exist in modules/billing today and what each returns when billing is off; whether plan/upgrade UI is hidden or shown as informational; whether the billing_plan column keeps free/solo/teams or gains an explicit unlimited; what the Inngest billing sync does when off; how the Polar webhook route responds when off.
Grilled 2026-09-23. "Billing disabled" means the self_hosted mode that already exists: no Polar env at all. A Self-hosted Cloud never configures Polar; billing is a Ployz-hosted concern only.
POLAR_ACCESS_TOKEN + POLAR_WEBHOOK_SECRET + one product ID
│
┌───────┴────────┐
set absent
"hosted" "self_hosted" ◄── names unchanged
│ │
one paid plan no billing
checkout/portal plugin absent ─► webhook/checkout/portal 404
webhook + nightly sync Inngest billing functions not registered
Billing page (1 card) nav item + route hidden
row per subscriber no rows, ever
custom domains gated custom domains always allowed ◄── the one meaning of "unlimited"
Answers to the ticket's questions
Capability checks in modules/billing today: one, the custom-domain capability, with no live caller. Kept and cut down: allowed when self-hosted, or when the Organization has an active subscription. Judged from the cached organization_billing_state row, never a live Polar call, so a Polar outage cannot block a deploy. One denial reason. Wired at the existing ponytail comment in service-operations.server.ts. This is the only plan-gated capability at 1.0, and it overrides the cleanup spec's "delete custom-domain capability gating".
Plan/upgrade UI when off: hidden entirely. No Billing nav item, no route. Not informational.
current_plan column:dropped, along with product id, amount, currency, period start, and the hobby/pro aliases. The row keeps organization id, active subscription id, active flag, current period end, and sync timestamps. Self-hosted never writes the table. No unlimited value exists anywhere.
Inngest billing sync when off: the sync and nightly reconcile functions are not registered in self-hosted mode. (Today the nightly cron fans out one failing, thrice-retried sync per Organization on every self-hosted install.)
Polar webhook route when off: stays 404, which is what the unmounted better-auth plugin already yields. Client-side Polar auth plugin stays, inert.
Decisions that fell out
One purchasable plan at launch. Free and Teams are dropped. The plan's name is unlocked and is UI copy only; nothing server-side stores or names it. A second plan for multi-member Organizations comes later and is not shown in the UI.
Free's signup hook goes. Today handleUserCreated creates a $0 Polar subscription inside the better-auth user-create hook, so a Polar outage fails sign-in. Account creation no longer touches Polar.
Deleted machinery: plan change, proration preview, change dialog, product price lookup, plan ranking, unknown-product flag, the "active paid" distinction, free/teams product IDs and checkout slugs.
Billing page on hosted: one card. "Get Started" opens embedded checkout; once subscribed, "Manage billing" opens the Polar portal, where cancellation happens.
Follow-ups (execution, not decisions)
Archive the Free and Teams products and their subscriptions in Polar production; the selector already ignores unknown products, but the nightly reconcile would keep seeing them.
Glossary updated in dashboard/CONTEXT.md: Self-hosted Cloud, new Billing Plan and Custom Domain Capability terms.
Activity
nick-potts commentedon Sep 23, 2026
Resolution
Grilled 2026-09-23. "Billing disabled" means the
self_hostedmode that already exists: no Polar env at all. A Self-hosted Cloud never configures Polar; billing is a Ployz-hosted concern only.Answers to the ticket's questions
modules/billingtoday: one, the custom-domain capability, with no live caller. Kept and cut down: allowed when self-hosted, or when the Organization has an active subscription. Judged from the cachedorganization_billing_staterow, never a live Polar call, so a Polar outage cannot block a deploy. One denial reason. Wired at the existing ponytail comment inservice-operations.server.ts. This is the only plan-gated capability at 1.0, and it overrides the cleanup spec's "delete custom-domain capability gating".current_plancolumn: dropped, along with product id, amount, currency, period start, and the hobby/pro aliases. The row keeps organization id, active subscription id, active flag, current period end, and sync timestamps. Self-hosted never writes the table. Nounlimitedvalue exists anywhere.Decisions that fell out
handleUserCreatedcreates a $0 Polar subscription inside the better-auth user-create hook, so a Polar outage fails sign-in. Account creation no longer touches Polar.Follow-ups (execution, not decisions)
dashboard/CONTEXT.md: Self-hosted Cloud, new Billing Plan and Custom Domain Capability terms.Record the billing-disabled glossary terms (#1001)
Make Cloud config and billing optional for self-hosters
Make Cloud config and billing optional for self-hosters (#1059)
Record the billing-disabled glossary terms (#1001)
Make Cloud config and billing optional for self-hosters (#1059)