Two GET endpoints that any viewer can call make the controller do outside work on every request. They are in addition to the ones in #72.
plugins/updates/+server.ts:18-23 runs a GitHub release lookup for every tracked plugin (or one with ?pluginId=). There's a five-minute cache.
chat/+server.ts:16 calls collectServerChat, which reads the log through readLogChunk (chat.ts:139, logs.ts:63) and queues a host-agent operation (native.ts:73). Calls only merge while one is already in flight (chat.ts:130-131).
ChatPanel.svelte:67 polls chat every 5 s for each open tab, even when the server is empty.
The #62 fix made the worker skip empty servers and unchanged logs, but this API path still reads every time. GETs also skip the same-origin check, so a cross-site link can trigger them.
Fix
Restrict /api/plugins/updates to operators
Serve chat from the worker's saved reader state, and start a new agent read only when the last one for that server is older than a minimum age
Two GET endpoints that any viewer can call make the controller do outside work on every request. They are in addition to the ones in #72.
- `plugins/updates/+server.ts:18-23` runs a GitHub release lookup for every tracked plugin (or one with `?pluginId=`). There's a five-minute cache.
- `chat/+server.ts:16` calls `collectServerChat`, which reads the log through `readLogChunk` (`chat.ts:139`, `logs.ts:63`) and queues a host-agent operation (`native.ts:73`). Calls only merge while one is already in flight (`chat.ts:130-131`).
- `ChatPanel.svelte:67` polls chat every 5 s for each open tab, even when the server is empty.
The #62 fix made the worker skip empty servers and unchanged logs, but this API path still reads every time. GETs also skip the same-origin check, so a cross-site link can trigger them.
**Fix**
- [x] Restrict `/api/plugins/updates` to operators
- [x] Serve chat from the worker's saved reader state, and start a new agent read only when the last one for that server is older than a minimum age
Reviewed f3b9699317 against current HEAD 4501eba873. Both fix items are done, so I'm closing this.
Checklist
Operator-only plugin update checks. The resolved /api/plugins/updates route now requires operator permission for GET and HEAD. Guard tests verify anonymous requests stop before the handler, viewer/super-viewer/moderator requests are denied, and operator/admin requests remain allowed. Query parameters cannot change this route classification. The Plugins UI already gates its update controls on operational access.
Share and age-limit chat reads. The chat API now calls readServerChat, which reuses the worker's reader state and existing in-flight work. New on-demand reads wait until the previous read completed at least 15 seconds ago; missing logs and errors observe the same cooldown. Cache keys include server registration, native instance and generation, preventing a replacement from inheriting stale state. Cached responses still filter against the current match, while background collection retains the final read after players leave.
Review notes
The chat regression tests cover repeated viewers, the exact 15-second boundary, slow worker reads, missing/error cooldowns, replacement identities, late old-generation results, match boundaries and final collection after departure.
Combined validation for #63/#98/#112 at the stated HEAD passed all 403 tests across 17 relevant files after the sandbox-blocked recovery subprocess test was rerun successfully. Type/style checks and lint passed.
Native-host documentation describes the shared cooldown. Reviewed current route, guard, worker, cache and UI callers; no blocking regressions found.
Reviewed f3b9699317fe464f29cfe78e0174b3220bbf0fa4 against current HEAD 4501eba8735ceacccc924138502ed424b3310020. Both fix items are done, so I'm closing this.
**Checklist**
- [x] **Operator-only plugin update checks.** The resolved `/api/plugins/updates` route now requires operator permission for GET and HEAD. Guard tests verify anonymous requests stop before the handler, viewer/super-viewer/moderator requests are denied, and operator/admin requests remain allowed. Query parameters cannot change this route classification. The Plugins UI already gates its update controls on operational access.
- [x] **Share and age-limit chat reads.** The chat API now calls `readServerChat`, which reuses the worker's reader state and existing in-flight work. New on-demand reads wait until the previous read completed at least 15 seconds ago; missing logs and errors observe the same cooldown. Cache keys include server registration, native instance and generation, preventing a replacement from inheriting stale state. Cached responses still filter against the current match, while background collection retains the final read after players leave.
**Review notes**
- The chat regression tests cover repeated viewers, the exact 15-second boundary, slow worker reads, missing/error cooldowns, replacement identities, late old-generation results, match boundaries and final collection after departure.
- Combined validation for #63/#98/#112 at the stated HEAD passed all 403 tests across 17 relevant files after the sandbox-blocked recovery subprocess test was rerun successfully. Type/style checks and lint passed.
- Native-host documentation describes the shared cooldown. Reviewed current route, guard, worker, cache and UI callers; no blocking regressions found.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two GET endpoints that any viewer can call make the controller do outside work on every request. They are in addition to the ones in #72.
plugins/updates/+server.ts:18-23runs a GitHub release lookup for every tracked plugin (or one with?pluginId=). There's a five-minute cache.chat/+server.ts:16callscollectServerChat, which reads the log throughreadLogChunk(chat.ts:139,logs.ts:63) and queues a host-agent operation (native.ts:73). Calls only merge while one is already in flight (chat.ts:130-131).ChatPanel.svelte:67polls chat every 5 s for each open tab, even when the server is empty.The #62 fix made the worker skip empty servers and unchanged logs, but this API path still reads every time. GETs also skip the same-origin check, so a cross-site link can trigger them.
Fix
/api/plugins/updatesto operatorsReviewed
f3b9699317against current HEAD4501eba873. Both fix items are done, so I'm closing this.Checklist
/api/plugins/updatesroute now requires operator permission for GET and HEAD. Guard tests verify anonymous requests stop before the handler, viewer/super-viewer/moderator requests are denied, and operator/admin requests remain allowed. Query parameters cannot change this route classification. The Plugins UI already gates its update controls on operational access.readServerChat, which reuses the worker's reader state and existing in-flight work. New on-demand reads wait until the previous read completed at least 15 seconds ago; missing logs and errors observe the same cooldown. Cache keys include server registration, native instance and generation, preventing a replacement from inheriting stale state. Cached responses still filter against the current match, while background collection retains the final read after players leave.Review notes