Skip to content

Releases: PayGlue/PayGlue-OS

PayGlue-OS v0.4.0 — Run it yourself, without signing up for anything

Choose a tag to compare

@Nuenni Nuenni released this 08 Aug 19:56
bf9004a

You can finally just run it

Until this release, starting your own copy of PayGlue meant creating a Supabase project first. Not as a nice-to-have. Without one the application threw while loading and never reached a screen at all.

That is a strange first requirement for software you were about to run on your own machine. It is gone.

git clone https://github.com/PayGlue/PayGlue-OS.git
cd PayGlue-OS
cp .env.example .env      # fill in one key, the file tells you the command
docker compose up -d postgres redis
docker compose run --rm web python manage.py migrate
docker compose up -d

Open the dashboard and it walks you through creating your account and your first publication. No external service, no second sign-up, no account anywhere but yours.

Accounts that live in your installation

LOCAL_AUTH_ENABLED=1, which the compose file now sets by default, and the accounts are in your own database. Django hashes the passwords, Django's token generator carries the reset links.

This is a branch, not a fork. get_auth_token_verifier() already chose between four implementations behind one protocol; local accounts are the fifth. A local token arrives in the same header and resolves to the same profile through the same invite gate, so nothing downstream can tell the two apart. Whatever is true of the hosted path stays true here, including the parts nobody remembers to test.

Prefer a hosted identity provider? Unchanged, and it still brings what only such a provider can: authenticator apps, magic links, sign-in with Google or GitHub. Where those do not exist, the screens for them are hidden rather than shown and broken.

Setup on first run

Two steps: pick how sign-in works, create the first account. Then straight into the publication and Ghost screens that were always there.

The wizard closes for good once an account exists, and that is decided by counting rows in your database, never by anything the browser sends. Everyone after the first person arrives by invitation. An installation reachable from the internet with an open registration endpoint is the easiest way there is to lose it, so that gate is not a UI decision.

We installed it before we shipped it

This is the first release that was set up twice from a clean clone before it went out, on a laptop, with Docker Desktop, following our own guide word for word. That found things no test could:

  • The quickstart broke the install. .env.example shipped a placeholder encryption key that is not a valid key, and it overrode the working default in the compose file. Copying the example file, step one of our own guide, left you worse off than skipping it. Fixed, with the command to generate a real one right beside the empty value.
  • Host ports could not be changed without editing a file under version control. Compose appends port lists rather than replacing them, so the obvious workaround does not work. They come from the environment now.

Neither of these would ever have shown up in CI. Both stood between a stranger and their first working install.

Also in this release

  • X-Frame-Options was missing from every response, including the HTML the embed endpoints serve. The middleware that sets it was simply not in the list. Found by a test we had written but never published.
  • The full test suite ships now: 176 tests become 579. It used to be a separate hand-maintained copy here, which is how main once went red for two commits without anyone noticing.
  • Billing, plans and affiliate screens are gone. They were the storefront of the hosted service. You have no subscription with us, and being shown one is worse than being shown nothing.
  • Creating a publication no longer needs PostgREST.
  • A race in the setup gate is closed. Two requests arriving together could both create a first account, leaving an installation with two administrators. A unique index settles it in the database now, however the two are interleaved.
  • The setup guide gained a chapter on signing in, and the wizard carries a short letter about why this project exists.

Upgrade notes

Run migrations. 0044 and 0045 add local credentials and the constraint that keeps the first account unique.

Using a hosted identity provider? Nothing changes. Leave LOCAL_AUTH_ENABLED unset or 0 and everything works exactly as before.


Full changelog: CHANGELOG.md · Setup guide: SETUP.md · Docs: docs.payglue.io

PayGlue-OS v0.3.0 Nothing phones home any more

Choose a tag to compare

@Nuenni Nuenni released this 06 Aug 02:26
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

PayGlue-OS v0.2.0 — Live, open beta, and eight payment providers

Choose a tag to compare

@Nuenni Nuenni released this 20 Jul 21:50
6d07887

PayGlue is live. The hosted product at payglue.io is in open beta with its first paying customers, and this release brings the open-source repo up to the exact code that runs it.

Payment providers: 3 → 8. Gumroad, Paddle, Ko-fi, Creem, and Patreon join Polar, Lemon Squeezy, and PayPal — each with encrypted credential storage, health checks, webhook verification, and product autofetch.

Dashboard 2.0. A full visual rebuild: dark mode everywhere, a connections overview with real provider logos, and one config-driven detail view instead of eight hand-copied pages.

Accounts that behave like they should. Step-up confirmation for destructive actions, TOTP login with backup codes, account deletion that actually deletes, and nobody gets removed from a team quietly.

Support with reference numbers. Requests are stored first, confirmed by email, and trackable by status.

And one more thing: somewhere in this source code hides a small thank-you for people who actually read it. First ten finders win. Happy hunting.

Full details in CHANGELOG.md. Don't want to run it yourself? payglue.io is the hosted version — same code, minus the ops.

PayGlue-OS v0.1.0 - Initial Open Source Release

Choose a tag to compare

@Nuenni Nuenni released this 30 Jun 18:37

This is the first public release of PayGlue-OS, made possible by everyone who joined the closed beta, tested edge cases, and gave feedback along the way. Thank you to all testers and everyone on the waitlist — you shaped what this is.

What's included

  • Ghost CMS integration via Admin API (comped member management)
  • Polar payment provider (webhooks + checkout links)
  • Lemon Squeezy payment provider (webhooks)
  • PayPal payment provider (webhook verification via RSA-SHA256)
  • Cloudflare Worker proxy for clean webhook routing
  • Django backend with encrypted credential storage
  • Vue 3 dashboard for tenant management and provider setup
  • Two-layer webhook security (URL token + provider HMAC/RSA)

Status

Hosted version is in closed beta — join the waitlist at payglue.io.

PayGlue sits between Ghost and your payment provider of choice. It works with any processor your provider supports — no lock-in, no forced stack.

Self-hosting: see SETUP.md for the full setup guide.

Providers in development

  • Paddle
  • Gumroad

License

BUSL 1.1 — converts to Apache 2.0 in 2030. See LICENSE.md.