life-docs/infrastructure.md
2026-03-20 09:32:20 -07:00

9.3 KiB

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