diff --git a/docs/spec/change-feed.md b/docs/spec/change-feed.md index 6a7a0812..c032e277 100644 --- a/docs/spec/change-feed.md +++ b/docs/spec/change-feed.md @@ -120,7 +120,7 @@ As with [the auth checklist](./wire-format.md#server-implementation-checklist), - **Close on buffer overflow; never drop a frame silently.** - **Only the storage owner can emit.** [Exactly one process owns a stack's storage](./adapters.md#concurrency--storage-ownership), so events exist only in that process. A multi-process server needs its own fan-out from the owner; a second process subscribing to its own `Stack` sees nothing and looks fine in testing. - **Mint cursors in the base64url alphabet only** — a value containing a newline truncates the frame that carries it. -- **Emit for every mutating endpoint**, not the convenient ones. The list is the exhaustive one under [Versions](./wire-format.md#versions), plus create, purge, the [association](./wire-format.md#associations) and [permission](./wire-format.md#permissions) endpoints, and a `PATCH` naming only no-bump keys. None of that second group bumps `version`, so a server that emits off its snapshot path alone silently serves no association, permission, move or listing events at all. +- **Emit for every mutating endpoint**, not the convenient ones. The list is the exhaustive one under [Versions](./wire-format.md#versions), plus create, purge and every [no-bump write](./versioning.md#version-history). None of that second group bumps `version`, so a server that emits off its snapshot path alone silently serves no association, permission, move or listing events at all. ## Why SSE, and why not `EventSource` diff --git a/docs/spec/wire-format.md b/docs/spec/wire-format.md index 1a4b0164..1702d246 100644 --- a/docs/spec/wire-format.md +++ b/docs/spec/wire-format.md @@ -221,6 +221,7 @@ A server built on core reaches this through the wire parsers in `@haverstack/cor | `GET /changes` | `parseChangeParams()` | | `GET /attachments/:fileId` | `parseDownloadParams()` | | `POST /types` | `parseTypeBody()` | +| `POST /installs` | `parseInstallBody()` | | `POST /auth/challenge` | `parseAuthChallengeBody()` | | `POST /auth/token` | `parseAuthTokenBody()` | @@ -464,7 +465,7 @@ A move that would make the record its own ancestor answers **409** (code `confli ## Versions -**The server snapshots prior state automatically on every mutating endpoint that bumps `version`** — there is no client-initiated endpoint to write a version directly. The list is exhaustive on purpose: `PATCH /records/:id/content`, `DELETE` (soft), `POST .../undelete`, `POST .../migrate`, and `POST .../restore/:version` itself (restore always creates a new version). The parent, unlisted, association and permission endpoints are the deliberate exception: none of them bumps `version`, so none snapshots either. `APIAdapter` has no [`importRecord()`](./portability.md#writing-history) — the server is the only snapshot writer for this adapter — so a server that implements anything less than every endpoint above silently loses rollback history for that endpoint's mutations. +**The server snapshots prior state automatically on every mutating endpoint that bumps `version`** — there is no client-initiated endpoint to write a version directly. The list is exhaustive on purpose: `PATCH /records/:id/content`, `DELETE` (soft), `POST .../undelete`, `POST .../migrate`, and `POST .../restore/:version` itself (restore always creates a new version). The parent, unlisted, association and permission endpoints are the deliberate exception: each is a [no-bump write](./versioning.md#version-history), so none snapshots either. `APIAdapter` has no [`importRecord()`](./portability.md#writing-history) — the server is the only snapshot writer for this adapter — so a server that implements anything less than every endpoint above silently loses rollback history for that endpoint's mutations. ``` GET /records/:id/versions — list all versions (newest first) @@ -484,7 +485,7 @@ The second durable tier a mutation writes is [the change journal](./journal.md), `POST .../restore/:version` accepts the same optional `If-Match` precondition described under [Optimistic concurrency](#optimistic-concurrency). It settles `content` and `typeId` alone, so it never moves the record and never lists or unlists it: neither the **403** nor the **409** a `PUT .../parent` can earn has a site here. A snapshot naming a `file-ref` the requester cannot currently reach still answers **403**, which is the only reference a restore can re-convey. See [Versioning § Restore semantics](./versioning.md#restore-semantics). -**The [change feed](./change-feed.md) reports on a wider list than snapshotting does**: every endpoint above, plus create, purge, the association and permission endpoints, and a `PATCH` naming only no-bump keys — none of which snapshots. A server that skips an endpoint there loses reactivity for that verb exactly as silently as a version-bumping endpoint's omission loses rollback history here. +**The [change feed](./change-feed.md) reports on a wider list than snapshotting does**: every endpoint above, plus create, purge and every [no-bump write](./versioning.md#version-history) — none of which snapshots. A server that skips an endpoint there loses reactivity for that verb exactly as silently as a version-bumping endpoint's omission loses rollback history here. ## Journal