292 lines
18 KiB
Markdown
292 lines
18 KiB
Markdown
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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.
|