Deployment Guide

Complete guide to deploy Monaime on a new server from scratch. Supabase + VPS + Caddy + PM2.

1 Requirements

Server (VPS)

  • Ubuntu 22+ or Debian 12+
  • Node.js 20+ with npm
  • PM2 process manager
  • Caddy web server (auto TLS)
  • Min 1 GB RAM, 10 GB disk

External Services

  • Supabase project (PostgreSQL + Auth)
  • OpenAI API key (GPT-4o access)
  • Domain name with DNS A record
  • Fixer.io API key (optional, for currency rates)
  • Apple Developer account (for iOS push)

Local Development

  • Node.js 20+
  • Xcode 15+ (for iOS)
  • rsync (for deploy)
  • sshpass (optional, for scripted deploy)

Supabase Setup

  • Enable Email Auth provider
  • Enable pgvector extension
  • Get: Project URL, Anon Key, Service Key
  • Run schema SQL in SQL Editor

2 Step 1: Supabase Project Setup

A

Create Supabase Project

Go to supabase.com, create a new project. Note the following credentials from Settings → API:

# From Supabase Dashboard → Settings → API SUPABASE_URL=https://your-project-ref.supabase.co SUPABASE_ANON_KEY=eyJ... SUPABASE_SERVICE_KEY=eyJ... # From Supabase Dashboard → Settings → Auth → JWT Secret JWT_SECRET=your-jwt-secret-min-32-chars
B

Enable Extensions

In Supabase Dashboard → SQL Editor, run:

-- Required for conversation memory embeddings (RAG) CREATE EXTENSION IF NOT EXISTS vector;
C

Apply Database Schema

Copy the full contents of Server/sql/schema.sql into Supabase SQL Editor and run. This creates all tables, indexes, functions, triggers, and RLS policies.

The complete SQL is in Section 7 of this page.

D

Create User Sync Trigger

When a user signs up via Supabase Auth, automatically create a record in the public.users table:

-- Auto-create public.users row when auth.users signs up CREATE OR REPLACE FUNCTION handle_new_user() RETURNS TRIGGER AS $$ BEGIN INSERT INTO public.users (id, email, name) VALUES ( NEW.id, NEW.email, COALESCE(NEW.raw_user_meta_data->>'name', split_part(NEW.email, '@', 1)) ); RETURN NEW; END; $$ LANGUAGE plpgsql SECURITY DEFINER; DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users; CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION handle_new_user();

3 Step 2: Server (VPS) Setup

A

Install Dependencies

# Update system apt update && apt upgrade -y # Install Node.js 20 curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt install -y nodejs # Install PM2 globally npm install -g pm2 # Install Caddy apt install -y debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy.list apt update && apt install -y caddy
B

Create Project Directories

mkdir -p /var/www/finance-chat-backend/dist mkdir -p /var/www/financechat-web/dist
C

Create Environment File

nano /var/www/finance-chat-backend/.env

Add the following:

PORT=3004 HOST=0.0.0.0 NODE_ENV=production SUPABASE_URL=https://your-project-ref.supabase.co SUPABASE_ANON_KEY=eyJ... SUPABASE_SERVICE_KEY=eyJ... OPENAI_API_KEY=sk-... JWT_SECRET=your-jwt-secret-min-32-chars CORS_ORIGIN=https://app.yourdomain.com,https://yourdomain.com # Optional FIXER_API_KEY=your-fixer-key AI_MODEL_FAMILY=gpt-5
D

Configure Caddy (Reverse Proxy + TLS)

Edit /etc/caddy/Caddyfile:

api.yourdomain.com { reverse_proxy localhost:3004 } app.yourdomain.com { root * /var/www/financechat-web/dist file_server try_files {path} /index.html }
# Restart Caddy to apply systemctl restart caddy

Caddy automatically provisions TLS certificates via Let's Encrypt.

4 Step 3: Deploy Backend

A

Build & Deploy (from local machine)

# Build TypeScript cd Server npm install npm test -- --run npm run build # Copy compiled code + dependencies to server rsync -avz --delete dist/ user@server:/var/www/finance-chat-backend/dist/ rsync -avz package.json package-lock.json user@server:/var/www/finance-chat-backend/ # Install production dependencies on server ssh user@server "cd /var/www/finance-chat-backend && npm install --production"
B

Start with PM2

# First time: start process cd /var/www/finance-chat-backend pm2 start dist/index.js --name financechat-api pm2 save pm2 startup # Subsequent deploys: restart pm2 restart financechat-api # Check logs pm2 logs financechat-api --lines 50 --nostream

5 Step 4: Deploy Web Frontend

A

Build & Deploy

# Build React app cd Web npm install npm test -- --run npm run build # Copy dist to server rsync -avz --delete dist/ user@server:/var/www/financechat-web/dist/

No restart needed — Caddy serves static files directly.

6 Step 5: iOS App Configuration

A

Update Config.swift

In iOS/MoneyChat/Config/Config.swift, update the API URLs and Supabase credentials:

// API endpoints static let apiBaseURL = "https://api.yourdomain.com" static let wsURL = "wss://api.yourdomain.com/ws/chat" // Supabase credentials static let supabaseURL = "https://your-project-ref.supabase.co" static let supabaseAnonKey = "eyJ..."
B

Update Certificate Pinning

In iOS/MoneyChat/Services/HTTPClient.swift, update the certificate pin hash for your new domain's TLS certificate.

7 Full Database Schema (SQL)

⚠️
Schema Source of Truth The full SQL schema is maintained in Server/sql/schema.sql. Always use that file as the source of truth. Copy its contents into Supabase SQL Editor to apply.
-- Current tables (Server/sql/schema.sql): -- -- CORE TABLES: -- users - User profiles (synced from Supabase Auth) -- wallets - Bank accounts, cash, cards -- categories - Income/expense categories (includes budget fields) -- transactions - All financial operations -- contacts - Debt tracking (people who owe/are owed) -- merchants - Auto-categorization by merchant name -- recurring_templates - Subscriptions and recurring payments -- -- AI & NOTIFICATIONS: -- ai_trigger_configs - Per-user notification settings -- ai_notifications - Generated notifications (limit warnings, etc.) -- reminders - User-created reminders -- notification_milestones - Prevent duplicate notifications -- conversation_memories - RAG embeddings (pgvector) -- -- CHAT: -- chat_messages - Conversation history -- chat_feedback - User feedback on AI responses -- docs_feedback - Documentation page feedback -- -- AUTOMATION: -- automation_rules - User-defined "if X then Y" rules -- rule_applications - Tracks which rules applied to which transactions -- suggestions - AI-generated recommendations -- execution_plans - Multi-step operation plans -- -- SYSTEM: -- action_history - Undo functionality (max 50 per user) -- pending_actions - Dangerous command confirmations (with TTL) -- entity_aliases - User-defined names for wallets/categories/contacts -- user_devices - Multi-device sync -- device_tokens - Push notification tokens -- currency_rates - Exchange rate cache -- financial_health_history - Periodic financial health scores -- -- GLOBAL (no user_id): -- mcc_categories - MCC code to category mapping -- merchant_patterns - Auto-detection patterns for merchants -- -- KEY FUNCTIONS: -- update_wallet_balance() - Atomic balance update -- transfer_between_wallets() - Atomic transfer with row locking -- process_recurring_templates() - Cron: create transactions from templates -- update_merchant_usage() - Update merchant stats -- cleanup_old_action_history() - Trigger: keep max 50 per user -- delete_user_data() - GDPR: delete all user data -- -- NOTE: Budgets are stored as fields on categories table: -- budget_amount, budget_period, budget_currency -- current_spent is computed at query time (no trigger/cron needed) -- -- All tables have RLS enabled: auth.uid() = user_id -- Server uses service_role key to bypass RLS

8 Deploy Checklist

New Server Setup

Create Supabase project, get credentials
Enable pgvector extension
Run full schema SQL in Supabase SQL Editor
Create user sync trigger (auth.users → public.users)
Get OpenAI API key
Provision VPS (Ubuntu 22+)
Install Node.js 20+, PM2, Caddy
Configure DNS A records for domains
Configure Caddyfile (reverse proxy + static files)
Create .env file with all credentials
Build and deploy Server (rsync dist/ + npm install --production)
Start PM2 process (pm2 start, pm2 save, pm2 startup)
Build and deploy Web (rsync dist/)
Update iOS Config.swift with new URLs
Update iOS certificate pinning hash
Test: create user, send chat message, check WebSocket

Regular Deploy (code update)

Run tests: npm test -- --run
Build: npm run build
rsync dist/ to server
pm2 restart financechat-api
Check logs: pm2 logs --lines 20 --nostream