Skip to content

PayGlue-OS v0.3.0 Nothing phones home any more

Choose a tag to compare

@Nuenni Nuenni released this 06 Aug 02:26
· 12 commits to main since this release
3dc977a

Self-hosting actually works now

Everything here comes back to one thing: a self-hosted install was quietly tied to the hosted product. It pointed at that product's servers, inherited its defaults, signed its emails with a stranger's name, and told you your own setup was broken. If you run PayGlue yourself, this is the release that fixes that.

⚠️ Upgrade notes

Re-copy your embed snippets. If you pasted a paywall, buy button or pricing table snippet into your site before this release, it still carries a fixed api.payglue.io address, which means your readers' browsers talk to a server you do not control. Open the relevant page in your dashboard and copy the snippet again. Same for webhook URLs you handed to a payment provider.

Run migrations. 0021_stable_entitlement_key rewrites pricing-table entitlement keys. Two tiers pointing at the same product collapse into one mapping; the oldest wins, and the resolver already skipped the duplicate.

Settings that changed, and what happens if you skip them:

Setting Without it
DEFAULT_FROM_EMAIL Nothing sends. It no longer defaults to a domain you do not own
INTERNAL_ADMIN_EMAIL Operational notices are skipped. They used to go to a fixed address
SYSTEM_NOTICE_FROM_EMAIL Falls back to DEFAULT_FROM_EMAIL
DJANGO_ALLOWED_HOSTS Loopback only. It used to name four hostnames of the hosted product
PUBLIC_APP_BASE_URL Emails go out without links, and checkout returns are rejected for anything but loopback
PUBLIC_API_BASE_URL Falls back to the request origin. Set it if the backend sits behind a proxy
VITE_SUPPORT_EMAIL The support contact is hidden rather than naming an inbox nobody reads
VITE_PASSWORD_SIGNIN_EMAILS Everyone signs in with a magic link
DEV_BYPASS_EMAILS The invite gate applies to everyone. It used to ship with an address already in it

Onboarding emails now ship switched off. They used to be seeded on, carrying copy signed with one person's name and linking to one particular dashboard. There is no admin console in this repository, so nobody could have turned that off without going into the database. The seeded copy is plain now. Read it, then enable it.

No product analytics at all. The analytics module used to carry the hosted product's project key, so any build of it reported there. There is no analytics package in this build, nothing is sent anywhere, and there is no key to configure. To wire up your own, replace frontend/src/lib/posthog.ts; its four signatures are the whole contract.

Fixed

  • Embed snippets and webhook URLs pointed at the hosted product. The three embedded scripts now work out their own backend by reading the script tag they were loaded from, so there is nothing to configure and it stays correct behind a proxy.
  • The "is the paywall installed?" check could never succeed on a self-hosted install. It compared your page source against a fixed hostname. It matches on the path now.
  • Checkout could not complete. The allowed return hosts were a fixed list.
  • The support status notice crashed. tenants/support.py read a setting that was never defined in this repository, so the path raised AttributeError.
  • The invite gate shipped with a built-in bypass address.
  • Paywall access ignored genuine Stripe subscribers. Access is granted for paid, comped, or a payglue-active label. Switching a post from Ghost's own visibility to the PayGlue paywall used to lock out your longest-paying readers.
  • Access was marked with a combined label, so a reader who bought through one provider and renewed through another silently lost access. The marker is bare now and the provider is recorded separately.
  • Pricing-table entitlement keys were tied to a tier's position, so reordering or deleting a tier moved the key onto a different product and collided on the unique constraint.
  • A Polar order without data.product invented a product id from the line-item id, which is new on every purchase. The event was recorded as processed and nobody was ever granted access. It fails loudly now.
  • Events stuck at "processed, nothing granted" could not be replayed, which is exactly the case where you fix a mapping and want the purchase to run again.
  • Switching publication did not reload the dashboard.
  • The webhook URL was shown without its key on first-time setup.
  • Renaming a tenant slug left slug-linked rows behind.

Security

Six advisories closed: undici (one high, four moderate), cryptography (one high), and postcss. Dependencies updated across frontend and backend. No open alerts.

Housekeeping

  • SupabaseBearerAuthentication replaces the misleading FirebaseBearerAuthentication name. The auth stack has been Supabase for a long time.
  • The support-status sync command is gone. It pulled from an issue tracker a self-hosted install has no access to.
  • The frontend env example lists every variable the code actually reads, checked against the source.
  • An internal notice that was written in German is now English like the rest.

Full changelog: v0.2.0...v0.3.0