Conditional room writes with ETag and If-Match #15

Merged
oss merged 2 commits from feat/XSH-1-conditional-room-put into selfhost 2026-11-11 12:52:07 +01:00
Owner

Part of Kaneo Live region shares for Excalidraw boards (XSH-1).

  • GET /rooms/:id returns an ETag: a quoted base64url SHA-256 of the stored bytes.
  • PUT /rooms/:id honours If-Match (write only if the stored ETag still matches) and If-None-Match: * (create only). On a mismatch it answers 412.
  • Every write to one room, conditional or not, goes through a per-room chain, so a check and its write can't interleave with another writer. The backend runs as a single process.
  • Unconditional PUTs, from apps not yet updated, behave as before. files and scenes are unchanged.

Used by the live-share bridge (oss/excalidraw-share#1) and the fork's app saves (oss/excalidraw#5), so neither can overwrite a save it didn't merge.

Tests: 88 of 88 pass, including two racing conditional writes (exactly one wins) and an unconditional write queued behind a conditional one. Reviewed by GPT-6.1-Sol: PASS.

Deploy this first. The bridge refuses to save to a backend that sends no ETag. Our image is built by hand, because release.yml targets upstream's ghcr.io.

🤖 Generated with Claude Code

Part of Kaneo *Live region shares for Excalidraw boards* (XSH-1). - `GET /rooms/:id` returns an `ETag`: a quoted base64url SHA-256 of the stored bytes. - `PUT /rooms/:id` honours `If-Match` (write only if the stored ETag still matches) and `If-None-Match: *` (create only). On a mismatch it answers **412**. - Every write to one room, conditional or not, goes through a per-room chain, so a check and its write can't interleave with another writer. The backend runs as a single process. - Unconditional PUTs, from apps not yet updated, behave as before. `files` and `scenes` are unchanged. Used by the live-share bridge ([oss/excalidraw-share#1](https://git.ossalali.com/oss/excalidraw-share/pulls/1)) and the fork's app saves ([oss/excalidraw#5](https://git.ossalali.com/oss/excalidraw/pulls/5)), so neither can overwrite a save it didn't merge. Tests: 88 of 88 pass, including two racing conditional writes (exactly one wins) and an unconditional write queued behind a conditional one. Reviewed by GPT-6.1-Sol: PASS. **Deploy this first.** The bridge refuses to save to a backend that sends no ETag. Our image is built by hand, because `release.yml` targets upstream's ghcr.io. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
oss merged commit 548d26d38d into selfhost 2026-11-11 12:52:07 +01:00
oss deleted branch feat/XSH-1-conditional-room-put 2026-11-11 12:52:07 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
oss/excalidraw-storage-backend!15
No description provided.