Skip to content

Retire the legacy auth; add the pgstencil CLI, billing security rules and email options - #100

Merged
nedtwigg merged 7 commits into
mainfrom
retire-legacy-auth
Nov 10, 2026
Merged

nedtwigg merged 7 commits into
mainfrom
retire-legacy-auth

Conversation

@nedtwigg

Copy link
Copy Markdown
Member

Removes the original code/link authentication, leaving @pgstencil/auth as the Better Auth integration only, and makes the changes that depended on that. These are breaking, so they should release as 0.5.0. This PR does not raise the version, so merging it does not stage a release.

CHANGELOG.md lists every breaking change with how an application adapts.

What changes

  • Legacy auth removed. The original source modules, their subpath exports, the package's migrations/ directory, openid-client, and the examples/login and examples/workers applications are gone, with their tests and snapshots. The package root now re-exports the Better Auth module.
  • One example. examples/better-auth gains billing routes behind its sign-in, the pnpm dev entry with the local inbox and StripeDev, and the deployable Workers entry.
  • Infrastructure tests stand alone. The database, parallel and query-type tests run on a small fixture schema and fixture app instead of auth tables.
  • pgstencil command. The core package ships the database commands that scripts/db.ts ran privately, configured by pgstencil.json. kysely-codegen is a new optional tooling peer. defaultMigrations is now always a list.
  • Sign-in email. emailFrom and renderEmail on createAuthApp and createBetterAuthWorker; the default sender is signin@<origin hostname> instead of [email protected]. Core exports escapeHtml.
  • Billing. Reconciliation reads every page of a customer's subscriptions instead of throwing past 100. Billing.pruneEvents({ olderThanDays }) trims the webhook ledger. BillingDB is compared with generated declarations at typecheck.
  • Security. SECURITY.md gains FAIL IF rules for Stripe webhooks, checkout and entitlement, each pinned by a test, and the release-gating audit's scope now includes the stripe package. Its timeout rises from 40 to 50 minutes.
  • Node. Pinned once, at 24.22.2, in devEngines.runtime; pnpm and all three workflows read it. CI ran only Node 26 before, so the declared minimum of 24 was untested. .nvmrc is removed.
  • Docs. README, PACKAGES, BILLING and WORKERS are rewritten for the new layout. PLAN.md, DISTRIBUTION_AND_BILLING_PLAN.md, LOGIN_FLOW.md and OAUTH.md are deleted. CHANGELOG.md and AGENTS.md are new.

Verification

Run locally on the branch tip, under Node 24.22.2:

  • pnpm check (formatting, typecheck, script tests, 178 Vitest tests): pass
  • pnpm db:verify: pass
  • pnpm --filter @pgstencil/example-better-auth build: pass
  • pnpm packages:verify, including the provenance check and the CLI run from the packed tarball: pass

Only the tip was run. The six commits are grouped by theme and were not each checked on their own.

For the reviewer

  • A database that applied the old auth migrations must keep 001_auth.sql, 002_oauth.sql and 004_oauth_workers.sql as a source of its own, byte for byte, because the package no longer ships them and applied SQL stays under checksum validation.
  • Audit duration. Past audit runs took 13 to 23 minutes. Billing adds roughly 60 clauses to check, so the first run on main is worth watching against the new 50-minute limit.
  • Secret scanning. gitleaks is not installed where this was prepared, so .gitleaksignore was not rechecked. The rewritten billing HTTP test still contains a local test secret and may need a new fingerprint.
  • Failed sign-in sends answer 200. Better Auth swallows a send failure, including a renderer that throws; nothing is delivered and an email.delivery.failed diagnostic is recorded. This predates the PR and is now documented.

🤖 Generated with Claude Code

nedtwigg and others added 7 commits November 10, 2026 13:56
…ledger

synchronize read one page of 100 subscriptions and threw when Stripe
reported more, so an owner past 100 subscriptions, ended ones included,
failed every reconciliation and webhook. It now reads every page under
the same owner lock.

pruneEvents({ olderThanDays }) deletes processed ledger events older than
30 to 3650 days for a scheduled job. Unprocessed and failed events are
always kept.

StripeDev's subscription list honours limit and starting_after and
reports has_more. billing.test.ts takes its schema from the billing
package's own migrations rather than the login example.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
The email-code message was composed inline with a hard-coded
[email protected] sender. createAuthApp and createBetterAuthWorker now
take emailFrom and renderEmail; the default sender is signin@ the
origin's hostname, and defaultSignInEmail is exported. A rendered message
must be addressed to the requested recipient alone, or nothing is sent.

Core pgstencil exports escapeHtml for values placed in an email's HTML.
postmarkEmail reports Postmark's numeric ErrorCode on HTTP errors too.

The Better Auth test takes its deterministic bundle from a shared
support module.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
@pgstencil/auth is now only the Better Auth integration. The original
implementation is removed: its source modules and subpath exports, the
package's migrations directory, the openid-client dependency, and the
examples/login and examples/workers applications with their tests and
snapshots. The package root re-exports the Better Auth module.

examples/better-auth is the one example. It gains billing routes behind
its sign-in, the persistent dev entry with the local inbox and StripeDev,
and the deployable Workers entry. The infrastructure tests (database,
parallel, query types) run on a small fixture schema and fixture app of
their own instead of auth tables.

The pgstencil command in the core package replaces the private
scripts/db.ts: status, validate, migrate, create, schema, types, verify,
reset, gc and stop, configured by pgstencil.json (migrations, schema,
types). kysely-codegen becomes an optional tooling peer.
defaultMigrations is now always a list, and pgstencil.json is validated
strictly.

The billing schema's Kysely declarations are generated to
packages/stripe/src/db.generated.ts, and the hand-written BillingDB is
compared with them column by column at typecheck.

CHANGELOG.md, added with the docs later in this branch, says how an
application adapts.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
SECURITY.md stated rules only for the Better Auth integration and core.
It now carries FAIL IF rules for Stripe webhooks, checkout and
entitlement, each pinned by a test, with what the application owns for
billing and what is not defended. billing-security.test.ts adds the
pinning tests that did not exist yet. The auditor's scope and commands
include the stripe package and the billing suites, and name the auth
suites directly now that the test:better-auth script is gone.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
Node was named in three places that disagreed: engines and .nvmrc said
24, while every workflow ran 26, so the declared minimum was never
tested. The root package.json now pins 24.22.2 in devEngines.runtime
with onFail: download: pnpm fetches that version and runs every script
with it, and check, release and security-audit read the same pin through
node-version-file. .nvmrc is removed.

check.yml pins its actions to commits like the other two workflows and
builds the Workers entry from the Better Auth example. The audit job's
timeout rises from 40 to 50 minutes now that billing is in its scope,
staying under the 55 minutes release.yml waits for its verdict.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
README, PACKAGES, BILLING and WORKERS are rewritten around the single
Better Auth example, the pgstencil command and the new options, with
hand-typed version numbers removed. CHANGELOG.md records the unreleased
breaking changes and how an application adapts to each. AGENTS.md gains
working notes for the repository.

PLAN.md and DISTRIBUTION_AND_BILLING_PLAN.md described finished work, and
LOGIN_FLOW.md and OAUTH.md the removed implementation; all four are
deleted. Provider registration notes that still apply moved to the
example's README.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
Keeps this branch's root manifest and takes main's Miniflare 5.20261104.0-beta
and Renovate grouping; the lockfile is regenerated from the merged manifests.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
@nedtwigg
nedtwigg merged commit 93645f1 into main Nov 10, 2026
1 check passed
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