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

18 KiB

Service Map

All services across hosts for the life-manager ecosystem.


Architecture Overview

┌─────────────────────────────────────────────────────────────────────────────┐
│                              LIFE-MANAGER                                  │
│                                                                            │
│  ┌─────────────────────┐  ┌──────────────────┐  ┌──────────────────────┐   │
│  │  Backend API        │  │  Frontend (web)   │  │  CLI                 │   │
│  │  NestJS :3700       │  │  React+Vite :5701 │  │  ./run <cmd>         │   │
│  │                     │  │       │           │  │       │              │   │
│  │  Modules:           │  │       │ REST/WS   │  │       │ REST         │   │
│  │  ├ assistant        │  │       ▼           │  │       ▼              │   │
│  │  ├ tasks/goals      │  │  ┌────────────┐   │  │  ┌────────────┐     │   │
│  │  ├ health/meds      │  │  │ API :3700  │   │  │  │ API :3700  │     │   │
│  │  ├ notifications    │  │  └────────────┘   │  │  └────────────┘     │   │
│  │  ├ messages         │  │                   │  │                     │   │
│  │  ├ schedule         │  │                   │  │  infra consumers ───┐   │
│  │  └ ...              │  │                   │  │  infra status ─────┐│   │
│  └──────┬──┬──┬────────┘  └──────────────────┘  └───────────────────┘│   │
│         │  │  │                                         │  │          │   │
└─────────┼──┼──┼─────────────────────────────────────────┼──┼──────────┘   │
          │  │  │                                         │  │              │
          │  │  │  OWNED INFRA (same compose.yml)         │  │              │
          │  │  │  ┌──────────────────────────────────┐   │  │              │
          │  │  └──┤  PostgreSQL :25471                │   │  │              │
          │  │     │  Redis :26370 + Insight :28371    │   │  │              │
          │  │     └──────────────────────────────────┘   │  │              │
          │  │                                            │  │              │
          │  │  SHARED SERVICES (separate repos)          │  │              │
          │  │  ┌─────────────────────────────────────────┼──┼──────────┐   │
          │  │  │                                         │  │          │   │
          │  │  │  ┌──────────────────────────────┐       │  │          │   │
          │  └──┼─►│ messenger (NestJS)           │◄──────┘  │          │   │
          │     │  │ black :3100                  │          │          │   │
          │     │  │ @lilith/consumer-tracking    │          │          │   │
          │     │  │ GET /consumers               │          │          │   │
          │     │  │                              │          │          │   │
          │     │  │ Callers from life-manager:   │          │          │   │
          │     │  │   messaging.channel.ts       │          │          │   │
          │     │  │   messages-proxy.service.ts   │          │          │   │
          │     │  └──────────────────────────────┘          │          │   │
          │     │                                            │          │   │
          │     │  ┌──────────────────────────────┐          │          │   │
          ├─────┼─►│ model-boss (Python/FastAPI)  │◄─────────┘          │   │
          │     │  │ apricot :8210                │                     │   │
          │     │  │ Own consumer tracking        │                     │   │
          │     │  │ GET /api/v1/clients           │                     │   │
          │     │  │ GET /api/v1/gpu/status        │                     │   │
          │     │  │                              │                     │   │
          │     │  │ life-manager uses 10 models: │                     │   │
          │     │  │   qwen3-4b (default)         │                     │   │
          │     │  │   qwen3-8b, assistant-*-ft2  │                     │   │
          │     │  │   ii-medical-8b (health)     │                     │   │
          │     │  │   kuvera-8b (finance)        │                     │   │
          │     │  │   fin-o1-8b (fin reasoning)  │                     │   │
          │     │  │   phi-4, phi-4-therapy       │                     │   │
          │     │  │   veritas-12b (philosophy)   │                     │   │
          │     │  │   companion-14b              │                     │   │
          │     │  │   assistant-lm-v1            │                     │   │
          │     │  │                              │                     │   │
          │     │  │ Callers from life-manager:   │                     │   │
          │     │  │   llm-client.service.ts      │                     │   │
          │     │  │   life-manager-vram (black)   │                     │   │
          │     │  └──────────────────────────────┘                     │   │
          │     │                                                       │   │
          │     │  ┌──────────────────────────────┐                     │   │
          └─────┼─►│ Chatterbox TTS (optional)    │                     │   │
                │  │ apricot :41222               │                     │   │
                │  │ No consumer tracking         │                     │   │
                │  │                              │                     │   │
                │  │ Caller from life-manager:    │                     │   │
                │  │   chatterbox-proxy.service    │                     │   │
                │  │   (WebSocket, no X-Client-Id) │                     │   │
                │  └──────────────────────────────┘                     │   │
                │                                                       │   │
                │  ┌──────────────────────────────┐                     │   │
                ├─►│ speech-synthesis (Express)   │◄────────────────────┘   │
                │  │ apricot :31770 (on-demand)   │                         │
                │  │ @lilith/consumer-tracking    │                         │
                │  │ GET /consumers               │                         │
                │  │                              │                         │
                │  │ NOT YET CONSUMED by LM       │                         │
                │  │ (tracking wired for future)   │                         │
                │  └──────────────────────────────┘                         │
                │                                                           │
                └───────────────────────────────────────────────────────────┘

Data Flows

Outbound from life-manager:

  API ──► model-boss :8210    LlmClientService (OpenAI-compat, streaming)
      │                       10 models via inference proxy, prompt-based tool calls
      │
      ├──► messenger :3100    MessagingChannel (notification dispatch, enqueue)
      │                       MessagesProxyService (browse, search, self-messages)
      │                       Headers: X-Client-Id: life-manager, X-Service-Key: <key>
      │
      └──► Chatterbox :41222  ChatterboxProxyService (WebSocket TTS/STT)
                              Health check via HTTP /health

  CLI ──► model-boss :8210    ./run services gpu (model-boss CLI)
      ├──► messenger :3100    ./run infra consumers (GET /consumers)
      └──► speech-synth :31770  ./run infra consumers (GET /consumers)

  VRAM ──► model-boss :8210   life-manager-vram.service on black
                              GPU lease management (warmup, drain)

Hosts

Host Role Access
apricot Dev workstation Local
black Production server SSH (ssh black)
plum macOS build host SSH (ssh plum-voyager)

Services by Host

apricot (dev)

Service Type Port Status Command
PostgreSQL Docker (life-manager-postgres) 25471 docker compose ps
Redis + RedisInsight Docker (life-manager-redis) 26370 (Redis), 28371 (Insight) docker compose ps
Backend API Turbo dev (Node) 3700 ./run dev
Frontend (web) Turbo dev (Vite) 5701 ./run dev
Showcase Turbo dev (Vite) 5702 ./run dev:all or ./run dev:showcase
Remote Management Daemon Systemd user unit 3710 systemctl --user status life-manager-daemon
Tray Icon (local) Systemd user unit (graphical-session.target) systemctl --user status life-manager-tray
model-boss coordinator External (separate repo) 8210 curl http://localhost:8210/health
Chatterbox TTS External (optional) 41222 HTTP+WS

black (prod)

Service Unit / Type Port Status
life-manager-api life-manager-api.service (systemd user) 3700 systemctl --user status life-manager-api
life-manager-caddy life-manager-caddy.service (systemd user) 5700 (HTTPS proxy) systemctl --user status life-manager-caddy
life-manager-daemon life-manager-daemon.service (systemd user) 3710 systemctl --user status life-manager-daemon
life-manager-vram life-manager-vram.service (systemd user) — (manages GPU leases on apricot remotely) systemctl --user status life-manager-vram
messenger-caddy messenger-caddy.service (systemd user) 3100 (proxy) systemctl --user status messenger-caddy
PostgreSQL Docker (life-manager-postgres) 25471 docker ps --filter name=life-manager-postgres
Redis + RedisInsight Docker (life-manager-redis) 26370 / 28371 docker ps --filter name=life-manager-redis

plum (macOS)

Service Type Notes
LilithMessaging ~/Applications/LilithMessaging.app Syncs chat.db → black every 30s
Life Desktop ~/Applications/LifeDesktop.app Menu bar voice assistant + service monitor
Xcode build host Manual iOS simulator builds via xcodebuild

Docker Containers

All environments use the same compose.yml at project root.

Container Image Port Mapping Healthcheck
life-manager-postgres postgres:16-alpine 25471 → 5432 pg_isready
life-manager-redis redis/redis-stack:latest 26370 → 6379, 28371 → 8001 redis-cli ping

Dev database: life_manager / Prod database: life_manager_prod (same Postgres, different DB).


Tray Icon System

All tray/menu bar apps use @lilith/tray-resources for colored status icons (SVG → PNG generation).

App Host Platform Icon Template How it connects
scripts/tray.py apricot Linux (AyatanaAppIndicator3) hexagon-hub Port checks (dev) or systemd units (prod)
Life Desktop plum macOS (LilithMenuBar) hexagon-hub HTTP health check to API
LilithMessaging plum macOS (LilithMenuBar) chat-bubble iMessage sync state

Messenger Dependency Chain

Phone (iMessage)
  → chat.db on plum
    → LilithMessaging.app syncs to black:3100 (messenger-caddy → messenger API)
      → life-manager-api polls (1min cron, MessagingLoopService)
        → AgentExecutor → model-boss (apricot:8210) → qwen3-4b
          → reply enqueued in messenger DB
            → LilithMessaging.app picks up send queue
              → osascript sends via Messages.app
                → Phone receives reply

Shared Service Consumer Tracking

Life-manager depends on three shared services. Each exposes a consumer tracking endpoint so ./run infra consumers can show who's calling what.

Service Package Endpoint Auth
model-boss Own implementation GET /api/v1/clients + GET /api/v1/gpu/status None
messenger @lilith/consumer-tracking (NestJS module) GET /consumers X-Service-Key
speech-synthesis @lilith/consumer-tracking (Express middleware) GET /consumers None

How Consumer Tracking Works

  • Callers send X-Client-Id: life-manager header with every request
  • The @lilith/consumer-tracking package (published to forgejo) tracks per-client: request count, last seen time, last endpoint hit
  • model-boss has its own client registry (registered clients with profiles + GPU leases)
  • ./run infra consumers [service] queries all three in parallel

life-manager's Outbound Headers

Caller Target Headers Sent
messaging.channel.ts messenger X-Client-Id: life-manager, X-Service-Key: <key>
messages-proxy.service.ts messenger X-Client-Id: life-manager, X-Service-Key: <key>
llm-client.service.ts model-boss OpenAI-compat (no X-Client-Id, uses registered client)
chatterbox-proxy.service.ts Chatterbox WebSocket (no HTTP headers)

Port Registry (Single Source of Truth)

Port Service Host(s) Protocol Consumer Tracking
3100 Messenger API (via Caddy) black HTTPS GET /consumers
3700 Life Manager API apricot (dev), black (prod) HTTP
3710 Remote Management Daemon apricot, black HTTP
5700 Frontend (Caddy prod) black HTTPS
5701 Frontend (Vite dev) apricot HTTP
5702 Showcase (Vite dev) apricot HTTP
8210 model-boss coordinator apricot HTTP GET /api/v1/clients
25471 PostgreSQL apricot, black TCP
26370 Redis apricot, black TCP
28371 RedisInsight apricot, black HTTP
31770 speech-synthesis (on-demand) apricot HTTP+WS GET /consumers
41222 Chatterbox TTS (optional) apricot HTTP+WS

How to Check Status

# Full infrastructure dashboard
./run infra status              # All hosts
./run infra status apricot      # Single host
./run infra health              # One-line-per-host summary

# Consumer tracking — who's calling shared services
./run infra consumers           # All three services
./run infra consumers model-boss      # Single service
./run infra consumers messenger
./run infra consumers speech-synthesis

# Manual checks
systemctl --user status life-manager-daemon

# black (prod)
ssh black "systemctl --user status life-manager-api life-manager-caddy life-manager-daemon life-manager-vram"
ssh black "docker ps --filter name=life-manager"

# plum (macOS)
ssh plum-voyager "pgrep -fl LilithMessaging"
ssh plum-voyager "pgrep -fl LifeDesktop"

Known Issues (as of 2026-03-08)

  • MessagingLoopService on black is throwing Cannot read properties of undefined (reading 'startsWith') every minute — reply loop is broken.
  • life-manager-vram — remotely manages GPU leases on apricot via model-boss (apricot:8210). Currently failing: warmup 502/500, drain 422. Black has no GPUs — all LLM inference runs on apricot.
  • life-manager-tray on black — restart-looping (no display server). The unit installs via prod.sh release but black is headless. Tray runs on apricot only (local tray.py).
  • messenger-imessage.service — unit file does not exist; the messenger API is proxied through messenger-caddy instead.