Media Management Technical Guide
Complete technical reference for Liberty Dynamic's media management system using Supabase Storage + Keystatic CMS.
🚨 CRITICAL SAFETY WARNING
⚠️ PRODUCTION PROTECTION ⚠️
Your production Supabase contains real business assets that power your live website. Any destructive operations could result in catastrophic data loss.
NEVER DO ON PRODUCTION:
- ❌ DELETE buckets or policies
- ❌ DROP tables or storage objects
- ❌ Experimental migrations
- ❌ Bulk delete operations
- ❌ Policy modifications without testing
ALWAYS DO:
- ✅ Test locally first
- ✅ Use MCP tools for safe operations
- ✅ Backup before changes
- ✅ ADD-only operations when possible
Table of Contents
- Architecture Overview
- Business-Focused Bucket Structure
- Development Setup
- Console Access & Management
- CLI Tools Reference
- MCP Tools for Safe Operations
- Security & RLS Policies
- Performance & Optimization
- Troubleshooting
- Emergency Procedures
Architecture Overview
Hybrid Storage Strategy
Why Production Supabase for Media:
- ✅ Team Collaboration: All developers/content creators see same media
- ✅ Cross-Branch Consistency: Media URLs work across all git branches
- ✅ Global CDN: Fast delivery via 285+ edge locations
- ✅ No GitHub Bloat: Repository stays lightweight
- ✅ Professional Workflow: Proper separation of code vs. assets
Development Setup:
Development Environment: ├── Database/Auth: Local Supabase (127.0.0.1:54321) └── Media Storage: Production Supabase (shared assets) Production Environment: ├── Database/Auth: Production Supabase └── Media Storage: Production Supabase (same as dev)
Technology Stack
- Storage: Supabase Storage (Postgres + CDN)
- CMS: Keystatic (Git-based, URL references)
- Framework: Next.js with optimized Image component
- CLI Tools: Custom Node.js scripts
- Management: Supabase MCP tools
Business-Focused Bucket Structure
Current Production Buckets
| Bucket | Purpose | Use Cases | Max Size |
|---|---|---|---|
media_library | General CMS content | Blog images, page assets, general content | 25MB |
website_media | Visual website assets | Product photos, logos, branding elements | 25MB |
marketing_media | Downloadable/shareable content | PDFs, slide decks, videos, brochures | 50MB |
demonstration_media | Product demo library | Demo videos, tutorial images, use cases | 50MB |
legal_documents | Legal/compliance materials | Terms, privacy, contracts | 10MB |
Folder Organization Standards
media_library/ ├── uploads/ # General uploads ├── blog/ # Blog post images ├── pages/ # Page-specific assets └── icons/ # UI icons website_media/ ├── products/ # Product photography ├── logos/ # Company branding ├── team/ # Team photos └── branding/ # Brand assets marketing_media/ ├── brochures/ # PDF marketing materials ├── presentations/ # Slide decks ├── videos/ # Marketing videos └── social/ # Social media assets demonstration_media/ ├── videos/ # Product demo videos ├── tutorials/ # How-to content ├── use-cases/ # Real-world examples └── screenshots/ # Product screenshots legal_documents/ ├── terms/ # Terms of service ├── privacy/ # Privacy policies └── contracts/ # Legal agreements
Development Setup
Environment Configuration
Required Variables (.env.production):
# Production Supabase for media (shared across team) NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co SUPABASE_SERVICE_ROLE_KEY=your-service-role-key NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key # Optional: Media-specific overrides NEXT_PUBLIC_MEDIA_SUPABASE_URL=https://your-project.supabase.co MEDIA_SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
Local Development (.env.development):
# Local Supabase for database/auth NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321 SUPABASE_SERVICE_ROLE_KEY=your-local-service-key # Production Supabase for media (team sharing) NEXT_PUBLIC_MEDIA_SUPABASE_URL=https://your-project.supabase.co MEDIA_SUPABASE_SERVICE_ROLE_KEY=your-production-service-key
Initial Setup Verification
# 1. Verify environment loading node scripts/debug-env.mjs # 2. Test media connection pnpm media:list --bucket=media_library # 3. Check bucket access node scripts/setup-production-media-simple.mjs
Console Access & Management
Accessing Supabase Dashboard
- Navigate to Dashboard: https://app.supabase.com
- Select Project:
ld_website - Go to Storage: Storage → Buckets
Console Operations
File Upload via Console
- Select target bucket (e.g.,
website_media) - Click "Upload files"
- Drag/drop or select files
- Upload to appropriate folder
- Copy public URL for Keystatic
Bucket Management
- View Usage: Monitor storage consumption
- Set Policies: Configure access permissions
- File Operations: Download, move, delete files
- Access Logs: Monitor file access patterns
Public URL Format
https://mgyzvwmmbtmtllliixue.supabase.co/storage/v1/object/public/{bucket}/{path}
Example:
https://mgyzvwmmbtmtllliixue.supabase.co/storage/v1/object/public/website_media/logos/logo-primary.svg
CLI Tools Reference
Available Commands
# Help and information pnpm media:help # Show all commands node scripts/media-cli.mjs help # Detailed CLI help # File operations pnpm media:list --bucket=media_library pnpm media:stats --bucket=website_media pnpm media:validate --bucket=marketing_media # Upload operations pnpm media:upload --bucket=website_media --folder=./assets node scripts/bulk-media-upload.mjs --bucket=demonstration_media --folder=./videos
Bulk Upload Script
# Upload directory with validation node scripts/bulk-media-upload.mjs \ --bucket=marketing_media \ --folder=./marketing-assets \ --destination=brochures/ \ --filter=pdf,png,jpg \ --max-size=25 \ --validate=true # Dry run to preview node scripts/bulk-media-upload.mjs \ --bucket=website_media \ --folder=./product-images \ --dry-run=true
File Management CLI
# List files with details node scripts/media-cli.mjs list \ --bucket=demonstration_media \ --folder=videos/ \ --sort=date \ --limit=50 # Copy between buckets node scripts/media-cli.mjs copy \ --source=media_library \ --destination=website_media \ --filter=logo \ --dry-run=true # Validate organization node scripts/media-cli.mjs validate \ --bucket=marketing_media
MCP Tools for Safe Operations
Why Use MCP Tools
MCP (Model Context Protocol) tools provide safe, programmatic access to your Supabase production environment:
- ✅ Non-destructive operations
- ✅ Automatic validation
- ✅ Audit trail
- ✅ Error handling
- ✅ No manual SQL risks
Safe Bucket Management
Adding New Buckets
// Use MCP tools for safe bucket creation
await mcp_supabase_apply_migration({
project_id: "mgyzvwmmbtmtllliixue",
name: "add_new_bucket_safely",
query: `
-- Create new bucket safely
INSERT INTO storage.buckets (id, name, PUBLIC)
VALUES ('new_bucket_name', 'new_bucket_name', true)
ON CONFLICT (id) DO NOTHING;
-- Add to existing policies (non-destructive)
CREATE POLICY "new_bucket_read" ON storage.objects
FOR SELECT USING (bucket_id = 'new_bucket_name');
`
});
Updating RLS Policies
// Safe policy addition (never destructive)
await mcp_supabase_apply_migration({
project_id: "mgyzvwmmbtmtllliixue",
name: "add_bucket_to_existing_policies",
query: `
-- Drop and recreate policies with new bucket included
DROP POLICY IF EXISTS "Public Read Access" ON storage.objects;
CREATE POLICY "Public Read Access" ON storage.objects
FOR SELECT
USING (bucket_id IN (
'media_library', 'website_media', 'marketing_media',
'demonstration_media', 'legal_documents', 'new_bucket_name'
));
`
});
MCP Tool Examples
Check Current Buckets
// Safe read-only operation
const buckets = await mcp_supabase_execute_sql({
project_id: "mgyzvwmmbtmtllliixue",
query: "SELECT id, name, public FROM storage.buckets ORDER BY id;"
});
Verify Policies
// Check existing policies
const policies = await mcp_supabase_execute_sql({
project_id: "mgyzvwmmbtmtllliixue",
query: `
SELECT policyname, cmd, qual
FROM pg_policies
WHERE tablename = 'objects' AND schemaname = 'storage'
ORDER BY policyname;
`
});
Security & RLS Policies
Current Policy Structure
Optimized Policies (Applied):
- Public Read Access: Anyone can view/download media
- Authenticated Insert Access: Only authenticated users can upload
- Authenticated Update Access: Only authenticated users can modify
- Authenticated Delete Access: Only authenticated users can delete
Policy Implementation
-- Current production policies (DO NOT MODIFY)
CREATE POLICY "Public Read Access" ON storage.objects
FOR SELECT
USING (bucket_id IN (
'media_library', 'website_media', 'marketing_media',
'demonstration_media', 'legal_documents'
));
CREATE POLICY "Authenticated Insert Access" ON storage.objects
FOR INSERT
WITH CHECK (
bucket_id IN (
'media_library', 'website_media', 'marketing_media',
'demonstration_media', 'legal_documents'
)
AND (auth.role() = 'authenticated')
);
Security Best Practices
Service Role Key Protection:
- Never expose in client code
- Store in environment variables only
- Rotate periodically
- Monitor usage logs
Bucket Access Control:
- Use public buckets for website assets
- Keep sensitive documents private
- Regular policy audits
File Validation:
- Validate file types before upload
- Check file sizes against limits
- Scan for malicious content
Performance & Optimization
Image Optimization
Automatic Optimization via Next.js
import { MediaImage } from '~/components/media';
// Auto-optimized with Next.js Image component
<MediaImage
src="https://mgyzvwmmbtmtllliixue.supabase.co/storage/v1/object/public/website_media/products/tactical-vest.jpg"
alt="Tactical vest front view"
width={800}
height={600}
priority={true} // For above-the-fold images
/>
Manual Optimization
import { getOptimizedImageUrl } from '~/lib/media';
// Generate optimized URLs
const optimizedUrl = getOptimizedImageUrl(
'website_media',
'products/tactical-vest.jpg',
{
width: 800,
height: 600,
quality: 85,
format: 'webp'
}
);
CDN Performance
Supabase CDN Features:
- 285+ Edge Locations: Global content delivery
- Automatic WebP Conversion: Modern format support
- Responsive Transformations: On-the-fly resizing
- Smart Caching: Optimized cache headers
Performance Monitoring
# Check file sizes node scripts/media-cli.mjs validate --bucket=website_media | grep "Size Issues" # Monitor storage usage node scripts/media-cli.mjs stats --bucket=marketing_media # Find large files node scripts/media-cli.mjs list --bucket=demonstration_media | grep "MB"
Troubleshooting
Common Issues
1. Upload Failures
Symptoms: Files fail to upload
# Debug steps node scripts/bulk-media-upload.mjs \ --bucket=website_media \ --folder=./test \ --dry-run=true \ --validate=true
Solutions:
- Check file size limits per bucket
- Verify MIME types are allowed
- Ensure proper authentication
- Test with smaller files first
2. Access Denied
Symptoms: Cannot view uploaded files
Debug Commands:
# Check if file exists node scripts/media-cli.mjs list --bucket=website_media --folder=logos/ # Test authentication node scripts/debug-env.mjs
Solutions:
- Verify RLS policies are correct
- Check environment variables
- Ensure bucket is public if needed
- Review service role key permissions
3. Keystatic Integration Issues
Symptoms: Media not displaying in CMS
Checklist:
- ✅ File uploaded to correct bucket
- ✅ Public URL is accessible
- ✅ Media Library entry created
- ✅ URL field populated correctly
Debug Tools
Environment Debugging
# Test environment and connection node scripts/debug-env.mjs # Verify bucket structure node scripts/setup-production-media-simple.mjs
File Validation
# Comprehensive validation node scripts/media-cli.mjs validate --bucket=marketing_media # Check specific issues node scripts/media-cli.mjs validate --bucket=website_media | grep -E "(error|warning)"
Emergency Procedures
Data Loss Prevention
Before Any Changes
Document current state:
# Backup bucket list node scripts/media-cli.mjs list --bucket=website_media > backup-website-media.txt # Save policy state # Use MCP tools to query current policies
Test in development first
Have rollback plan ready
Backup Procedures
# Create local backup of critical buckets for bucket in website_media marketing_media; do echo "Backing up $bucket..." # Use bulk download (implement if needed) node scripts/media-cli.mjs list --bucket=$bucket > "backup-$bucket-$(date +%Y%m%d).txt" done
Recovery Procedures
Accidental File Deletion
- Check Supabase Dashboard logs
- Review recent operations
- Restore from local backups if available
- Re-upload missing files
Policy Issues
- Never modify policies directly
- Use MCP tools for safe changes
- Test with single file first
- Rollback immediately if issues
Emergency Contacts
- Supabase Support: Dashboard → Support → New Ticket
- Team Lead: Internal escalation
- Documentation: This guide + Supabase docs
Best Practices Summary
Development Workflow
- ✅ Test locally first before production changes
- ✅ Use MCP tools for all production modifications
- ✅ Validate files before bulk uploads
- ✅ Document changes in commit messages
Content Management
- ✅ Use descriptive filenames following conventions
- ✅ Organize in appropriate folders within buckets
- ✅ Include alt text for all images
- ✅ Optimize file sizes before upload
Security & Safety
- ✅ Never expose service keys in client code
- ✅ Use environment variables for all credentials
- ✅ Monitor access logs regularly
- ✅ Backup before major changes
Performance
- ✅ Optimize images before upload (< 2MB ideal)
- ✅ Use WebP format when possible
- ✅ Implement responsive images with Next.js
- ✅ Monitor CDN performance via dashboard
This guide is your primary reference for all media management operations. Always prioritize safety and test changes in development first.