Backend: - tiers.py: real license validation via Heimdall (self-hosted key or cloud user_id), 30-min cache, BYOK exception for translation - session_store.py: create_session() accepts tier/user_id/window_ms/transcribe_lang/ num_speakers; tier-aware _should_reap() (Free: reap on subscriber leave, Paid: activity TTL); auto-snapshot paid sessions with history on reap; voice-ready probe on session start - services/translator.py: DeepL-backed label translation, CF-managed and BYOK key paths, per-session label cache - models/session.py, scene_event.py, service_event.py: new event types and session fields - api/snapshots.py: GET /sessions/history + POST /session/resume (Paid tier only) - api/dev.py: dev-only /dev/voice/stop|start|restart proxy to cf-orch agent - migrations/002_session_snapshots.sql: session_snapshots table Frontend: - ComposeBar.vue: pre-session settings panel (window, language, speakers, Elcor toggle) with slide transition; passes options to startSession() - stores/settings.ts: persistent user preferences (localStorage), survives page reload - stores/session.ts: voiceReady/voiceError/voiceWarning states; SceneEvent/AccentEvent/ ServiceEvent types; TTL timers for environ/queue badges; startSession() accepts opts - composables/useToneStream.ts: handlers for scene-event, accent-event, service-event SSE - NowPanel.vue: diarization speaker identity badge, acoustic scene badge, privacy risk indicator - HistoryStrip.vue: per-speaker colour chips in history strip - components/DevPanel.vue: dev-only cf-voice stop/restart controls + thread view toggle - App.vue: DevPanel in footer (DEV only), threadView flag switches v0.1.x/v0.2.x preview Tests: - tests/test_pinning.py: 20 tests covering tier-aware reaping, snapshot creation, and translator behaviour - tests/test_tiers.py: 17 tests covering key validation, cloud resolution, caching, require_paid() gate, BYOK exception - All 72 tests passing
63 lines
2.1 KiB
YAML
63 lines
2.1 KiB
YAML
# compose.cloud.yml — Cloud managed instance
|
|
#
|
|
# Project: linnet-cloud (docker compose -f compose.cloud.yml -p linnet-cloud ...)
|
|
# Web: http://127.0.0.1:8527 → menagerie.circuitforge.tech/linnet (via Caddy + JWT)
|
|
# API: internal only on linnet-cloud-net (nginx proxies /session/ → linnet-api:8522)
|
|
#
|
|
# Requires in .env:
|
|
# CLOUD_MODE=true
|
|
# HEIMDALL_URL=https://license.circuitforge.tech
|
|
# HEIMDALL_ADMIN_TOKEN=... (admin token for /admin/cloud/resolve — resolves tier from user_id)
|
|
# DIRECTUS_JWT_SECRET=... (Caddy injects X-CF-Session from cf_session cookie)
|
|
# HF_TOKEN=... (needed for real cf-voice inference; omit for mock)
|
|
# CF_VOICE_MOCK=0 (set to 1 during staged rollout)
|
|
#
|
|
# Note: LINNET_LICENSE_KEY is for self-hosted installs only.
|
|
# Cloud mode resolves tier via HEIMDALL_ADMIN_TOKEN + /admin/cloud/resolve.
|
|
|
|
services:
|
|
linnet-api:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
args:
|
|
GIT_TOKEN: ${GIT_TOKEN:-}
|
|
restart: unless-stopped
|
|
env_file: .env
|
|
environment:
|
|
CLOUD_MODE: "true"
|
|
LINNET_PORT: "8522"
|
|
LINNET_FRONTEND_PORT: "8527"
|
|
LINNET_BASE_URL: "https://menagerie.circuitforge.tech/linnet"
|
|
CLOUD_DATA_ROOT: /devl/linnet-cloud-data
|
|
CF_ORCH_URL: http://host.docker.internal:7700
|
|
extra_hosts:
|
|
- "host.docker.internal:host-gateway"
|
|
volumes:
|
|
- /devl/linnet-cloud-data:/devl/linnet-cloud-data
|
|
- ${HOME}/.config/circuitforge:/root/.config/circuitforge:ro
|
|
# Live-reload app code without rebuilding during active dev
|
|
- ./app:/app/app:ro
|
|
networks:
|
|
- linnet-cloud-net
|
|
|
|
linnet-web:
|
|
build:
|
|
context: .
|
|
dockerfile: docker/web/Dockerfile
|
|
args:
|
|
VITE_BASE_URL: /linnet/
|
|
VITE_API_BASE: /linnet
|
|
restart: unless-stopped
|
|
ports:
|
|
- "8527:80"
|
|
volumes:
|
|
- ./docker/web/nginx.cloud.conf:/etc/nginx/conf.d/default.conf:ro
|
|
networks:
|
|
- linnet-cloud-net
|
|
depends_on:
|
|
- linnet-api
|
|
|
|
networks:
|
|
linnet-cloud-net:
|
|
driver: bridge
|