Skip to main content

Environment File Setup

JOIP uses environment variables for configuration. All variables are loaded from a .env file in the project root.
1

Create Environment File

cp .env.example .env
2

Edit Configuration

Open .env in your editor and configure the required variables below.
3

Restart Server

npm run dev
Changes to .env require a server restart.
Never commit .env files to version control. The .gitignore file should already exclude them.

Core Configuration

Database Connection

DATABASE_URL
string
required
PostgreSQL connection string in URI format.
DATABASE_URL="postgresql://username:password@host:5432/database"
Examples:
  • Local: postgresql://postgres:password@localhost:5432/joip_dev
  • Supabase: postgresql://postgres:[password]@db.[project].supabase.co:5432/postgres
  • Neon: postgresql://[user]:[password]@[host].neon.tech/[database]
For production, enable SSL by appending ?sslmode=require to the connection string.

Session Management

SESSION_SECRET
string
required
Secret key for signing session cookies. Must be a strong random string.
SESSION_SECRET="your_session_secret_key"
Generate securely:
openssl rand -hex 32
Use a different secret for development and production. Changing this will invalidate all existing sessions.

Application Settings

NODE_ENV
string
default:"development"
Application environment mode.
NODE_ENV="development"  # or "production"
Effects:
  • development: Enables Vite HMR, verbose logging, local auth
  • production: Serves from dist/, minified assets, Replit OIDC
PORT
number
default:5000
Server port number.
PORT=5000
Replit automatically proxies this port. The default is always 5000.
JOIP_INTERNAL_PORT
number
Optional override for internal localhost callbacks/proxy calls.
# JOIP_INTERNAL_PORT=5000
Defaults to PORT when omitted. Only needed for special reverse proxy setups.
UPLOAD_DIR
string
default:"./uploads"
Directory for temporary file uploads.
UPLOAD_DIR="./uploads"
Deprecated: JOIP now uses Supabase Storage exclusively. This setting is retained for backward compatibility but not actively used.

Reddit Integration

Required for Reddit-based sessions, Gaslighter, and Scroller features.
REDDIT_CLIENT_ID
string
required
Reddit application client ID.
REDDIT_CLIENT_ID="your_reddit_client_id"
How to get:
  1. Go to https://www.reddit.com/prefs/apps
  2. Click “Create App” or “Create Another App”
  3. Choose “script” type
  4. The client ID is shown under the app name
REDDIT_CLIENT_SECRET
string
required
Reddit application client secret.
REDDIT_CLIENT_SECRET="your_reddit_client_secret"
Found in the same location as the client ID.

AI Services

Caption Generation

Choose one or both providers for AI caption generation:
OPENROUTER_API_KEY
string
OpenRouter API key for AI caption generation (recommended).
OPENROUTER_API_KEY="sk-or-v1-your_openrouter_api_key"
Features:
  • Access to Gemini 2.5 Pro and Flash models
  • Automatic fallback between models
  • Used for contextual/narrative captions in Manual Editor
  • Used for Smart Captions with themed prompts
Get your key:
  1. Sign up at https://openrouter.ai
  2. Navigate to Settings → API Keys
  3. Create a new key
Models used:
  • Manual Editor: google/gemini-2.5-pro (fallback: gemini-2.5-flash-lite)
  • Smart Captions: gemini-2.5-flash-lite
OPENAI_API_KEY
string
OpenAI API key as alternative to OpenRouter.
OPENAI_API_KEY="sk-your_openai_api_key"
The system will use OpenRouter if available, falling back to OpenAI.

AI Image Services

XAI_API_KEY
string
xAI API key for AI Undress feature (primary provider).
XAI_API_KEY="xai-your_xai_api_key"
Get your key:Model:
  • Uses grok-imagine-image-pro by default
  • Override with XAI_UNDRESS_MODEL
XAI_UNDRESS_MODEL
string
default:"grok-imagine-image-pro"
Optional xAI model override for AI Undress.
XAI_UNDRESS_MODEL="grok-imagine-image-pro"
FREEPIK_API_KEY
string
Freepik API key for AI Undress feature (fallback provider).
FREEPIK_API_KEY="your_freepik_api_key"
Get your key:Model:
  • Uses Seedream 4.5 Edit
  • Automatically used when xAI fails or is unavailable
UNDRESS_PRIMARY_PROVIDER
string
default:"xai"
Select primary provider for AI Undress.
UNDRESS_PRIMARY_PROVIDER="xai"  # or "freepik"
Options:
  • xai: Use xAI as primary, Freepik as fallback
  • freepik: Use Freepik as primary, xAI as fallback
REPLICATE_API_TOKEN
string
Replicate API token (dormant/legacy provider).
REPLICATE_API_TOKEN="r8_your_replicate_api_token"
This provider is dormant and kept for backward compatibility. The system uses xAI and Freepik.

Cloud Storage

Required for manual sessions, Media Vault, and file uploads.
SUPABASE_URL
string
required
Supabase project URL.
SUPABASE_URL="https://your-project.supabase.co"
Get from:
  1. Supabase Dashboard
  2. Select your project
  3. Settings → API → Project URL
SUPABASE_ANON_KEY
string
required
Supabase anonymous (public) key.
SUPABASE_ANON_KEY="your_supabase_anon_key"
Found in the same location as the project URL under “Project API keys” → “anon public”.
This key is safe to use in client-side code. It respects Row Level Security (RLS) policies.
SUPABASE_SERVICE_KEY
string
required
Supabase service role (admin) key.
SUPABASE_SERVICE_KEY="your_supabase_service_key"
Found under “Project API keys” → “service_role”.
Never expose this key client-side. It bypasses all RLS policies and has full admin access.

Storage Buckets

The application automatically creates these buckets:
  • user-media: User uploads and manual session media
    • Path structure: users/{userId}/manual-sessions/{sessionId}/
  • general: Community content and shared media
Test storage connectivity with:
curl http://localhost:5000/api/storage/status

External Services

Imgchest Integration

IMGCHEST_API_KEY
string
Imgchest API key for importing existing galleries.
IMGCHEST_API_KEY="your_imgchest_api_key"
Required only for the “Import JOIP” feature to import from Imgchest.

Replit Deployment

These variables are automatically set by Replit. Only configure manually for custom deployments.
REPLIT_DOMAINS
string
Replit domain for the deployed app.
REPLIT_DOMAINS="your-repl-domain.replit.dev"
Effects:
  • When set: Enables Replit OIDC authentication
  • When unset: Uses local development auth strategy
REPL_ID
string
Unique Replit project identifier.
REPL_ID="your_repl_id"
Used for Replit OIDC configuration.
ISSUER_URL
string
default:"https://identity.util.repl.co"
OIDC issuer URL for Replit authentication.
ISSUER_URL="https://identity.util.repl.co"
REPLIT_DEV_DOMAIN
string
Development domain (auto-populated by Replit).
# REPLIT_DEV_DOMAIN="your-dev-domain.replit.dev"
REPLIT_DB_URL
string
Replit database URL (auto-populated).
# REPLIT_DB_URL="your-replit-db-url"

Payment Integrations

Telegram Payments

TELEGRAM_BOT_TOKEN
string
Telegram bot token for Telegram Stars payments.
TELEGRAM_BOT_TOKEN="your_telegram_bot_token"
Required together with TELEGRAM_WEBHOOK_SECRET to enable Telegram payment flow.
TELEGRAM_WEBHOOK_SECRET
string
Telegram webhook secret for payment verification.
TELEGRAM_WEBHOOK_SECRET="your_telegram_webhook_secret"
TELEGRAM_BOT_USERNAME
string
default:"OfficialJoipBot"
Telegram bot username.
TELEGRAM_BOT_USERNAME="OfficialJoipBot"
Defaults to “OfficialJoipBot” when unset.
TELEGRAM_CHANNEL_ID
string
Telegram channel ID for membership features.
TELEGRAM_CHANNEL_ID="-1001234567890"
Only needed for channel invite/membership features.

Thirdweb Checkout

THIRDWEB_CLIENT_ID
string
Thirdweb client ID for crypto/card payments.
THIRDWEB_CLIENT_ID="your_thirdweb_client_id"
Required together with webhook secret to enable Thirdweb checkout flow.
THIRDWEB_WEBHOOK_SECRET
string
Thirdweb webhook secret for payment verification.
THIRDWEB_WEBHOOK_SECRET="your_thirdweb_webhook_secret"
THIRDWEB_SELLER_WALLET
string
Ethereum wallet address for receiving payments.
THIRDWEB_SELLER_WALLET="0xyour_seller_wallet_address"

Database Tuning

Optional connection pool and performance settings.
# Maximum pool size
DB_POOL_MAX=20

# Minimum pool size  
DB_POOL_MIN=2

# Idle timeout in milliseconds
DB_POOL_IDLE=10000

# Connection timeout in seconds
DB_CONNECT_TIMEOUT=10

# Max connection lifetime in seconds
DB_MAX_LIFETIME=3600
These are optional. The application uses sensible defaults optimized for most deployments.

Logging Configuration

LOG_LEVEL
string
default:"info"
Application logging verbosity.
LOG_LEVEL="info"
Options:
  • error: Only errors
  • warn: Warnings and errors
  • info: General information (recommended)
  • debug: Detailed debugging information

Complete Example

# Database
DATABASE_URL="postgresql://postgres:password@localhost:5432/joip_dev"

# Session
SESSION_SECRET="generated-secure-random-string-here"

# Application
NODE_ENV="development"
PORT=5000

# Reddit
REDDIT_CLIENT_ID="your_reddit_client_id"
REDDIT_CLIENT_SECRET="your_reddit_client_secret"

# AI - Caption Generation
OPENROUTER_API_KEY="sk-or-v1-your_openrouter_key"

# AI - Image Services
XAI_API_KEY="xai-your_key"
FREEPIK_API_KEY="your_freepik_key"
UNDRESS_PRIMARY_PROVIDER="xai"

# Cloud Storage
SUPABASE_URL="https://yourproject.supabase.co"
SUPABASE_ANON_KEY="your_anon_key"
SUPABASE_SERVICE_KEY="your_service_key"

# Optional
IMGCHEST_API_KEY="your_imgchest_key"

Validation

The application validates environment configuration on startup via server/environmentConfig.ts. Startup checks:
  • Required variables are present
  • Database connection succeeds
  • Connection pool initializes
  • Supabase connectivity (if configured)
Validation errors:
✗ DATABASE_URL must be set
✗ SESSION_SECRET must be set
✗ Supabase configuration incomplete

Diagnostics

Check Storage Status

curl http://localhost:5000/api/storage/status
Response:
{
  "configured": true,
  "reachable": true,
  "buckets": ["user-media", "general"],
  "testPassed": true
}
Error codes:
  • STORAGE_CONFIG_ERROR: Missing environment variables
  • STORAGE_UNREACHABLE: Cannot connect to Supabase
  • STORAGE_PREFLIGHT_FAILED: Bucket test upload failed

Check Database Connection

Verify the database is accessible:
psql "$DATABASE_URL" -c "SELECT version();"

Test API Keys

Create a Reddit session and verify media loads. Check server logs for:
✓ Reddit API request successful
✓ Fetched 100 posts from r/subreddit
Try generating a caption in Smart Captions. Look for:
✓ OpenRouter request successful
✓ Generated caption with gemini-2.5-flash-lite
Upload an image in Media Vault. Verify:
✓ File uploaded to user-media bucket
✓ Public URL generated

Security Best Practices

1

Secret Management

  • Never commit .env files to Git
  • Use different secrets for dev/staging/production
  • Rotate API keys regularly (quarterly recommended)
  • Store production secrets in secure vaults (Replit Secrets, AWS Secrets Manager)
2

API Key Security

  • Never expose service keys client-side
  • Use SUPABASE_ANON_KEY for client operations only
  • Keep SUPABASE_SERVICE_KEY server-side only
  • Monitor API usage for anomalies
3

Database Security

  • Use SSL for production database connections
  • Implement IP whitelisting where possible
  • Use strong passwords (20+ characters)
  • Enable connection pooling with max limits
4

Session Security

  • Use strong SESSION_SECRET (32+ bytes)
  • Enable secure cookies in production (HTTPS only)
  • Set appropriate session timeouts
  • Implement CSRF protection

Troubleshooting

Symptom: Manual session creation returns 503 with error codesSolutions:
  1. Verify all three Supabase variables are set
  2. Check if Supabase project is paused (resume it)
  3. Test with /api/storage/status
  4. Review error code:
    • STORAGE_CONFIG_ERROR: Add missing env vars
    • STORAGE_UNREACHABLE: Check URL and network
    • STORAGE_PREFLIGHT_FAILED: Verify bucket permissions
Symptom: Caption generation fails silentlySolutions:
  1. Check API keys are valid (not expired)
  2. Verify sufficient credits/quota on provider
  3. Check server logs for specific errors
  4. Test fallback: if OpenRouter fails, try OpenAI
Symptom: 429 errors when fetching Reddit contentSolutions:
  1. Built-in retry logic should handle this automatically
  2. Reduce concurrent requests
  3. Check if Reddit credentials are valid
  4. Wait for rate limit to reset (typically 1 minute)
Symptom: Cannot connect to databaseSolutions:
  1. Verify DATABASE_URL format is correct
  2. Check database is running and accessible
  3. For cloud databases: verify IP whitelist
  4. Test connection: psql "$DATABASE_URL"
  5. Check connection pool settings if hitting limits

Next Steps

Quick Start Guide

Create your first session with AI captions

API Reference

Explore all available API endpoints

Build docs developers (and LLMs) love