fix(api): honor the request's origin as the writer label — app rows stop wearing "web"
Release / build-and-push (push) Successful in 3m36s

Every web-API write endpoint hardcoded Source:"web", so the Android
app's own pushes landed with last_sync_source/last_modified_source
"web" — the app was indistinguishable from the web SPA in progress
conflicts, device_sync_data, and annotation bookkeeping. The app CAN
say who it is (bookmark create already carried origin → origin_source);
the server just ignored it for labeling.

All six write sites (progress PUT, bookmark/note/highlight create+PUT)
now parse an ADDITIVE optional "origin" key, normalized through one
gate: "android"/"ios" pass through, anything absent/unknown stays
"web" (the label keys conflict claims and device_sync_data — free-form
client strings would pollute them). Precedence on progress: a paired
X-Device-Token still outranks origin. Bookmark origin_source now rides
the same gate. The web SPA sends no origin — its rows are unchanged;
KOReader/Kobo handlers keep their own labels.

Wire discipline: additive request keys only (rule 2), response shapes
untouched, pins changelog widened in-commit, locked by
TestOriginWriterAttribution (android lands, default stays web, unknown
normalizes). Full battery: only the two known pre-existing failures
(TestOPDSLibraryFolders rot, TestWebSocketPingPong — both reproduce
with the change stashed).
This commit is contained in:
John O'Keefe committed 2026-10-17 21:44:27 -04:00
1 parent 8f05f1116e
commit 45b8542e25
4 files changed
+130 -10

No files matched your search

+10
View File
@@ -92,3 +92,13 @@ cover_image_path, has_conflict, format_group, badge`.
search row gained the two columns it lagged (EXPLAIN plan-identical —
both ride the already-scanned media_items side), and the endpoint
serializes the row directly, so they emit. No key removed or renamed.
- **Origin writer attribution (post-hardening parity)** — every web-API
write endpoint now parses an ADDITIVE optional request key `origin`
("android" | "ios"; anything else stays "web") and records it as the
writer: progress PUT → `last_sync_source`, bookmark/note/highlight
create+PUT → `last_modified_source` (and `origin_source` on bookmark
creates, now normalized through the same gate). Precedence on progress:
a paired X-Device-Token still outranks origin. The web SPA sends no
origin — its rows are unchanged. Request-parse widening documented per
rule 2; response shapes untouched; locked by
TestOriginWriterAttribution.