Supabase Implementation Summary¶
Date: November 13, 2025 Status: ✅ Development Complete - Ready for Production Deployment Implementation: Phase 3.6 - Supabase Authentication & Storage System
Executive Summary¶
Successfully implemented Supabase Row Level Security (RLS) and Supabase Storage integration to replace S3/local storage and enhance security. The implementation is complete in development and ready for production deployment.
Key Benefits¶
- Cost Savings: $79-139/year (eliminates S3 costs, no Redis needed for JWT)
- Security: Database-level privacy enforcement (defense in depth)
- Simplified Architecture: JWT-based auth with automatic refresh
- Photo Storage: Built-in transformations, automatic RLS protection
What Was Implemented¶
1. Backend Code Changes¶
app/identity/supabase_client.py¶
- Added user linking logic to connect internal users with Supabase auth users
- Links via
supabase_user_idcolumn for RLS to work correctly - Creates Supabase auth users after successful Twilio OTP verification
Key Function:
def create_session_for_phone(self, phone: str, user_id: str):
# Links internal user {user_id} with Supabase auth user
# Required for RLS policies to work
self.client.table("users").update({
"supabase_user_id": supabase_user_id
}).eq("id", user_id).execute()
app/api/photo_routes.py¶
- Updated to use Supabase Storage instead of S3/local storage
- All photo upload, download, and deletion operations now use Supabase Storage API
- Stores both Supabase URL and storage path in database for retrieval
Changes:
- POST /photo/upload - Uses Supabase Storage
- POST /photo/batch-upload - Uses Supabase Storage
- DELETE /photo/{photo_id} - Deletes from Supabase Storage
app/storage/supabase_storage.py (Already Existed)¶
- Storage client wrapper for Supabase Storage operations
- Handles upload, download, delete, and thumbnail generation
- No changes needed - already implemented
2. Database Migrations¶
Migration 1: Add supabase_user_id and Enable RLS¶
Created via Supabase MCP and applied to development database.
Schema Changes:
-- Add Supabase auth link column
ALTER TABLE public.users
ADD COLUMN supabase_user_id UUID REFERENCES auth.users(id);
-- Enable RLS on 11 tables
ALTER TABLE public.users ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.messages ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.conversations ENABLE ROW LEVEL SECURITY;
-- ... (8 more tables)
RLS Policies Created (30+ policies): - Users can only view their own profile - Users can only see messages from their conversations - Users can only view their own OAuth tokens - Users can only view their own relationship states - Users can only view their own usage events - And more...
Status: ✅ Applied to dev database
Migration 2: Storage RLS Policies¶
Must be created via Supabase Dashboard (SQL migration not permitted for storage tables).
Buckets Required:
- user-photos (private) - User photo storage
- user-photos-thumbnails (public) - Public thumbnails
Policies Needed:
-- Users can upload photos to their own folder
CREATE POLICY "Users can upload own photos"
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'user-photos' AND
(storage.foldername(name))[1] = (
SELECT u.id::text
FROM public.users u
WHERE u.supabase_user_id = auth.uid()
)
);
-- Similar policies for SELECT, UPDATE, DELETE on user-photos
-- Public read policy for user-photos-thumbnails
Status: ⏳ Needs to be created via Supabase Dashboard
3. Documentation¶
Created comprehensive documentation for team:
- docs/implementation/SUPABASE_RLS_AND_STORAGE.md (Technical Docs)
- Architecture overview
- RLS policy explanations
- Storage bucket setup
- Cost analysis
- Testing checklist
-
Troubleshooting guide
-
docs/integration/FRONTEND_SUPABASE_INTEGRATION.md (Frontend Guide)
- Authentication flow changes
- API endpoint changes (Authorization header required)
- Complete React/Next.js code examples
- Migration strategy from old to new auth
-
Login flow implementation with Zustand store
-
docs/integration/MAC_MINI_EDGE_CLIENT_SUPABASE.md (Edge Client Guide)
- Updated relay code with Authorization header
- Environment variable setup
- Webhook secret authentication
-
Troubleshooting guide
-
tests/test_rls_policies.py (RLS Test Suite)
- Tests for database RLS policies
- Tests for storage RLS policies
- Cross-user data leakage tests
4. Environment Configuration¶
.env.example¶
Added Supabase configuration section:
# Supabase project URL (get from Supabase Dashboard → Settings → API)
SUPABASE_URL=https://your-project.supabase.co
# Supabase anonymous key (public, safe for frontend)
SUPABASE_ANON_KEY=your_supabase_anon_key_here
# Supabase service role key (secret, backend only - bypasses RLS)
SUPABASE_SERVICE_KEY=your_supabase_service_key_here
Environment Status¶
Development Environment ✅ COMPLETE¶
Supabase Project: archety-dev Project URL: https://atqvbecetywpppbdkdhp.supabase.co Railway Environment: development Git Branch: dev Deployment Status: ✅ Successfully deployed and running
Configuration: - ✅ Supabase environment variables set in Railway dev - ✅ DATABASE_URL updated to new dev Supabase database - ✅ Database migrations applied (RLS enabled on all tables) - ✅ Backend code deployed and running - ✅ Health checks passing
Testing Status: - ✅ Backend startup successful - ✅ mem0 connection working - ✅ All services initialized - ⏳ Storage buckets need to be created via Supabase Dashboard - ⏳ Storage RLS policies need to be applied via Supabase Dashboard
Production Environment ⏳ READY FOR DEPLOYMENT¶
Supabase Project: archety-prod (existing) Project URL: https://tinckcednwwyruzdjfex.supabase.co Railway Environment: production Git Branch: master Deployment Status: ⏳ Not deployed yet (running old code)
Branch Status: - ✅ Master branch has all Supabase changes (commit c0f2085) - ✅ Master and dev branches are aligned - ⏳ Production Railway deployment needs to be triggered manually
Next Steps for Production:
1. Apply database migrations to production Supabase (same as dev)
2. Create storage buckets in production Supabase Dashboard
3. Apply storage RLS policies in production Supabase Dashboard
4. Trigger production deployment via GitHub Actions:
- Option A: Manual workflow dispatch (type "DEPLOY TO PRODUCTION")
- Option B: Create version tag (e.g., git tag v3.6.0 && git push origin v3.6.0)
Git Status¶
Branch Alignment ✅¶
Both master and dev branches are aligned at commit c0f2085:
* c0f2085 (HEAD -> dev, origin/master, origin/dev, master)
feat: Implement Supabase RLS and Storage integration
Commit Message:
feat: Implement Supabase RLS and Storage integration
Implements Phase 3.6 - Supabase Authentication System with Row Level Security and Supabase Storage for photos.
Files Changed:
- app/identity/supabase_client.py (modified)
- app/api/photo_routes.py (modified)
- app/storage/supabase_storage.py (new)
- .env.example (modified)
- docs/implementation/SUPABASE_RLS_AND_STORAGE.md (new)
- docs/integration/FRONTEND_SUPABASE_INTEGRATION.md (new)
- tests/test_rls_policies.py (new)
Deployment Status¶
| Environment | Branch | Status | URL |
|---|---|---|---|
| Development | dev |
✅ Deployed | https://archety-backend-dev.up.railway.app |
| Production | master |
⏳ Not deployed | https://archety-backend-prod.up.railway.app |
Manual Steps Required¶
1. Supabase Dashboard Configuration (Both Environments)¶
Create Storage Buckets¶
For Development (https://atqvbecetywpppbdkdhp.supabase.co):
1. Go to Supabase Dashboard → Storage
2. Create bucket: user-photos (PRIVATE)
3. Create bucket: user-photos-thumbnails (PUBLIC)
For Production (https://tinckcednwwyruzdjfex.supabase.co):
1. Go to Supabase Dashboard → Storage
2. Create bucket: user-photos (PRIVATE)
3. Create bucket: user-photos-thumbnails (PUBLIC)
Apply Storage RLS Policies¶
For Both Environments:
Go to SQL Editor in Supabase Dashboard and run:
-- Enable RLS on storage tables (if not already enabled)
ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY;
ALTER TABLE storage.buckets ENABLE ROW LEVEL SECURITY;
-- Policy: Users can upload photos to their own folder
CREATE POLICY "Users can upload own photos"
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'user-photos' AND
(storage.foldername(name))[1] = (
SELECT u.id::text
FROM public.users u
WHERE u.supabase_user_id = auth.uid()
)
);
-- Policy: Users can view their own photos
CREATE POLICY "Users can view own photos"
ON storage.objects FOR SELECT
USING (
bucket_id = 'user-photos' AND
(storage.foldername(name))[1] = (
SELECT u.id::text
FROM public.users u
WHERE u.supabase_user_id = auth.uid()
)
);
-- Policy: Users can update their own photos
CREATE POLICY "Users can update own photos"
ON storage.objects FOR UPDATE
USING (
bucket_id = 'user-photos' AND
(storage.foldername(name))[1] = (
SELECT u.id::text
FROM public.users u
WHERE u.supabase_user_id = auth.uid()
)
);
-- Policy: Users can delete their own photos
CREATE POLICY "Users can delete own photos"
ON storage.objects FOR DELETE
USING (
bucket_id = 'user-photos' AND
(storage.foldername(name))[1] = (
SELECT u.id::text
FROM public.users u
WHERE u.supabase_user_id = auth.uid()
)
);
-- Policy: Users can upload thumbnails to their own folder
CREATE POLICY "Users can upload own thumbnails"
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'user-photos-thumbnails' AND
(storage.foldername(name))[1] = (
SELECT u.id::text
FROM public.users u
WHERE u.supabase_user_id = auth.uid()
)
);
-- Policy: Anyone can view thumbnails (public bucket)
CREATE POLICY "Public can view thumbnails"
ON storage.objects FOR SELECT
USING (bucket_id = 'user-photos-thumbnails');
-- Policy: Users can delete their own thumbnails
CREATE POLICY "Users can delete own thumbnails"
ON storage.objects FOR DELETE
USING (
bucket_id = 'user-photos-thumbnails' AND
(storage.foldername(name))[1] = (
SELECT u.id::text
FROM public.users u
WHERE u.supabase_user_id = auth.uid()
)
);
2. Apply Database Migrations to Production¶
Connect to production Supabase and apply the RLS migration (same as applied to dev).
Using Supabase MCP (recommended):
# Connect MCP to production project
# Then apply migration: add_supabase_user_id_and_rls_policies
# Migration adds:
# - supabase_user_id column to users table
# - Enables RLS on 11 tables
# - Creates 30+ RLS policies
Or manually via SQL Editor:
See the full migration SQL in supabase_migration.sql
3. Deploy to Production Railway¶
Option A: Manual GitHub Actions Workflow
1. Go to GitHub → Actions → "Deploy to Production"
2. Click "Run workflow"
3. Type DEPLOY TO PRODUCTION (exactly)
4. Click "Run workflow"
5. Wait for deployment to complete (~3-5 minutes)
6. Verify health: https://archety-backend-prod.up.railway.app/health
Option B: Version Tag (Recommended)
# Create version tag for Phase 3.6
git tag v3.6.0 -m "Phase 3.6: Supabase RLS and Storage implementation"
git push origin v3.6.0
# This automatically triggers production deployment
# Monitor progress in GitHub Actions
Testing Checklist¶
Development Environment¶
- Backend deployment successful
- Health endpoint responding (200 OK)
- Database connection working
- mem0 connection working
- All services initialized
- Storage buckets created
- Storage RLS policies applied
- Photo upload test (after storage setup)
- Photo download test (after storage setup)
- RLS policy tests passing
Production Environment¶
- Database migrations applied
- Storage buckets created
- Storage RLS policies applied
- Backend deployment triggered
- Health endpoint responding (200 OK)
- Database connection working
- Photo upload test (with real user)
- Photo download test (with real user)
- Cross-user data leakage test (verify RLS)
- Frontend integration test
- Mac mini relay integration test
Breaking Changes & Migration Guide¶
For Frontend Team (Vercel)¶
Breaking Change: Authentication now requires JWT tokens in Authorization header.
Before:
// Old: Query parameter authentication
const response = await fetch(`/api/user/profile?phone=${phone}`);
After:
// New: Bearer token authentication
const response = await fetch('/api/user/profile', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
Full Integration Guide: See docs/integration/FRONTEND_SUPABASE_INTEGRATION.md
For Mac Mini Edge Client¶
Breaking Change: Orchestrator endpoint now requires webhook secret authentication.
Before:
# Old: No authentication
response = requests.post(
"https://archety-backend-prod.up.railway.app/orchestrator/message",
json=message_data
)
After:
# New: Webhook secret in Authorization header
response = requests.post(
"https://archety-backend-prod.up.railway.app/orchestrator/message",
headers={
"Authorization": f"Bearer {RELAY_WEBHOOK_SECRET}"
},
json=message_data
)
Full Integration Guide: See docs/integration/MAC_MINI_EDGE_CLIENT_SUPABASE.md
Webhook Secret: Already configured in Railway as RELAY_WEBHOOK_SECRET
Rollback Plan¶
If issues arise in production:
Quick Rollback (Revert Backend Code)¶
# Revert to previous commit
git revert c0f2085
# Push revert
git push origin master
# Trigger production deployment with reverted code
Disable RLS (Emergency Only)¶
-- Temporarily disable RLS on all tables (emergency only!)
ALTER TABLE public.users DISABLE ROW LEVEL SECURITY;
ALTER TABLE public.messages DISABLE ROW LEVEL SECURITY;
-- ... (repeat for all 11 tables)
⚠️ Warning: This removes database-level security. Only use in emergency.
Full Rollback Steps¶
- Revert backend code (as above)
- Remove Supabase environment variables from Railway prod
- Restore old S3/local storage if needed
- Notify frontend team to revert to old auth
- Notify Mac mini team to remove Authorization header
Support & Resources¶
Documentation¶
- Technical Implementation:
docs/implementation/SUPABASE_RLS_AND_STORAGE.md - Frontend Integration:
docs/integration/FRONTEND_SUPABASE_INTEGRATION.md - Mac Mini Integration:
docs/integration/MAC_MINI_EDGE_CLIENT_SUPABASE.md - Deployment Guide:
docs/DEPLOYMENT.md - Railway Setup:
docs/RAILWAY_SETUP.md
Supabase Resources¶
- Dashboard: https://supabase.com/dashboard
- Docs: https://supabase.com/docs
- RLS Guide: https://supabase.com/docs/guides/auth/row-level-security
- Storage Guide: https://supabase.com/docs/guides/storage
Team Contacts¶
- Backend Engineer: Implemented Supabase integration (Engineer 2)
- Frontend Engineer: Needs to update auth flow (Vercel deployment)
- Mac Mini Owner: Needs to update relay authentication
Timeline¶
| Date | Milestone |
|---|---|
| November 13, 2025 | Implementation complete, dev environment deployed |
| TBD | Storage buckets created in dev/prod Supabase |
| TBD | Storage RLS policies applied in dev/prod Supabase |
| TBD | Production deployment triggered |
| TBD | Frontend integration complete |
| TBD | Mac mini relay updated |
| TBD | End-to-end testing complete |
Next Steps (Priority Order)¶
- ✅ DONE: Implement backend code changes
- ✅ DONE: Apply database migrations to dev
- ✅ DONE: Deploy to dev environment
- ✅ DONE: Align master and dev branches
- ⏳ TODO: Create storage buckets in dev Supabase Dashboard
- ⏳ TODO: Apply storage RLS policies in dev Supabase Dashboard
- ⏳ TODO: Test photo upload/download in dev
- ⏳ TODO: Apply database migrations to prod Supabase
- ⏳ TODO: Create storage buckets in prod Supabase Dashboard
- ⏳ TODO: Apply storage RLS policies in prod Supabase Dashboard
- ⏳ TODO: Deploy to production Railway (manual trigger)
- ⏳ TODO: Notify frontend team to update auth
- ⏳ TODO: Notify Mac mini owner to update relay
- ⏳ TODO: Run end-to-end tests in production
Implementation Status: ✅ Code Complete, ⏳ Deployment Pending Last Updated: November 13, 2025 Version: Phase 3.6