Configuration Reference
Backend Environment Variables
Set these in docker-compose.yml under the backend service, or in a .env file.
| Variable | Default | Description |
|---|---|---|
PORT | 3001 | Backend API port |
DATABASE_URL | — | PostgreSQL connection string (e.g., postgresql://tandemu:tandemu@postgres:5432/tandemu) |
REDIS_URL | — | Redis connection string (e.g., redis://redis:6379) |
CLICKHOUSE_URL | — | ClickHouse HTTP URL (e.g., http://clickhouse:8123) |
JWT_SECRET | change-me-in-production | Secret for signing JWT tokens. Change this in production. |
CORS_ORIGIN | http://localhost:3000 | Allowed CORS origin for the frontend |
STRIPE_SECRET_KEY | — | Stripe secret key (SaaS billing, optional) |
STRIPE_WEBHOOK_SECRET | — | Stripe webhook signing secret (optional) |
OPENAI_API_KEY | — | OpenAI API key for memory embeddings. Required for memory search to work. |
APP_URL | http://localhost:3001 | Backend URL used for redirects and callbacks |
FRONTEND_URL | http://localhost:3000 | Frontend URL used for email links and redirects |
ENCRYPTION_KEY | — | AES-256 key for encrypting integration API tokens at rest |
WATCHTOWER_API_URL | http://watchtower:8080 | Watchtower HTTP API URL for triggering updates from the dashboard |
WATCHTOWER_API_TOKEN | tandemu-update | Bearer token for Watchtower HTTP API. Change this in production. |
Frontend Environment Variables
| Variable | Default | Description |
|---|---|---|
NEXT_PUBLIC_API_URL | http://localhost:3001 | Backend API URL (accessed from the browser) |
OTel Collector Configuration
The collector config is at otel-collector.yaml in the repo root. It defines:
Receivers:
- OTLP gRPC on port 4317
- OTLP HTTP on port 4318
Processors:
memory_limiter— Prevents OOM (512 MiB limit)batch— Batches 10,000 records or 5s, whichever comes first
Exporters:
clickhouse— Writes to ClickHouse with auto-schema creationdebug— Logs basic info (useful for troubleshooting)
To modify the pipeline, edit otel-collector.yaml and restart the collector:
docker compose restart otel-collectorDatabase
PostgreSQL Schema
Tables are created by migration scripts in packages/database/src/migrations/:
| Migration | Creates |
|---|---|
0001_initial.sql | organizations, users, memberships tables + RLS policies |
0002_teams_and_invites.sql | teams, team_members, invites tables + RLS |
0003_integrations.sql | integrations, integration_project_mappings tables + RLS |
0004 – 0010 | Additional tables and schema updates (applied automatically on startup) |
Migrations are applied automatically when the backend starts. The manual psql commands below are a fallback if auto-migration fails.
All tables use Row-Level Security (RLS) for tenant isolation. Every query is scoped to the current organization via SET LOCAL app.current_tenant.
ClickHouse Tables
Created automatically by the OTel Collector on first startup:
| Table | Data |
|---|---|
otel_traces | Task session spans |
otel_metrics_sum | Code line counters (AI vs manual) |
otel_logs | Friction events (prompt loops, errors) |
memory_access_log | Memory usage tracking (has 90-day TTL) |
The memory_access_log table has a 90-day TTL. Core telemetry tables do not have a TTL configured — data is retained indefinitely unless you add one.
Applying Migrations
Migrations are idempotent (safe to run multiple times):
for f in packages/database/src/migrations/*.sql; do
docker exec -i tandemu-postgres-1 psql -U tandemu -d tandemu < "$f"
doneSecurity Notes
- Change
JWT_SECRETin production — the default is not secure - API tokens for integrations are encrypted at rest using AES-256-GCM (set
ENCRYPTION_KEYin production) - RLS ensures organization data isolation at the database level
- CORS is restricted to the configured origin
- Stripe webhooks use cryptographic signature verification