.env file in the repository root or apps/api/.env.
Core API Configuration
Public base URL where the API is accessible.Examples:
- Local development:
http://localhost:8080 - Production:
https://api.yourdomain.com
Port the API server listens on.
PostgreSQL connection string.Format:
postgres://user:password@host:port/databaseExamples:The database must exist before running migrations. Ensure PostgreSQL user has CREATE, ALTER, and DROP permissions.
Secret key for signing access tokens. Minimum 16 characters.Generate a secure secret:
Secret key for signing refresh tokens. Minimum 16 characters. Must be different from
JWT_SECRET.Generate a secure secret:Access Policy Configuration
Email domain restriction for user authentication. Only emails ending with this domain can authenticate.Examples:Legacy compatibility:
The
@ prefix is optional. The API normalizes it automatically: yourcompany.com → @yourcompany.comRIPSEED_EMAIL_DOMAIN is accepted as a fallback.Slack workspace team ID. Only users from this workspace can authenticate.Format: Starts with Legacy compatibility:
T (e.g., T01234567AB)Find your team ID in the Slack URL:
https://app.slack.com/client/T01234567AB/...RIPSEED_SLACK_TEAM_ID is accepted as a fallback.Slack OAuth Configuration
OAuth client ID from your Slack app configuration.Found in Slack App → Basic Information → App Credentials.
OAuth client secret from your Slack app configuration.Found in Slack App → Basic Information → App Credentials.
OAuth callback URL that Slack redirects to after authentication.Must exactly match the redirect URL configured in your Slack app.Examples:
The path must be
/v1/auth/slack/callback. Only the base URL changes between environments.Cloudflare Configuration
Cloudflare account identifier.Found in Cloudflare Dashboard → Your Domain → API section (right sidebar).
Cloudflare zone (domain) identifier.Found in Cloudflare Dashboard → Your Domain → API section (right sidebar).
API token with Tunnel and DNS edit permissions.Required permissions:
- Zone → DNS → Edit
- Account → Cloudflare Tunnel → Edit
Base domain for tunnel hostnames.All tunnels will be created as subdomains: With
<slug>.<CLOUDFLARE_BASE_DOMAIN>Examples:CLOUDFLARE_BASE_DOMAIN=tunnel.yourdomain.com, a tunnel with slug my-app becomes:This domain must be managed by the Cloudflare zone specified in
CLOUDFLARE_ZONE_ID.Behavior Controls
Access token time-to-live in minutes.Shorter TTL = better security, more frequent refresh token usage.
Refresh token time-to-live in days.Users must re-authenticate after this period.
Maximum number of concurrent active tunnels per user.
This is enforced server-side. Users attempting to create more tunnels will receive a 409 Conflict error.
How often (in seconds) CLI clients must send heartbeats to keep tunnels alive.If a client stops heartbeating, the tunnel lease expires after
LEASE_TIMEOUT_SEC.Seconds without a heartbeat before a tunnel lease is considered expired.
Should be significantly larger than
HEARTBEAT_INTERVAL_SEC to account for network delays.How often (in seconds) the cleanup worker checks for expired leases.Expired tunnels are automatically stopped and DNS records are removed.
CLI Configuration
Client-side variable for the CLI. Points to the API base URL.Set globally for CLI usage:Or use the
--domain flag:The CLI also saves this to
~/.rs-tunnel/config.json after first use.Legacy alias for
RS_TUNNEL_API_URL. Still supported for backward compatibility.Prefer using RS_TUNNEL_API_URL instead.Example Configuration Files
Local Development
.env
Production
.env
Environment Variable Validation
The API validates all environment variables at startup using Zod schemas (seeapps/api/src/config/env.ts:16).
Validation rules:
API_BASE_URL: Must be a valid URLDATABASE_URL: Required, non-empty stringJWT_SECRETandREFRESH_TOKEN_SECRET: Minimum 16 characters eachPORT: Positive integer (default: 8080)SLACK_REDIRECT_URI: Must be a valid URL- Email domain normalization: Automatically adds
@prefix if missing
Troubleshooting
Database connection error: 'client password must be a string'
Database connection error: 'client password must be a string'
Cause:
DATABASE_URL is not loaded or incomplete.Solution:- Verify
.envfile exists in repo root orapps/api/.env - Check
DATABASE_URLformat:postgres://user:password@host:port/database - Ensure no extra whitespace or quotes around the value
Slack OAuth callback fails
Slack OAuth callback fails
Cause:
SLACK_REDIRECT_URI doesn’t match Slack app configuration.Solution:- Go to Slack app settings → OAuth & Permissions
- Verify redirect URL exactly matches
SLACK_REDIRECT_URI - Include protocol (
http://orhttps://) and path (/v1/auth/slack/callback)
JWT validation error: 'JWT_SECRET must be at least 16 characters'
JWT validation error: 'JWT_SECRET must be at least 16 characters'
Cause:
JWT_SECRET or REFRESH_TOKEN_SECRET is too short.Solution:
Generate new secrets with at least 16 characters:User authentication fails with 'Email domain not allowed'
User authentication fails with 'Email domain not allowed'
Cause: User’s email doesn’t match
ALLOWED_EMAIL_DOMAIN.Solution:- Verify
ALLOWED_EMAIL_DOMAINis set correctly (e.g.,@yourcompany.com) - Ensure user email ends with this domain
- Check for typos in the domain name
Next Steps
Docker Setup
Configure Docker Compose for PostgreSQL and API
Database Migration
Run Drizzle ORM migrations to set up the schema

