Skip to content

Public Supabase bucket posture review

What happened

Jackson asked publicly in #product-eng (2026-05-11 09:18 ET) who owns these 8 public Supabase Storage buckets and why they're public:

  • service-experts-assets
  • voice-samples — Christian: "can be private, just need to fix the one screen that reads from it"
  • technician-portraits
  • background-souund (sic)
  • public-client-assets — Ankur: hosts hold music for Vapi warm transfers; Vapi requires public URLs to download/play audio; no PII
  • mr-handyman-northern-st-joseph-1066-assets
  • compliance-screenshots — Aditya: used to share with Twilio for A2P compliance; no sensitive PII; can move to signed URLs since assets are already wrapped in Bitly
  • del-air-assets

Jackson's framing: even non-PII public storage carries risk (anonymous upload = $ risk; anonymous read = customer enumeration risk) and we lose access observability. Direction is to consolidate per-customer buckets, then proxy reads through a signed-URL endpoint.

Why we care

This is not a direct incident on our work. It's the security thread to watch if our work ever touches Supabase Storage:

  • If we add test-phone assets, voice samples, or any customer-specific media for the EAS engagement, the precedent is now "private bucket + signed-URL proxy", not "public bucket + direct URL".
  • If we read existing hold-music for Vapi warm transfers from public-client-assets in any code we author, that fetch path will change when the signed-URL proxy lands. Worth watching for the API contract change.

The direction (no code change yet)

Jackson is pushing for a proxy pattern: a route like app.avoca.ai/public-client-assets/teamId/hold-music.mp4 302s to a signed URL on a private bucket. Benefits cited:

  • Closes anonymous upload + read attack surface.
  • Gives observability on asset access.
  • Consolidates per-customer buckets to public-client-assets/team_id/{asset}.

Open question Ankur raised: whether Vapi's hold-music fetcher works correctly with a 302 to a signed URL (worth a manual test before migrating).

Per Ankur's offer, the consolidation effort owns reducing per-customer buckets (del-air-assets, technician-portraits, mr-handyman-northern-st-joseph-1066-assets, etc.) into the consolidated bucket.

What we should mirror

  • If we add any Supabase Storage usage for the EAS engagement, default to private bucket + signed-URL access via a server endpoint. No new public buckets.
  • For Vapi-driven media (hold music, voice samples), confirm the playback client tolerates 302 redirects to signed URLs before assuming the proxy pattern is drop-in.
  • Per-customer bucket proliferation is the antipattern; one consolidated bucket with team_id path segmentation is the preferred shape.
  • Slack thread: #product-eng, 2026-05-11 09:18 ET. Jackson opened; owners (Aditya, Ankur, Esteban, Christian) responded.
  • Vapi hold-music context: see also [upload hold audio url using signed urls for warm transfers](https://github.com/AvocaAI/avoca-next/commit/edfa3029ce) (edfa3029ce), an earlier commit on the same theme.
  • Related incident: none in this set; this is a governance thread, not a defect.