Storage overview and setup
Understand the three included asset paths before adding a new bucket or file feature.
Three different asset paths
The starter stores personal avatars, workspace logos and public product artwork differently. They have different owners and deletion rules. Do not put them all in one public bucket to simplify URLs.
| Asset | Storage | Owner/access | Cleanup |
|---|---|---|---|
| Personal avatar | Private Supabase avatars bucket | Verified account owner; signed display URL | Explicit Storage removal before Auth deletion |
| Workspace logo | RLS-protected database row | Verified workspace members read; owners/admins write | Atomic cascade with workspace deletion |
| Brand/blog/docs artwork | Repository public/ directory | Public visitor | Source change and deployment |
Account → private avatar object → expiring signed URL. Workspace → database logo row → authenticated image route. Published source → public static asset → public URL.
The included storage features are image settings, not a general document manager, S3 abstraction or arbitrary workspace upload service. Additional file types and buckets require their own validation, policies, retention and cleanup design.
Automatic installation
Kickstart applies the versioned account settings and workspace-logo migrations after required preflight. The avatar migration creates the private bucket and owner policies. Logo migrations create the table, manager write RPC and membership-based reads. Setup verifies the expected schema/permissions rather than asking buyers to click-create a collection of generic buckets.
The original avatar setup is in supabase/migrations/20260913234743_account_settings.sql. Follow later migrations as well: the final installed schema is the cumulative result. Never edit that released file to change an existing installation. Add a new migration for a policy or limit change and test it on a dedicated hosted target.
Avatar ownership
The object path is <user-id>/avatar.webp. The server derives the user ID from verified authentication; a request does not select another account's folder. Profile policies prevent a user from referencing another user's avatar path.
The bucket is private. Reads, inserts, replacements and deletes each need the appropriate Storage policy. Replacement uses upsert, which requires the relevant select/insert/update access, not only insert. The action normalizes input to a square WebP and stores the canonical path on the profile.
Signed display URLs last one hour. Anyone holding a still-valid signed URL can use that bearer URL, so do not treat it as a membership check on every view or write it into public documents. Generating a new signed URL uses the user's authorized Storage access. Supabase Storage access control.
Database-backed workspace logos
Small workspace logos are stored in public.workspace_logos, with org_id, image content and a version. saveWorkspaceLogo() calls set_workspace_logo after checking the current verified manager. The image route at /api/workspaces/[id]/logo verifies membership and returns a noncached image.
This avoids a separate Storage cleanup transaction for these small assets: deleting the workspace cascades to the logo row. The sidebar shows the same logo in the active workspace, workspace options and collapsed rail. A departed member's subsequent logo request loses access; existing bytes already downloaded cannot be recalled.
Limits and normalization
Both settings accept JPG, PNG or WebP input up to 2 MB and produce 256 × 256 WebP. Sharp decodes with a 25-million-pixel bound, rotates from metadata, crops and removes metadata by re-encoding. Logo validation also verifies a still supported format and caps encoded output at 128 KiB.
Those are image-feature limits, not subscription allowances. If you increase input size, check server action body limits, decoded pixel limits, runtime memory/time, output limits, bucket restrictions and policies together. A larger HTML accept list does not implement a new safe file type.
Inspect the hosted configuration
In the intended Supabase project, open Storage and inspect the avatars bucket's private status and policies. Use the project SQL/Table Editor to inspect the workspace logo schema and relevant policies. Reading metadata is enough to inspect configuration; do not create a new bucket or change permissions just for a screenshot.
Confirm each policy's operation, target role and owner predicate. A bucket name alone is not evidence of isolation. Check a second account attempting a different avatar path and a nonmember requesting a workspace logo. Configuration inspection and live access tests are separate evidence.
Public assets
Put product-owned, approved files in public/ and reference them with an absolute site path such as /blog/your-image.webp. These files are public by design and shipped with source/deployment. They are suitable for branding, blog thumbnails and sanitized documentation illustrations.
Do not place provider screenshots awaiting review, uploaded user images, account exports or environment files there. Disabling search indexing does not make a public asset private. Replacing a static asset requires deployment; changing a database blog paragraph does not.
Extend storage deliberately
Before adding a notes attachment bucket, specify its workspace ownership, accepted MIME types, maximum size, read/download authorization, replacement policy and account/workspace cleanup behavior. Decide whether signed URLs may remain usable after membership changes. Add metadata rows with tenant RLS and Storage object policies together.
Test a forged object path, wrong role, stale membership, oversized/malformed file, replacement and deletion failure. The starter's avatar implementation is a pattern to study, not a complete arbitrary-upload backend. Continue with upload, access and deletion.
Template 0.0.1 · September 2026 source snapshot