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