sojorn/sojorn_docs/deployment/SETUP.md

9 KiB

Sojorn - Setup Guide

Quick guide to get Sojorn running locally and deployed.


Prerequisites


Step 1: Environment Setup

1.1 Create Supabase Project

  1. Go to app.supabase.com
  2. Click "New Project"
  3. Choose a name (e.g., "sojorn-dev")
  4. Set a strong database password
  5. Select a region close to you
  6. Wait for project to initialize (~2 minutes)

1.2 Get Your Credentials

Once your project is ready:

  1. Go to Settings → API

  2. Copy these values:

    • Project URL (e.g., https://abcdefgh.supabase.co)
    • anon/public key (starts with eyJ...)
    • service_role key (starts with eyJ...)
  3. Go to Settings → General

    • Copy your Project Reference ID (e.g., abcdefgh)
  4. Go to Settings → Database

    • Copy your Database Password (or reset it if you forgot)

1.3 Configure .env File

Open .env in this project and fill in your values:

SUPABASE_URL=https://YOUR_PROJECT_REF.supabase.co
SUPABASE_ANON_KEY=eyJ...your_anon_key...
SUPABASE_SERVICE_ROLE_KEY=eyJ...your_service_role_key...
SUPABASE_PROJECT_REF=YOUR_PROJECT_REF
SUPABASE_DB_PASSWORD=your_database_password
CRON_SECRET=generate_with_openssl_rand
NODE_ENV=development
API_BASE_URL=https://YOUR_PROJECT_REF.supabase.co/functions/v1

1.4 Generate CRON_SECRET

In your terminal:

# macOS/Linux
openssl rand -base64 32

# Windows (PowerShell)
-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 32 | % {[char]$_})

Copy the output and paste it as your CRON_SECRET in .env.


# Login to Supabase
supabase login

# Link your local project to your Supabase project
supabase link --project-ref YOUR_PROJECT_REF

# Enter your database password when prompted

Step 3: Deploy Database

3.1 Push Migrations

# Apply all migrations to your Supabase project
supabase db push

This will create all tables, functions, and RLS policies.

3.2 Seed Categories

Connect to your database and run the seed script:

# Using psql
psql "postgresql://postgres:YOUR_DB_PASSWORD@db.YOUR_PROJECT_REF.supabase.co:5432/postgres" \
  -f supabase/seed/seed_categories.sql

# Or using Supabase SQL Editor:
# 1. Go to https://app.supabase.com/project/YOUR_PROJECT/editor
# 2. Copy contents of supabase/seed/seed_categories.sql
# 3. Paste and run

3.3 Verify Database Setup

# Check that tables were created
supabase db remote commit

Or in the Supabase Dashboard:

  • Go to Table Editor → You should see all 14 tables

Step 4: Deploy Edge Functions

4.1 Set Secrets

# Set the CRON_SECRET for the harmony calculation function
supabase secrets set CRON_SECRET="your_cron_secret_from_env"

4.2 Deploy All Functions

# Deploy each function
supabase functions deploy publish-post
supabase functions deploy publish-comment
supabase functions deploy block
supabase functions deploy report
supabase functions deploy feed-personal
supabase functions deploy feed-sojorn
supabase functions deploy trending
supabase functions deploy calculate-harmony

Or deploy all at once (if you have a deployment script).

4.3 Verify Functions

Go to Edge Functions in your Supabase dashboard:

  • You should see 8 functions listed
  • Check logs to ensure no deployment errors

Step 5: Test the API

5.1 Create a Test User

  1. Go to Authentication → Users in Supabase Dashboard
  2. Click "Add User"
  3. Enter email and password
  4. Copy the User ID (UUID)

5.2 Manually Create Profile

In SQL Editor, run:

-- Replace USER_ID with the UUID from step 5.1
INSERT INTO profiles (id, handle, display_name, bio)
VALUES ('USER_ID', 'testuser', 'Test User', 'Testing Sojorn');

5.3 Get a JWT Token

  1. Use the Supabase Auth API:
curl -X POST "https://YOUR_PROJECT_REF.supabase.co/auth/v1/token?grant_type=password" \
  -H "apikey: YOUR_ANON_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "your_password"
  }'
  1. Copy the access_token from the response

5.4 Test Edge Functions

# Set your token
export TOKEN="your_access_token_here"

# Get a category ID (from seed data)
# Go to Table Editor → categories → Copy the 'general' category UUID

# Test publishing a post
curl -X POST "https://YOUR_PROJECT_REF.supabase.co/functions/v1/publish-post" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "category_id": "CATEGORY_UUID",
    "body": "This is my first friendly post on Sojorn."
  }'

# Test getting personal feed
curl "https://YOUR_PROJECT_REF.supabase.co/functions/v1/feed-personal" \
  -H "Authorization: Bearer $TOKEN"

# Test Sojorn feed
curl "https://YOUR_PROJECT_REF.supabase.co/functions/v1/feed-sojorn?limit=10" \
  -H "Authorization: Bearer $TOKEN"

Step 6: Schedule Harmony Calculation

Create .github/workflows/harmony-cron.yml:

name: Calculate Harmony Daily

on:
  schedule:
    - cron: '0 2 * * *'  # 2 AM UTC daily
  workflow_dispatch:

jobs:
  calculate:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger harmony calculation
        run: |
          curl -X POST \
            https://${{ secrets.SUPABASE_PROJECT_REF }}.supabase.co/functions/v1/calculate-harmony \
            -H "Authorization: Bearer ${{ secrets.CRON_SECRET }}"          

Add secrets in GitHub:

  • SUPABASE_PROJECT_REF
  • CRON_SECRET

Option 2: Cron-Job.org (External service)

  1. Go to cron-job.org and create account
  2. Create new cron job:
    • URL: https://YOUR_PROJECT_REF.supabase.co/functions/v1/calculate-harmony
    • Schedule: Daily at 2 AM
    • Method: POST
    • Header: Authorization: Bearer YOUR_CRON_SECRET

Step 7: Verify Everything Works

7.1 Check Tables

In Table Editor, verify:

  • profiles has your test user
  • categories has 12 categories
  • trust_state has your user (with harmony_score = 50)
  • posts has any posts you created

7.2 Check RLS Policies

In SQL Editor, test block enforcement:

-- Create a second test user
INSERT INTO auth.users (id, email) VALUES (gen_random_uuid(), 'test2@example.com');
INSERT INTO profiles (id, handle, display_name)
VALUES ((SELECT id FROM auth.users WHERE email = 'test2@example.com'), 'testuser2', 'Test User 2');

-- Block user 2 from user 1
INSERT INTO blocks (blocker_id, blocked_id)
VALUES (
  (SELECT id FROM profiles WHERE handle = 'testuser'),
  (SELECT id FROM profiles WHERE handle = 'testuser2')
);

-- Verify user 1 cannot see user 2's profile
SET request.jwt.claims TO '{"sub": "USER_1_ID"}';
SELECT * FROM profiles WHERE handle = 'testuser2';  -- Should return 0 rows

-- Reset
RESET request.jwt.claims;

7.3 Test Tone Detection

Try publishing a hostile post:

curl -X POST "https://YOUR_PROJECT_REF.supabase.co/functions/v1/publish-post" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "category_id": "CATEGORY_UUID",
    "body": "This is fucking bullshit."
  }'

Expected response:

{
  "error": "Content rejected",
  "message": "This post contains language that does not fit here.",
  "suggestion": "This space works without profanity. Try rephrasing."
}

Troubleshooting

"Relation does not exist" error

  • Run supabase db push again
  • Check that migrations completed successfully

"JWT expired" error

  • Your auth token expired (tokens last 1 hour)
  • Sign in again to get a new token

"Failed to fetch" error

  • Check your SUPABASE_URL is correct
  • Verify Edge Functions are deployed
  • Check function logs: supabase functions logs FUNCTION_NAME

RLS policies blocking everything

  • Ensure you're using the correct user ID in JWT
  • Check that user exists in profiles table
  • Verify RLS policies with \d+ TABLE_NAME in psql

Next Steps

Now that your backend is running:

  1. Build missing Edge Functions (signup, follow, like, etc.)
  2. Start Flutter client (see Flutter setup guide when created)
  3. Write transparency pages (How Reach Works, Rules)
  4. Add admin tooling (report review, trending overrides)

Useful Commands

# View Edge Function logs
supabase functions logs publish-post --tail

# Reset database (WARNING: deletes all data)
supabase db reset

# Check Supabase status
supabase status

# View remote database changes
supabase db remote commit

# Generate TypeScript types from database
supabase gen types typescript --local > types/database.ts

Support