fix(apps): queue fencing and fairness, OAuth trust binding, scoped publish keys, maintenance loop
Radin committed
- Author
- Radin · radin@coline.appOct 1, 2026 at 06:06
- Committer
- Radin · radin@coline.appOct 1, 2026 at 06:06
- Parents
- d85fcb8236be
fix(apps): queue fencing and fairness, OAuth trust binding, scoped publish keys, maintenance loop From Sol (GPT-6.1) reviews of the runtime and platform, each verified in code; full-stack E2E 74/74 (5 new checks), Code app-project 21/21 (5 new), store 18, private 19, paid 22. Queue - Completion is fenced to the claim (attempt + worker): a worker that stalled past its lease can no longer overwrite the attempt that replaced it. - Per-job concurrency admits runs in claim order. Two runs claimed together each counted the other and both requeued, forever. - The runtime worker runs jobs in background mode: slots refill as runs finish instead of idling behind the slowest run of a batch. - jobs.get/list/cancel show a member their own runs and the app's, not other members' (results can hold what only they could read). - At most 1,000 queued jobs per install; endpoint requests may queue the full body their endpoint accepts (was rejected above 256 kB as a 500, now sized for 5 MB base64, and an oversize body is a 413). Network and connections - Decompression is async and capped while decoding (a 32 kB gzip expanded to 32 MB in the web process before the size check). - Connections record the token URL and client they were made with; a version that changes either gets "reconnect" instead of the stored refresh token. Refreshes serialize on a row lock and re-read, so a rotating refresh token is never used twice. - Changing a connection's client id or user/workspace scope requires re-consent. Platform - Code App projects get an `apps.publish` key that can publish only that project's app (checked against the project) and nothing else; it was a workspace-wide apps.write key any collaborator could use on other apps or installs. - App keys for new App projects get a stable suffix when the name is taken (two "Customer Pulse" projects collided on a global key). - Public pages stop for a lapsed paid app, and every render is rate limited like public tools (renders spent the install's budget freely). Files - Uploads are recorded as the app's files (type "upload"), so listing, rename, trash, and move work on them; readers that treat app files as JSON documents (preview, editor surface, package export, document writes) skip uploads. - Expired, never-completed direct uploads are deleted from storage. Housekeeping - No scheduler calls /api/internal/apps/update-sweep in production (checked Cloud Scheduler), so auto-updates, retention, uninstall purge, and paid-grant backfill never ran. They now run with upload cleanup every 10 minutes in the runtime worker; the route stays as a fallback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed+10−2