fix(apps): second Sol pass — deadlock, fencing, idempotency, OAuth binding, update grants
Radin committed
- Author
- Radin · radin@coline.appOct 1, 2026 at 06:43
- Committer
- Radin · radin@coline.appOct 1, 2026 at 06:43
- Parents
- fbed431f05c6
fix(apps): second Sol pass — deadlock, fencing, idempotency, OAuth binding, update grants Sol re-checked every earlier fix against HEAD. Fixed here, each verified: Regressions from the first pass - OAuth refresh held a transaction and then read the client secret on the global pool: a deadlock with a small pool. The secret is read first. - Upload cleanup could delete bytes of a registered file when completion failed after registering it; the sweeper now keeps those and marks the session done. - "Accept & update" re-granted every declared permission, restoring ones the workspace had revoked and skipping policy. It now grants only what the new version adds, then re-evaluates workspace policy. Gaps that remained - jobs.enqueue idempotency keys are scoped to the principal (another member reusing a key got the first member's run and result), and a retry is answered before the queue cap. - Jobs store the enqueuer's effect (migration 0379, effect_ceiling) and never run above it, even if an update declares the job stronger. - Checkpoint and progress writes are fenced to the claim the token was minted for (a late write from an earlier segment overwrote the next). - Cancelled jobs drop their payload (enqueue-and-cancel held storage outside the cap). - net.fetch scrubbing covers JSON-escaped forms and scrubs before truncating; secrets shorter than 8 characters are not injected. - Account labels are checked before being shortened (a truncated label could carry most of a reflected token). - OAuth completion re-checks membership, the connections grant, and admin rights, and only completes against the setup it started with (migration 0380, connection_fingerprint): an update mid sign-in can no longer redirect the code or turn a personal account into a shared one. - Re-consent compares mutation class and network reach separately (external → destructive escalates) and on any change to an existing endpoint's verification (HMAC → token, dropped timestamps). - Code publishing keys: the project binding applies to any key recorded as one, whatever its scope; provisioning binds a key by compare-and-set so concurrent attempts cannot store a mismatched pair; every app-key candidate is checked, with random suffixes as a last resort. - Async endpoint bodies: the queue cap covers worst-case JSON escaping of a 5 MB body. Migrations 0379 and 0380 (one nullable column each) are applied to the dev database. Full-stack 74/74, Code app 21/21, store 18, private 19, paid 22. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed+27−24