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
| 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.