diff --git a/content/docs/releases/v18/index.mdx b/content/docs/releases/v18/index.mdx index 8ebd03e386b..d5979172723 100644 --- a/content/docs/releases/v18/index.mdx +++ b/content/docs/releases/v18/index.mdx @@ -21,6 +21,47 @@ in one major (its D14) instead of spreading them across 17.x minors. While the line was open, every other breaking change that landed on `main` shipped on it too, under the same rules. +## Upgrading a database to 18.0.0 + +An existing database moves to 18.0.0 through the **organization-ownership +ceremony**, `os migrate organization-ownership` (ADR-0131 D10). + +- **A fresh install writes a birth marker.** The first boot that creates a + datastore from empty writes a `sys_migration` row + (`adr-0131-organization-ownership`) naming the ownership shape generation it + was born in. A fresh 18.0.0 install never needs the ceremony, under any + tenancy posture. +- **A database without one is refused at boot.** A database 17.x or an 18.0 + pre-release (`18.0.0-next.*`) created carries no birth marker, so an 18.0.0 + server refuses it and names the ceremony command. Start 18.0.0 against it + once (the refused boot still creates the tables 18.0.0 adds), take a backup, + run `os migrate organization-ownership --out plan.json`, review the plan, run + `os migrate organization-ownership --apply --plan-file plan.json --backup-taken`, + and start the server again. The ceremony writes the same marker, verified, + when its post-check passes. +- **A development boot passes `--dev`.** On a deployment served with `--dev` + (`os dev`, `os serve --dev`), for example a local one with no + `OS_AUTH_SECRET`, both ceremony commands take `--dev`, as the refusal + `os serve --dev` prints names them. `os dev` runs the ceremony itself for a + local SQLite development database. +- **Walled postures boot.** `OS_TENANCY_POSTURE=group` and `isolated` (and + `OS_MULTI_ORG_ENABLED=true`) boot on a fresh install and on a database the + ceremony upgraded, under the same prerequisites as 17.x: the enterprise + `@objectstack/organizations` runtime and `OS_PLATFORM_OWNER_EMAIL`. The + 18.0 pre-releases refused them; 18.0.0 does not. +- **A production `os serve` needs an auth secret.** A production `os serve` + whose stack enables auth and which resolves no `OS_AUTH_SECRET` is refused + before any plugin starts: with no AuthPlugin, no caller can + authenticate and every row the boot wrote would have no owner. Set the + secret, or serve without the data API (`--no-server`). `os dev` and + `os serve --dev` use a development fallback and need none, and `os start` + generates an auth secret on its first run and persists it to + `/auth-secret`. + +The step-by-step is [Upgrading to 18.0](/docs/upgrading#upgrading-to-180), and +every flag is in +[`os migrate organization-ownership`](/docs/deployment/cli#os-migrate-organization-ownership). + ## How a breaking stage lands on this line - **Through `next` before `latest`.** Every major is published to the `next` diff --git a/content/docs/upgrading.mdx b/content/docs/upgrading.mdx index 3957837e2f4..b6dd9319706 100644 --- a/content/docs/upgrading.mdx +++ b/content/docs/upgrading.mdx @@ -333,6 +333,61 @@ writes no artifact, which makes it the fast inner loop while you work through the to-dos. Full command reference in the [CLI documentation](/docs/deployment/cli). +## Upgrading to 18.0 + +18.0 refuses to serve an existing database until an operator step has run on +it. [ADR-0131](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0131-total-organization-ownership-no-null-organization-id.md) +makes organization ownership total, and the rows a 17.x database holds have to +be given their owners before an 18.0 server serves them. That is the +**organization-ownership ceremony**, `os migrate organization-ownership`. + +**Which databases need it.** A database 17.x created, or one an 18.0 +pre-release (`18.0.0-next.*`) created. The first boot that creates a datastore +from empty writes a **birth marker**, so a fresh 18.0 install never needs the +ceremony, under any tenancy posture. A v18 server serves a database it created +itself or one the ceremony certified, and refuses any other at boot, naming the +command. + +**The sequence:** + +1. Start 18.0 against the database once (`os serve` / `os start`). The boot is + refused, but its schema sync has already created the tables 18.0 adds, which + the ceremony moves rows into. +2. Take a backup. The platform takes none. +3. Write the plan: `os migrate organization-ownership --out plan.json`. It reads + only. +4. Review the plan. +5. Apply exactly that plan: + `os migrate organization-ownership --apply --plan-file plan.json --backup-taken`. +6. Start the server again. + +```bash +os migrate organization-ownership --out plan.json +os migrate organization-ownership --apply --plan-file plan.json --backup-taken +``` + +**A development boot passes `--dev` to both commands.** A deployment served +with `--dev` (`os dev`, `os serve --dev`), for example a local one with no +`OS_AUTH_SECRET`, composes as a development boot, and the ceremony has to boot +the same way to see the same database. The refusal `os serve --dev` prints names +the command with `--dev`. `os dev` runs the whole sequence itself for a local +SQLite development database. + +**Walled postures upgrade too.** `OS_TENANCY_POSTURE=group` and `isolated` boot +in 18.0 on a fresh install and on a database the ceremony upgraded. On a walled +database, a customized copy of a declared webhook or sharing rule blocks its +step and names the row. + +**A production `os serve` deployment needs an auth secret.** A production +`os serve` whose stack enables auth and which resolves no `OS_AUTH_SECRET` is +refused before any plugin starts, so set the secret before you start such a +deployment on 18.0. `os start` is not affected: it generates an auth secret on +its first run and persists it to `/auth-secret`. + +What the plan lists, how a blocked row is resolved, and every flag are in the +command's reference: +[`os migrate organization-ownership`](/docs/deployment/cli#os-migrate-organization-ownership). + ## Per-release specifics Everything above is the mechanism. The *contents* of a given release — which keys @@ -349,6 +404,7 @@ have no other channel to reach you than the page below. | Release | Where its upgrade notes live | | :--- | :--- | +| v18.0.0 | [Upgrading to 18.0](#upgrading-to-180) (the ownership ceremony) · [v18](/docs/releases/v18) | | v17.4.0 | [17.4.0](/docs/releases/v17/17-4) — ⛔ checklist not written; machine-draft notes only | | v17.3.0 | [Upgrade checklist — 17.3.0](/docs/releases/v17/17-3#upgrade-checklist) | | v17.2.0 | [Upgrade checklist — 17.2.0](/docs/releases/v17/17-2#upgrade-checklist) | diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 19d0a279492..8462f4d4b43 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -4,7 +4,7 @@ ### Minor Changes -- fc34d00: feat(cli): `os serve` (and so `os dev` and `os start`) boots a walled tenancy posture again; `group` and `isolated` are supported on fresh installs +- fc34d00: feat(cli): `os serve` (and so `os dev` and `os start`) boots a walled tenancy posture again; `group` and `isolated` are supported on fresh installs and on databases the ownership ceremony upgraded Clause-②: yes (widening) @@ -159,18 +159,18 @@ os migrate organization-ownership --out plan.json # plan, then review plan.json os migrate organization-ownership --apply --plan-file plan.json --backup-taken # perform exactly that plan ``` -- ae6bd1a: feat(cli)!: 18.0 supports fresh `single` installs only — `os migrate organization-ownership` refuses, and `os serve` refuses a walled posture at boot +- ae6bd1a: feat(cli)!: the 18.0 pre-release refusals of `os migrate organization-ownership` and of a walled posture at boot, both lifted in 18.0.0 Clause-②: yes (narrowing) - + - **BREAKING** (a command and a boot posture that used to run now refuse), shipped as `minor` under the launch-window convention for breaking changes. 18.0 supports fresh installs under the `single` tenancy posture only; upgrading a v17 database and the walled postures arrive in an 18.x release. + **This entry described `18.0.0-next.5`, not 18.0.0.** That pre-release refused the ownership ceremony and the walled postures. 18.0.0 ships neither refusal: - - **FROM** `os migrate organization-ownership` (plan) and `os migrate organization-ownership --apply …` running the v18 ownership ceremony **TO** both refusing with exit `1` and the standard code `NOT_IMPLEMENTED` (under `--json`: `{ error: 'plan_refused' | 'apply_refused', reason: 'upgrade-unavailable', code: 'NOT_IMPLEMENTED', detail }`), reading and writing nothing. The 18.0 ceremony is knowingly incomplete, and an incomplete ceremony must never certify an old database. **Fix:** keep a v17 database on a 17.x release until the 18.x release that carries the upgrade path. `os dev`'s in-place upgrade of a v17 development database runs this command, so it now stops with the same refusal and changes nothing. - - **FROM** `os serve` (and so `os dev` and `os start`) booting with `OS_TENANCY_POSTURE=group` or `isolated`, or with `OS_MULTI_ORG_ENABLED=true`, **TO** a FATAL refusal at the tenancy-posture gate, exit `1`, naming `NOT_IMPLEMENTED` and the 18.x release, before a config is read. `OS_ALLOW_DEGRADED_TENANCY` does not lift it, and no flag or variable does. **Fix:** set `OS_TENANCY_POSTURE=single` (or unset it and `OS_MULTI_ORG_ENABLED`), or keep a walled deployment on a 17.x release. + - **`os migrate organization-ownership` runs** and upgrades a database 17.x or an 18.0 pre-release (`18.0.0-next.*`) created (12d1476 above). A v18 server refuses such a database at boot and names the command. Start 18.0.0 against it once (the boot is refused), take a backup, run `os migrate organization-ownership --out plan.json`, review the plan, run `os migrate organization-ownership --apply --plan-file plan.json --backup-taken`, then start the server again. A deployment served as a development boot (`os dev`, `os serve --dev`, for example a local one with no `OS_AUTH_SECRET`) passes `--dev` to both commands, as the refusal `os serve --dev` prints names it. `os dev` runs the ceremony itself for a local SQLite development database. + - **`os serve` (and so `os dev` and `os start`) boots `OS_TENANCY_POSTURE=group` or `isolated`, and `OS_MULTI_ORG_ENABLED=true`** (fc34d00 above), on a fresh install and on a database the ceremony upgraded. - **What stays as it was.** A fresh install under `single` boots exactly as before. The boot guard that refuses a v17-shaped database is unchanged. Kernel-level boots (`@objectstack/verify`'s `bootStack`, embedded hosts) still accept every posture. + **What else holds.** A fresh install boots under every tenancy posture and writes its birth marker, so it never needs the ceremony. Kernel-level boots (`@objectstack/verify`'s `bootStack`, embedded hosts) still accept every posture. - 78652c0: feat(cli,plugin-sharing)!: in the 17 → 18 ownership ceremony, a row whose owner cannot be derived blocks certification, and a declared-rule mirror's grants are renamed only onto a rule plugin-sharing serves Clause-②: yes (narrowing)