PayGlue-OS v0.3.0 Nothing phones home any more
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.pyread a setting that was never defined in this repository, so the path raisedAttributeError. - The invite gate shipped with a built-in bypass address.
- Paywall access ignored genuine Stripe subscribers. Access is granted for
paid,comped, or apayglue-activelabel. 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.productinvented 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
SupabaseBearerAuthenticationreplaces the misleadingFirebaseBearerAuthenticationname. 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