Skip to content

Commit 82c14dd

Browse files
committed
fix(7b-3): the changeset names the retired export in ADR-0087's runtime-export-retired category and grades driver-turso minor; the refusal pin unsets the legacy secret spellings
Claude-Session: https://claude.ai/code/session_01H7Dv7HSDeT8d2fbDq3myx5 Co-authored-by: Claude <noreply@anthropic.com>
1 parent ef624ed commit 82c14dd

2 files changed

Lines changed: 11 additions & 4 deletions

File tree

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
'@objectstack/cli': minor
33
'@objectstack/driver-sql': minor
4-
'@objectstack/driver-turso': patch
4+
'@objectstack/driver-turso': minor
55
'@objectstack/objectql': minor
66
'@objectstack/lint': patch
77
'@objectstack/spec': patch
@@ -11,16 +11,19 @@ feat!: an organization-less row on a tenant table no longer draws its autonumber
1111

1212
Clause-②: yes (narrowing)
1313

14-
<!-- adr-0087: not-required (no-migration-prescription) A run-time narrowing at two doors — the SQL driver's autonumber counter and `os serve`'s boot composition. No key of any metadata schema is removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite and no tombstone. The one export removed, `GLOBAL_TENANT` from `@objectstack/objectql`, was a string constant the engine's refusal message quoted; the remedy for it is stated below. -->
14+
<!-- adr-0087: not-required (runtime-export-retired @objectstack/objectql#GLOBAL_TENANT) The one author-reachable removal is a runtime export: the string constant `GLOBAL_TENANT` leaves `@objectstack/objectql`'s published entry. No key of any metadata schema is removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite and no tombstone; the two run-time narrowings (the driver's counter, `os serve`'s boot) carry their own remedy below. -->
1515

1616
**BREAKING** accept-set narrowing, shipped as `minor` under the repo's launch-window convention for breaking changes (ADR-0131 D9, stage 8d).
1717

18-
- **`os serve` / `os start`: a production server boot whose auth did not compose for a missing secret is refused.** With the `auth` tier on and no `OS_AUTH_SECRET` (outside `--dev`), `serve` used to skip AuthPlugin with a warning and serve anyway. That boot had no security model: anonymous object data is always denied, so its data API was unusable. It also had no organization object, so its inline seed wrote rows with `organization_id` NULL and drew their autonumbers from the `__global__` counter. Measured on `examples/app-showcase` before this change: health 200, two NULL-organization rows and a `__global__` counter row on a fresh install. The boot now stops before the kernel starts, so nothing is written, and the message names both fixes. Unchanged: `--no-server` (kernel only) still boots with the warning, a development boot still uses its fallback secret, and the cloud host kernel (`ObjectOSEnvironmentPlugin`, where auth is each project's) still only warns.
18+
- **`os serve`: a production server boot whose auth did not compose for a missing secret is refused.** With the `auth` tier on and no `OS_AUTH_SECRET` (outside `--dev`), `serve` used to skip AuthPlugin with a warning and serve anyway. That boot had no security model: anonymous object data is always denied, so its data API was unusable. It also had no organization object, so its inline seed wrote rows with `organization_id` NULL and drew their autonumbers from the `__global__` counter. Measured on `examples/app-showcase` before this change: health 200, two NULL-organization rows and a `__global__` counter row on a fresh install. The boot now stops before the kernel starts, so nothing is written, and the message names both fixes. Unchanged: `--no-server` (kernel only) still boots with the warning, a development boot still uses its fallback secret, and the cloud host kernel (`ObjectOSEnvironmentPlugin`, where auth is each project's) still only warns. `os start` never reaches the refusal: it always supplies a secret to the `serve` child it spawns.
1919
- **`@objectstack/driver-sql` (and `@objectstack/driver-turso`'s remote face, which calls the same rule): the counter refuses an organization-less row on a tenant table.** Wherever the driver registered the organization object (`sys_organization`), a `create` / `bulkCreate` / `upsert` on an object with a tenant column, whose row carries no organization and whose options carry no `tenantId`, is refused with `ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED` (status 500), the code the engine already answers for the same write. It is refused before any counter row is read or written, and nothing is written. New exports: `OrganizationlessCounterRefusal`, `ORGANIZATIONLESS_COUNTER_REFUSAL_CODE`, `ORGANIZATIONLESS_COUNTER_REFUSAL_STATUS`, `renderOrganizationlessCounterRefusal`. Unchanged: `__global__` stays the counter key of an object with no tenant column, and of a composition that registers no organization object on the driver (a lean `@objectstack/objectql/core` embedding), where no organization can exist. Counter rows a 17.x install filed under `__global__` for a tenant table stay in `_objectstack_sequences`; the counter no longer reads them, and `os migrate duplicates` still reports them.
2020
- **`@objectstack/objectql`: `GLOBAL_TENANT` is no longer exported**, and `SystemWriteOrganizationRequiredError`'s message no longer describes the `__global__` counter as where the write would land. The code, status and fields are unchanged.
2121
- **Texts.** The lint explanation of `unique/legacy-organization-composite`, the `UniqueScopeSchema` TSDoc and the error-code ledger now say that a NULL-organization row is residue an earlier release wrote, not something a new write produces.
2222

2323
**The remedy.**
2424
- A production boot refused for the missing secret: set `OS_AUTH_SECRET` (a long random value, identical across restarts and nodes), or run kernel-only with `--no-server`.
2525
- A direct driver write refused by the counter: carry the organization — `driver.create(object, { ...row, organization_id: orgId })` or `driver.create(object, row, { tenantId: orgId })` — or write through the engine, which derives it under `single`.
26-
- An import of `GLOBAL_TENANT` from `@objectstack/objectql`: import it from `@objectstack/driver-sql`, where the counter and the index emitters keep it, or spell `'__global__'`.
26+
27+
The retired export:
28+
29+
- FROM `GLOBAL_TENANT` TO `GLOBAL_TENANT` imported from `@objectstack/driver-sql`, where the counter and the index emitters keep it (or the literal `'__global__'`).

‎packages/cli/test/serve-no-auth-secret-refusal.integration.test.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ async function boot(databaseFile: string, env: Record<string, string | undefined
6565
OS_DISABLE_CONSOLE: '1',
6666
OS_SECRET_KEY: E2E_SECRET_KEY,
6767
// Truly unset in both legs unless a leg sets it: the refusal's subject.
68+
// The two legacy spellings `resolveAuthSecret` still reads are unset too,
69+
// so an ambient one cannot compose auth behind the leg's back.
6870
OS_AUTH_SECRET: undefined,
71+
AUTH_SECRET: undefined,
72+
BETTER_AUTH_SECRET: undefined,
6973
// Production posture — see the header.
7074
NODE_ENV: undefined,
7175
...env,

0 commit comments

Comments
 (0)