Skip to content

Commit 2e6a2ea

Browse files
hotlongclaude
andauthored
fix(plugin-auth): pin the better-auth family to an exact 1.7.2, and gate the declared range against our own import surface (#16634)
* fix(plugin-auth): pin the better-auth family to an exact 1.7.2 (#16186) `@better-auth/core@1.7.3` deleted `createLocalAccountIssuer` and `createOAuthAccountIssuer` from `@better-auth/core/db` — and the whole `account.issuer` column with them — in a PATCH release. `plugin-auth` imports both names statically, so every consumer of published 17.1.0 / 17.2.0 / 17.3.0 resolved 1.7.3 through the declared `^1.7.2` range and could not load the plugin at all. This repo's lockfile held 1.7.2, so every CI job here was green while the artifact it produced was broken on install. The declared range and the workspace override target both become exact 1.7.2, so the version this repo tests is the version a consumer resolves. Resolutions are unchanged: the lockfile already held 1.7.2 for all eleven family members, and `pnpm install --lockfile-only` moves only the echoed specifier strings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(scripts): add the consumer-resolution regression control (#16186) Pinning the version fixes today's break and leaves the repository exactly as blind as it was: `pnpm-lock.yaml` held `@better-auth/core@1.7.2`, so every job here imported the version that still had `createLocalAccountIssuer` while every consumer resolved `^1.7.2` to 1.7.3 and could not load the plugin at all. `scripts/check-vendor-export-contract.mjs` closes that class. For every governed vendor a publishable package statically imports runtime values from, it asserts the declared range is an EXACT version, that the installed copy IS that version, and that importing each specifier really yields every symbol our shipped source takes from it — so "what CI imported" and "what a consumer resolves" are the same string. `--resolve` measures the same property instead of proving it: it enumerates every registry version the DECLARED range admits, installs each outside this workspace, and fails if any is missing a symbol we import. Wired into validate-deps.yml, which already has the network and runs daily; the offline leg runs in the lint farm on every PR. `check-override-consistency.mjs` structurally cannot cover this — it asks whether the override target is reachable from the declared range, and `^1.7.2` agreed with `^1.7.2` all the way onto the broken version. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: wire the vendor export contract into CI and add the changeset (#16186) The offline leg joins the lint farm (no `paths:` filter — a manifest edit, a lockfile refresh and a new vendor import can each move it). The network leg joins validate-deps.yml, which already installs from the registry and runs daily, so "a vendor published something today" becomes a red here instead of in a customer's install. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(scripts): route the new gate's entry guard through isEntrypoint (#16186) `check:entry-guard` caught a hand-typed `process.argv[1]` comparison: it goes inert through a symlinked checkout, silently, at exit 0 — which for a gate means a green that never ran. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 61b4eb3 commit 2e6a2ea

8 files changed

Lines changed: 736 additions & 67 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@objectstack/plugin-auth": patch
3+
---
4+
5+
`@objectstack/plugin-auth` pins the `better-auth` family to an exact `1.7.2`, so a fresh install of a published `@objectstack/*` release loads the auth plugin again — and with it creates the system tables and seeds the admin.
6+
7+
Published 17.1.0, 17.2.0 and 17.3.0 declared `"@better-auth/core": "^1.7.2"` and imported `createLocalAccountIssuer` / `createOAuthAccountIssuer` from `@better-auth/core/db`. `@better-auth/core@1.7.3` — a **patch** — deleted both names, and the `account.issuer` column behind them, because upstream rolled the issuer-scoped account identity back to opt-in (better-auth/better-auth#10909). A static ESM named import of a missing export is a link-time `SyntaxError`, so the plugin could not load at all. Every symptom followed from that one failure and every one of them was quiet: the scaffolded project's CLI printed the `SyntaxError` as a scrollable oclif warning and carried on, the server printed `✓ Server is ready` on the broken boot, `sys_user` / `sys_organization` / `sys_permission_set` / `sys_position` were never created, the seeded admin sign-in never answered, and the Console's sign-in form answered `Auth request failed with status 404`.
8+
9+
**This is a stopgap, deliberately, and it is labelled as one.** Upstream removed the export on purpose; adopting 1.7.3 means dropping `sys_account.issuer` — a required column with a unique `(issuer, accountId)` index — from the platform object, retiring the boot-time backfill that stamps it, and migrating every existing deployment. That is its own change with its own decision to make; this one restores a working install today.
10+
11+
All five members `plugin-auth` declares move together (`better-auth`, `@better-auth/core`, `@better-auth/oauth-provider`, `@better-auth/scim`, `@better-auth/sso`), because they are only correct as one line: `@better-auth/core@1.7.2` and `@better-auth/kysely-adapter@1.7.3` are mutually incompatible in both directions. `better-auth@1.7.2` declares its own siblings exactly, so pinning those five resolves all twelve family members to 1.7.2 — measured on a fresh `npm install` with no lockfile.
12+
13+
The workspace `overrides` move to the same exact target in step, so the version this repository tests is the version a consumer resolves. In-repo resolutions are unchanged: the lockfile already held 1.7.2 for all eleven overridden members.

.github/workflows/lint.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2647,6 +2647,24 @@ jobs:
26472647
- name: Auth mount-vs-ledger guard
26482648
run: pnpm check:auth-mount-ledger
26492649

2650+
# Vendor export contract (#16186). `@better-auth/core@1.7.3` deleted
2651+
# `createLocalAccountIssuer` in a PATCH; plugin-auth imports it statically
2652+
# through a caret range, so published 17.1.0-17.3.0 could not load at all
2653+
# on a fresh install -- while every job here stayed green, because
2654+
# pnpm-lock.yaml held 1.7.2. The lockfile protected the producer from the
2655+
# defect it was shipping. This gate asserts that a governed vendor's
2656+
# DECLARED range is exact, that the installed version IS that version, and
2657+
# that importing it really yields every symbol our shipped source takes
2658+
# from it -- so "what CI imported" and "what a consumer resolves" are the
2659+
# same string. Cheap and offline: it imports what `pnpm install` already
2660+
# put on disk. The network half (`--resolve`, which enumerates every
2661+
# registry version the range admits) runs in validate-deps.yml, which
2662+
# already installs from the registry. No `paths:` filter: a manifest edit,
2663+
# a lockfile refresh and a new vendor import can each move it. Runs its
2664+
# own --self-test first.
2665+
- name: Vendor export contract
2666+
run: pnpm check:vendor-export-contract
2667+
26502668
# Optional-`error` sink contract (#9754, wired by #10608). A sink TYPE that
26512669
# declares `error` as OPTIONAL must also declare `warn` as NON-optional, so
26522670
# every value of that type has somewhere to put a durability report —

.github/workflows/validate-deps.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ on:
99
- 'pnpm-workspace.yaml'
1010
- 'scripts/check-changeset-fixed.mjs'
1111
- 'scripts/check-override-consistency.mjs'
12+
# The consumer-resolution half of the same question (#16186) — an edit to
13+
# the gate must be exercised on the PR that makes it.
14+
- 'scripts/check-vendor-export-contract.mjs'
1215
# The OSV exemption ledger and its check: a PR that touches either must
1316
# run this workflow, or an exemption could be added without the gate
1417
# that governs it ever running on the PR that adds it.
@@ -83,6 +86,29 @@ jobs:
8386
- name: Verify overrides are reflected in published manifests
8487
run: pnpm check:override-consistency
8588

89+
# The half check-override-consistency structurally cannot cover (#16186).
90+
# It asks whether the override TARGET is reachable from the declared
91+
# range; `^1.7.2` and `^1.7.2` agreed perfectly while both floated onto
92+
# `@better-auth/core@1.7.3`, a PATCH that deleted the export plugin-auth
93+
# imports statically. Three published releases could not load on a fresh
94+
# install and nothing here went red, because pnpm-lock.yaml held 1.7.2.
95+
#
96+
# `--resolve` deliberately ignores the lockfile: for every version the
97+
# DECLARED range admits on the registry -- the way a downstream project
98+
# resolves -- it installs that version outside this workspace and checks
99+
# the export surface. It fails if ANY admitted version is missing a symbol
100+
# we import. With the ranges pinned exact that is one small install per
101+
# vendor; under a caret it is one per published version, which is the cost
102+
# of the risk being taken.
103+
#
104+
# It lives HERE rather than in the lint farm because it needs the network,
105+
# which this job already has, and because the daily schedule is what turns
106+
# "a vendor published something today" into a red in this repo instead of
107+
# in a customer's install. An unreachable registry exits 3
108+
# (PREREQUISITE NOT MET), never a green.
109+
- name: Verify the declared ranges cannot resolve past our import surface
110+
run: pnpm check:vendor-export-contract-resolve
111+
86112
# Fail the workflow if known vulnerabilities are found — enforces
87113
# security compliance before merging.
88114
#

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@
128128
"check:adr-0087-registration": "node scripts/check-adr-0087-registration.mjs --self-test && node scripts/check-adr-0087-registration.mjs",
129129
"check:changeset-gate-self-tests": "node scripts/check-empty-changeset.mjs --self-test && node scripts/check-adr-0087-registration.mjs --self-test && node scripts/check-changeset-no-major.mjs --self-test",
130130
"check:override-consistency": "node scripts/check-override-consistency.mjs --self-test && node scripts/check-override-consistency.mjs",
131+
"check:vendor-export-contract": "node scripts/check-vendor-export-contract.mjs --self-test && node scripts/check-vendor-export-contract.mjs",
132+
"check:vendor-export-contract-resolve": "node scripts/check-vendor-export-contract.mjs --self-test && node scripts/check-vendor-export-contract.mjs --resolve",
131133
"check:release-notes": "node scripts/check-release-notes.mjs",
132134
"check:release-page-status": "node scripts/check-release-page-status.mjs --self-test && node scripts/check-release-page-status.mjs",
133135
"check:release-body": "node scripts/release-github-releases.mjs --self-test",

packages/plugins/plugin-auth/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,18 @@
2626
"gen:test-typecheck-debt": "tsx ../../../scripts/check-test-typecheck.mts --update --package packages/plugins/plugin-auth --project tsconfig.test.json"
2727
},
2828
"dependencies": {
29-
"@better-auth/core": "^1.7.2",
30-
"@better-auth/oauth-provider": "^1.7.2",
31-
"@better-auth/scim": "^1.7.2",
32-
"@better-auth/sso": "^1.7.2",
29+
"@better-auth/core": "1.7.2",
30+
"@better-auth/oauth-provider": "1.7.2",
31+
"@better-auth/scim": "1.7.2",
32+
"@better-auth/sso": "1.7.2",
3333
"@noble/hashes": "^2.3.0",
3434
"@objectstack/core": "workspace:*",
3535
"@objectstack/platform-objects": "workspace:*",
3636
"@objectstack/rest": "workspace:*",
3737
"@objectstack/service-messaging": "workspace:*",
3838
"@objectstack/spec": "workspace:*",
3939
"@objectstack/types": "workspace:*",
40-
"better-auth": "^1.7.2",
40+
"better-auth": "1.7.2",
4141
"jose": "^6.2.8"
4242
},
4343
"devDependencies": {

pnpm-lock.yaml

Lines changed: 28 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)