Uh oh!
There was an error while loading. Please reload this page.
fix(storage): converge the avatars bucket on public read (#316) - #502
Conversation
Staging has avatars public=false while prod is public=true and 0029 records the intent explicitly. Avatar <img> src values are plain public object URLs, so a private bucket serves 400s and every avatar on staging renders broken. The reason this never self-heals is the interesting part: BOTH mechanisms that "create" the bucket decline to fix an existing one. - 0011_avatars_bucket.sql inserts it with public=true but ends in ON CONFLICT (id) DO NOTHING — a no-op once the row exists. - storage_service.ensure_bucket_exists (lifespan, public=True) treats the Storage API's 409 as success and deliberately does NOT overwrite settings, "in case an admin has intentionally tuned them in the dashboard". So a bucket that came into existence private stays private forever, through any number of deploys and migrations. An UPDATE is the only thing that corrects it — hence a new file rather than a re-run of 0011. This deliberately overrides that "an admin may have tuned it" stance for THIS bucket: the read path is unauthenticated <img src> against /storage/v1/object/public/avatars/..., so private isn't a valid tuning, it's broken avatars. Verified locally: from-empty replay applies the whole chain clean with 0041 in it; and against a bucket forced private to simulate the staging drift, the statement flips it to true, is idempotent on re-run, and is a safe zero-row no-op where the bucket doesn't exist. Does NOT fix staging by itself — that needs `python -m db.migrate` against that project. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Warning Review limit reached
Next review available in:19 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with |
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs | frontend-staging | 201f4cd | Commit Preview URL Branch Preview URL | Jul 31 2026, 06:25 PM |
AndresL230
commented
Jul 31, 2026
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. Independently verified beyond the diff:
🤖 Generated with Claude Code |
Part of #316.
The issue's suggested fix would have worked, but not lasted
The issue proposes flipping the staging bucket to public. That fixes staging. It does not explain why staging drifted, and it leaves the same drift possible on the next environment.
Both mechanisms that "create" this bucket decline to correct an existing one:
0011_avatars_bucket.sqlinserts it withpublic = truebut ends inON CONFLICT (id) DO NOTHING— a no-op once the row exists.storage_service.ensure_bucket_exists(called frommain.py's lifespan withpublic=True) treats the Storage API's409as success and deliberately does not overwrite settings, "in case an admin has intentionally tuned them in the dashboard".So a bucket that came into existence private stays private forever, through any number of deploys and migrations. An
UPDATEis the only thing that corrects it — hence a new file rather than a re-run of 0011.The one judgment call
This deliberately overrides
ensure_bucket_exists's "an admin may have tuned it" stance, for this bucket only. The read path is an unauthenticated<img src>against/storage/v1/object/public/avatars/..., so private isn't a valid tuning — it's broken avatars.0029already recorded the intent ("avatarsstays public (intended public read)"); this asserts that state instead of assuming it.Verification
scripts/local-db-reset.sh, not a normal cycle —e2e-upruns against a DB that already has the schema): the whole chain applies clean with 0041 in it.avatarstopublic=falseto reproduce the staging condition, then ran the statement — flips totrue, idempotent on re-run, and a safe zero-row no-op against a non-existent bucket id.This does not fix staging by itself
It converges on the next
python -m db.migrateagainst that project. #316 should stay open until that's run — I don't have (and shouldn't use) staging credentials for it.🤖 Generated with Claude Code