Life Management System — Infrastructure & Environment
Docker Compose Specification
# docker-compose.yml
services:
postgres:
image: postgres:16-alpine
container_name: life-manager-postgres
restart: unless-stopped
ports:
- "25470:5432"
environment:
POSTGRES_DB: life_manager
POSTGRES_USER: lilith
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U lilith -d life_manager"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis/redis-stack:latest
container_name: life-manager-redis
restart: unless-stopped
ports:
- "26370:6379"
- "28371:8001" # RedisInsight UI
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
redis_data:
init.sql
-- Executed on first database creation only
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
Environment Variable Catalog
Database
| Variable |
Purpose |
Default |
Required |
DATABASE_HOST |
PostgreSQL host |
localhost |
Yes |
DATABASE_PORT |
PostgreSQL port |
25470 |
Yes |
DATABASE_USER |
PostgreSQL user |
lilith |
Yes |
DATABASE_PASSWORD |
PostgreSQL password |
— |
Yes |
DATABASE_NAME |
Database name |
life_manager |
Yes |
DATABASE_SYNCHRONIZE |
TypeORM auto-sync schema |
true (dev only) |
No |
DATABASE_LOGGING |
TypeORM query logging |
false |
No |
API Server
| Variable |
Purpose |
Default |
Required |
NODE_ENV |
Runtime environment |
development |
Yes |
API_PORT |
API server port |
3700 |
No |
API_PREFIX |
Global route prefix |
api |
No |
CORS_ORIGINS |
Allowed CORS origins |
http://localhost:5700 |
No |
Frontend
| Variable |
Purpose |
Default |
Required |
VITE_API_URL |
Backend API URL |
http://localhost:3700/api |
No |
VITE_WS_URL |
WebSocket URL |
http://localhost:3700 |
No |
Encryption
| Variable |
Purpose |
Default |
Required |
ENCRYPTION_KEY |
pgcrypto symmetric key (32-byte hex) |
— |
Yes |
Generate with: openssl rand -hex 32
LLM Services
| Variable |
Purpose |
Default |
Required |
LLM_BASE_URL |
OpenAI-compatible LLM API URL |
http://localhost:10010/v1 |
Yes |
LLM_DEFAULT_MODEL |
Default model for chat |
qwen3-8b |
Yes |
LLM_FAST_MODEL |
Fast model for simple queries |
qwen3-4b |
No |
LLM_REASONING_MODEL |
Reasoning model for complex planning |
qwen3-8b |
No |
ANTHROPIC_API_KEY |
Claude API key (opt-in for complex tasks) |
— |
No |
LITELLM_PROXY_URL |
LiteLLM proxy URL for Claude routing |
— |
No |
Voice (Chatterbox)
| Variable |
Purpose |
Default |
Required |
CHATTERBOX_URL |
Chatterbox HTTP base URL |
http://localhost:41222 |
For voice |
CHATTERBOX_WS_URL |
Chatterbox WebSocket URL |
ws://localhost:41222 |
For voice |
CHATTERBOX_TTS_PATH |
TTS WebSocket path |
/ws |
No |
CHATTERBOX_STT_PATH |
STT WebSocket path |
/ws/stt |
No |
Redis
| Variable |
Purpose |
Default |
Required |
REDIS_HOST |
Redis host |
localhost |
Yes |
REDIS_PORT |
Redis port |
26370 |
Yes |
REDIS_PASSWORD |
Redis password |
— |
No |
.env.example
# Database
DATABASE_HOST=localhost
DATABASE_PORT=25470
DATABASE_USER=lilith
DATABASE_PASSWORD=change_me_in_production
DATABASE_NAME=life_manager
DATABASE_SYNCHRONIZE=true
# API
NODE_ENV=development
API_PORT=3700
CORS_ORIGINS=http://localhost:5700
# Frontend
VITE_API_URL=http://localhost:3700/api
VITE_WS_URL=http://localhost:3700
# Encryption (generate with: openssl rand -hex 32)
ENCRYPTION_KEY=
# LLM
LLM_BASE_URL=http://localhost:10010/v1
LLM_DEFAULT_MODEL=qwen3-8b
# Claude (optional)
ANTHROPIC_API_KEY=
# Chatterbox (optional, for voice)
CHATTERBOX_URL=http://localhost:41222
CHATTERBOX_WS_URL=ws://localhost:41222
# Redis
REDIS_HOST=localhost
REDIS_PORT=26370
Port Allocation
| Service |
Port |
Protocol |
Purpose |
| API Server |
3700 |
HTTP + WS |
REST API + Socket.IO gateways |
| Frontend Dev |
5700 |
HTTP |
Vite dev server |
| PostgreSQL |
25470 |
TCP |
Primary datastore |
| Redis |
26370 |
TCP |
Session cache, pub/sub |
| RedisInsight |
28371 |
HTTP |
Redis management UI |
| Chatterbox TTS/STT |
41222 |
HTTP + WS |
Voice synthesis and recognition |
| Local LLM (primary) |
10010 |
HTTP |
Qwen3 8B (default model) |
| Local LLM (fast) |
10020 |
HTTP |
Qwen3 4B (fast model) |
services.yaml
Port resolution via @lilith/service-registry:
life-manager:
api:
port: 3700
protocol: http
health: /health
frontend:
port: 5700
protocol: http
postgresql:
port: 25470
protocol: tcp
redis:
port: 26370
protocol: tcp
Development Setup
Prerequisites
- Node.js 22+ (via nvm or fnm)
- pnpm 9+ (
corepack enable && corepack prepare pnpm@latest --activate)
- Docker + Docker Compose (for PostgreSQL and Redis)
- Turborepo (
pnpm add -g turbo)
Quick Start
# 1. Clone and install
git clone <repo-url> life-manager
cd life-manager
pnpm install
# 2. Environment
cp .env.example .env
# Edit .env — set DATABASE_PASSWORD and generate ENCRYPTION_KEY:
openssl rand -hex 32
# Paste result as ENCRYPTION_KEY value
# 3. Start infrastructure
docker compose up -d
# Wait for healthy: docker compose ps
# 4. Run database migrations
pnpm --filter backend-api migration:run
# 5. Seed initial data
pnpm --filter backend-api seed
# 6. Start development servers
pnpm dev
# Backend: http://localhost:3700 (API), http://localhost:3700/api-docs (Swagger)
# Frontend: http://localhost:5700
Common Commands
# Start all dev servers (turbo)
pnpm dev
# Build all packages
pnpm build
# Type check
pnpm typecheck
# Run tests
pnpm test
# Run a specific workspace
pnpm --filter backend-api dev
pnpm --filter frontend dev
pnpm --filter shared build
# Database
pnpm --filter backend-api migration:generate -- -n MigrationName
pnpm --filter backend-api migration:run
pnpm --filter backend-api migration:revert
pnpm --filter backend-api seed
# Docker
docker compose up -d # Start infrastructure
docker compose down # Stop infrastructure
docker compose logs -f # Follow logs
docker compose ps # Check health
Production Considerations
Reverse Proxy
NGINX or Caddy in front of the API server:
# /etc/nginx/conf.d/life-manager.conf
upstream api {
server 127.0.0.1:3700;
}
server {
listen 443 ssl http2;
server_name life.example.com;
ssl_certificate /etc/ssl/certs/life.example.com.pem;
ssl_certificate_key /etc/ssl/private/life.example.com.key;
# Frontend (static files served from build output)
location / {
root /var/www/life-manager/frontend/dist;
try_files $uri $uri/ /index.html;
}
# API
location /api/ {
proxy_pass http://api;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket
location /socket.io/ {
proxy_pass http://api;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
# Health check
location /health {
proxy_pass http://api;
}
}
HTTPS
- Local development: HTTP only (no TLS overhead)
- Production: TLS via reverse proxy (Let's Encrypt or self-signed for local network)
Backup Strategy
# PostgreSQL automated backup (daily, cron)
pg_dump -h localhost -p 25470 -U lilith life_manager | \
gzip > /backups/life_manager_$(date +%Y%m%d_%H%M%S).sql.gz
# Retention: keep last 30 daily backups
find /backups -name "life_manager_*.sql.gz" -mtime +30 -delete
# Redis persistence: RDB snapshots + AOF (configured in redis.conf)
# Redis Stack includes persistence by default
Log Aggregation
- Backend: NestJS Logger outputs structured JSON in production (
NODE_ENV=production)
- Log levels: error, warn, log, debug, verbose
- Named loggers: Each module creates
new Logger('TasksService') for module-scoped filtering
- Production output: JSON lines to stdout → collected by systemd journal or container runtime
// Production log output format:
{"level":"log","context":"TasksService","message":"Task created","taskId":"uuid","domainId":"uuid","timestamp":"2026-02-24T10:00:00.000Z"}
Process Management
# Development: pnpm dev (turbo watch mode)
# Production: Node.js process via systemd or Docker
# systemd unit example:
[Unit]
Description=Life Manager API
After=network.target postgresql.service redis.service
[Service]
Type=simple
User=lilith
WorkingDirectory=/opt/life-manager
ExecStart=/usr/bin/node backend-api/dist/main.js
Restart=on-failure
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target