/Items/Suggestions narrows to the caller where upstream narrows to nobody #138

Closed
opened 2026-08-14 14:30:30 +02:00 by Claude · 0 comments
Collaborator

Upstream reads no user at all when userId is absent, so /Items/Suggestions answers the same rows to every account — including rows that account is refused by id. We narrow to the caller.

Measured across nine accounts while reviewing #128: upstream returns 29 rows to every one of them, folders and years included, and for allowkid twenty of those twenty-nine are items it 404s on by id. Ours returns between 1 and 19 depending on the account.

It hits the administrator too, which is what makes it this issue rather than a visibility one: it is the largest single behavioural gap left on that route and it is not about who may see what.

Note the direction. We are stricter than upstream, so closing this loosens a check — a client asking for suggestions gets rows it cannot then open. That is upstream's behaviour and the harness will report the difference either way, so the choice is to reproduce it or to argue the divergence in DECISIONS.md with a reason. The precedent for the second is D13's shape: upstream behaviour judged to have no value to agree with, written down rather than silently diverged from.

Where to start

crates/jellyfin-http/src/routes/library/discover.rs for the route, and upstream's ItemsController.GetSuggestions for what it binds — specifically what it does when userId is absent versus present, since the fix is likely to be "read the user only when the request named one".

How to know it is done

The route answers the same rows as upstream for an unrestricted and a restricted account alike, or DECISIONS.md says why it does not and the harness carries an entry with a reason and an expiry.

House rules

Tests run under cargo nextest; upstream's source is the checkout in upstream/ at the pin; generated artifacts are regenerated, never edited; comments say what the code does, not how it got there. The reference on :8096 is read-only — and Studios=, Artists= and Albums= are writes, because upstream resolves them through CreateItemByName. Anything needing a write gets its own container from the pinned image over a cp -a copy of a snapshot. Kill a server by the PID ss -lntpH 'sport = :NNNN' reports; never pkill, killall, or pgrep -f with a pattern your own command line contains.

Upstream reads no user at all when `userId` is absent, so `/Items/Suggestions` answers the same rows to every account — including rows that account is refused by id. We narrow to the caller. Measured across nine accounts while reviewing #128: upstream returns **29 rows to every one of them**, folders and years included, and for `allowkid` twenty of those twenty-nine are items it 404s on by id. Ours returns between 1 and 19 depending on the account. **It hits the administrator too**, which is what makes it this issue rather than a visibility one: it is the largest single behavioural gap left on that route and it is not about who may see what. Note the direction. **We are stricter than upstream**, so closing this *loosens* a check — a client asking for suggestions gets rows it cannot then open. That is upstream's behaviour and the harness will report the difference either way, so the choice is to reproduce it or to argue the divergence in `DECISIONS.md` with a reason. The precedent for the second is D13's shape: upstream behaviour judged to have no value to agree with, written down rather than silently diverged from. ## Where to start `crates/jellyfin-http/src/routes/library/discover.rs` for the route, and upstream's `ItemsController.GetSuggestions` for what it binds — specifically what it does when `userId` is absent versus present, since the fix is likely to be "read the user only when the request named one". ## How to know it is done The route answers the same rows as upstream for an unrestricted and a restricted account alike, or `DECISIONS.md` says why it does not and the harness carries an entry with a reason and an expiry. ### House rules Tests run under `cargo nextest`; upstream's source is the checkout in `upstream/` **at the pin**; generated artifacts are regenerated, never edited; comments say what the code does, not how it got there. The reference on `:8096` is read-only — and `Studios=`, `Artists=` and `Albums=` are *writes*, because upstream resolves them through `CreateItemByName`. Anything needing a write gets its own container from the pinned image over a `cp -a` copy of a snapshot. Kill a server by the PID `ss -lntpH 'sport = :NNNN'` reports; never `pkill`, `killall`, or `pgrep -f` with a pattern your own command line contains.
alex closed this issue 2026-08-16 07:06:51 +02:00
Sign in to join this conversation.
No description provided.