150 lines
4.9 KiB
Markdown
150 lines
4.9 KiB
Markdown
# quinn.ai Deployment
|
|
|
|
**Domain**: `ai.transquinnftw.com`
|
|
**Host**: vps-0 (89.127.233.145) — 1984 Hosting, Debian 12
|
|
**Auth**: nginx basic auth (`/etc/quinn-ai/htpasswd`)
|
|
|
|
---
|
|
|
|
## Services
|
|
|
|
| Service | Location | Port |
|
|
|---------|----------|------|
|
|
| companion-api | vps-0, systemd unit `quinn-ai-companion-api` | :3850 |
|
|
| companion-web (PWA) | vps-0, nginx static | /var/www/quinn.ai/dist/ |
|
|
| PostgreSQL | vps-0, Docker `companion-postgres` | :26407 |
|
|
| Redis | vps-0, Docker `companion-redis` | :26406 |
|
|
| model-boss | apricot, via wg1 | 10.9.0.2:8210 |
|
|
| chatterbox-tts | apricot, via wg1 | 10.9.0.2:8000 |
|
|
|
|
---
|
|
|
|
## WireGuard Tunnel (wg1 — direct apricot ↔ vps-0)
|
|
|
|
A direct peer-to-peer WireGuard tunnel (wg1, separate from the hub-based wg0) connects vps-0 to apricot for AI service access.
|
|
|
|
| Host | wg1 IP | Role |
|
|
|------|--------|------|
|
|
| vps-0 | 10.9.0.1/24 | Server (listens :51820) |
|
|
| apricot | 10.9.0.2/32 | Client |
|
|
|
|
**Config files:**
|
|
- vps-0: `/etc/wireguard/wg1.conf` — interface + apricot peer
|
|
- apricot: `/etc/wireguard/wg1.conf` — interface + vps-0 peer
|
|
- Both enabled via `systemctl enable wg-quick@wg1`
|
|
|
|
**UFW rule on vps-0** (already applied):
|
|
```
|
|
ufw allow 51820/udp comment "WireGuard - apricot companion tunnel"
|
|
```
|
|
|
|
**Firewalld on apricot** (already applied):
|
|
```
|
|
firewall-cmd --zone=trusted --add-interface=wg1 --permanent
|
|
```
|
|
|
|
---
|
|
|
|
## Apricot-Side Bindings
|
|
|
|
Services verified via `ss -tlnp` on apricot, and connectivity confirmed from vps-0:
|
|
|
|
| Service | Bind | Reachable from vps-0 | Notes |
|
|
|---------|------|---------------------|-------|
|
|
| model-boss coordinator | `0.0.0.0:8210` | `http://10.9.0.2:8210/v1/models` → 200 | claude:* models available |
|
|
| chatterbox-tts | `0.0.0.0:8000` | `http://10.9.0.2:8000/health` → healthy | **Port is 8000, NOT 41222** — app.manifest.yaml has stale port; actual `CHATTERBOX_PORT=8000` |
|
|
| ai-core | NOT RUNNING | N/A | Bypassed — chat uses `claude:*` via model-boss subprocess |
|
|
|
|
**`SPEECH_SYNTHESIS_URL` in env must be `http://10.9.0.2:8000`** (not 41222).
|
|
|
|
---
|
|
|
|
## One-Time Setup (vps-0)
|
|
|
|
```bash
|
|
# 1. Create directories
|
|
mkdir -p /etc/quinn-ai /var/www/quinn.ai/api /var/www/quinn.ai/dist
|
|
|
|
# 2. Create htpasswd
|
|
htpasswd -c /etc/quinn-ai/htpasswd quinn
|
|
|
|
# 3. Create companion-api env from example
|
|
cp env/vps-0.env.example /etc/quinn-ai/companion-api.env
|
|
# Edit /etc/quinn-ai/companion-api.env — fill DATABASE_URL, REDIS_URL, VAPID keys, PUSH_FIRE_TOKEN
|
|
|
|
# 4. Generate VAPID keys
|
|
npx web-push generate-vapid-keys
|
|
# Paste output into /etc/quinn-ai/companion-api.env
|
|
|
|
# 5. Start Docker services (postgres + redis)
|
|
cd /path/to/companion/@deployments && docker compose up -d
|
|
# Or start from the companion @deployments docker-compose.yml
|
|
|
|
# 6. Obtain TLS cert (first time only)
|
|
certbot certonly --webroot -w /var/www/certbot -d ai.transquinnftw.com
|
|
# Ensure /var/www/certbot exists and nginx serves it for ACME challenges
|
|
|
|
# 7. Create certbot webroot dir
|
|
mkdir -p /var/www/certbot
|
|
```
|
|
|
|
---
|
|
|
|
## Deploy
|
|
|
|
```bash
|
|
bash deploy.sh # full deploy
|
|
bash deploy.sh --rollback # restore previous companion-api
|
|
bash deploy.sh --skip-build # skip local build (CI)
|
|
```
|
|
|
|
---
|
|
|
|
## Push Fire Restriction
|
|
|
|
`/api/push/fire` is restricted at nginx level to `10.9.0.2` only (apricot wg1 IP). The coworker-agent on apricot calls this endpoint to trigger Web Push notifications. This rule prevents the public internet from firing push notifications.
|
|
|
|
---
|
|
|
|
## Coworker Nag → Push Wiring (apricot one-time setup)
|
|
|
|
The coworker-agent cron loop fires `push-nag.sh` after every Miku TTS synthesis to deliver a matching native iOS push. Set this up once on apricot:
|
|
|
|
```bash
|
|
# 1. Create config dir
|
|
mkdir -p ~/.config/coworker-agent
|
|
|
|
# 2. Copy env example
|
|
cp /var/home/lilith/Code/@projects/@lilith/lilith-platform.live/users/transquinnftw/agents/coworker-agent/scripts/push.env.example \
|
|
~/.config/coworker-agent/push.env
|
|
|
|
# 3. Lock down permissions
|
|
chmod 600 ~/.config/coworker-agent/push.env
|
|
|
|
# 4. Generate a push fire token (same value goes into companion-api env on vps-0)
|
|
openssl rand -hex 32
|
|
# Paste the output into:
|
|
# ~/.config/coworker-agent/push.env → PUSH_FIRE_TOKEN=<value>
|
|
# /etc/quinn-ai/companion-api.env → PUSH_FIRE_TOKEN=<value>
|
|
|
|
# 5. Test the script directly
|
|
bash /var/home/lilith/Code/@projects/@lilith/lilith-platform.live/users/transquinnftw/agents/coworker-agent/scripts/push-nag.sh "Test push from apricot"
|
|
# Expected: "[push-nag] push sent" (or non-fatal failure if no subscriptions yet)
|
|
```
|
|
|
|
**Token synchronisation:** `PUSH_FIRE_TOKEN` must be identical in:
|
|
- `~/.config/coworker-agent/push.env` on apricot (loaded by `push-nag.sh`)
|
|
- `/etc/quinn-ai/companion-api.env` on vps-0 (validated by `PushFireGuard`)
|
|
|
|
**Fallback:** if `push.env` is missing or `PUSH_FIRE_TOKEN` is unset, `push-nag.sh` exits 0 silently — the nag loop continues uninterrupted.
|
|
|
|
---
|
|
|
|
## Logs
|
|
|
|
```bash
|
|
# On vps-0:
|
|
journalctl -u quinn-ai-companion-api -n 100 -f
|
|
tail -f /var/log/nginx/quinn.ai.access.log
|
|
tail -f /var/log/nginx/quinn.ai.error.log
|
|
```
|