Media Management Technical Guide

Complete technical reference for Liberty Dynamic's media management system including CLI tools, MCP operations, security, and emergency procedures.

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

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

BucketPurposeUse CasesMax Size
media_libraryGeneral CMS contentBlog images, page assets, general content25MB
website_mediaVisual website assetsProduct photos, logos, branding elements25MB
marketing_mediaDownloadable/shareable contentPDFs, slide decks, videos, brochures50MB
demonstration_mediaProduct demo libraryDemo videos, tutorial images, use cases50MB
legal_documentsLegal/compliance materialsTerms, privacy, contracts10MB

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

  1. Navigate to Dashboard: https://app.supabase.com
  2. Select Project: ld_website
  3. Go to Storage: Storage → Buckets

Console Operations

File Upload via Console

  1. Select target bucket (e.g., website_media)
  2. Click "Upload files"
  3. Drag/drop or select files
  4. Upload to appropriate folder
  5. 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

  1. Service Role Key Protection:

    • Never expose in client code
    • Store in environment variables only
    • Rotate periodically
    • Monitor usage logs
  2. Bucket Access Control:

    • Use public buckets for website assets
    • Keep sensitive documents private
    • Regular policy audits
  3. 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

  1. 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
    
  2. Test in development first

  3. 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

  1. Check Supabase Dashboard logs
  2. Review recent operations
  3. Restore from local backups if available
  4. Re-upload missing files

Policy Issues

  1. Never modify policies directly
  2. Use MCP tools for safe changes
  3. Test with single file first
  4. 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.