# kuploy native stack spec — LearnHouse LMS + LiveKit (live classrooms)
#
# Composite of the standalone `learnhouse` and `livekit` templates: it
# co-deploys both and WIRES them together using the kuploy stack-connection
# passthrough primitive (kuploy#86 — `env:` + `wsUrl` connection fields). Two
# values flow from livekit-server into learnhouse-app's managed env block,
# server-side (never through the browser):
#
#   env:LIVEKIT_KEYS  → LIVEKIT_KEYS  (apikey:apisecret — the FastAPI token
#                                      endpoint signs JOIN tokens with this)
#   wsUrl             → LIVEKIT_URL   (public wss:// signalling URL handed to
#                                      the browser alongside the minted token)
#
# Requires a kuploy server built from `dev` (>= kuploy#86) so those connection
# fields resolve. Media path (STUNner relay) is auto-wired by the server when
# the shared gateway is installed — see templates/livekit for the detail.
#
# SECURITY: LearnHouse holds LIVEKIT_KEYS ONLY. The api-secret stays in the
# FastAPI backend (never NEXT_PUBLIC_*, never the browser); the browser gets
# only the short-lived JOIN token. The STUNner shared secret and the minted TURN
# credential never appear in LearnHouse — the SFU advertises relay candidates
# itself, so the client needs no TURN credential.

name: learnhouse-live
description: LearnHouse LMS with LiveKit live classrooms (video beside Boards)
components:
  - name: learnhouse-app
    type: web
    serviceType: application
    domain: true
    port: 80 # nginx inside the container fronts web(8000)/api(9000)/collab(4000)
    source:
      type: docker
      # Fork image carrying the live-classrooms app code (FastAPI token endpoint
      # + Next.js room UI). Built from kuploy/learnhouse and pushed to Docker Hub
      # under the ceduth namespace by the fork's publish workflow. NOT
      # ghcr.io/learnhouse/app — that upstream image predates this feature.
      image: ceduth/learnhouse-app:main # pin to :main-<sha> once the first build lands
    dependsOn:
      - learnhouse-postgres
      - learnhouse-redis
      - learnhouse-media
    env:
      # --- resolved from provider services at deploy ---
      - key: LEARNHOUSE_SQL_CONNECTION_STRING
        fromComponent: learnhouse-postgres
        field: connectionString # postgresql://user:pass@host:5432/db
      - key: LEARNHOUSE_REDIS_CONNECTION_STRING
        fromComponent: learnhouse-redis
        field: connectionString # redis://:pass@host:6379
      - key: LEARNHOUSE_REDIS_URL # the collab server reads this one
        fromComponent: learnhouse-redis
        field: connectionString
      # --- live classrooms: wired from livekit-server via the kuploy#86 primitive ---
      # apikey:apisecret — used ONLY by the FastAPI token endpoint to sign JOIN
      # tokens. Lands server-side; never shipped to the browser.
      - key: LIVEKIT_KEYS
        fromComponent: livekit-server
        field: "env:LIVEKIT_KEYS"
      # public wss:// signalling URL — returned to the browser by the token
      # endpoint alongside the token, so no NEXT_PUBLIC_* var is needed.
      - key: LIVEKIT_URL
        fromComponent: livekit-server
        field: wsUrl
      # --- generated at provision time (must be >=32 chars or the API refuses
      #     to boot — config.py raises on a short LEARNHOUSE_AUTH_JWT_SECRET_KEY) ---
      - key: NEXTAUTH_SECRET
        generate: random32
      - key: LEARNHOUSE_AUTH_JWT_SECRET_KEY
        generate: random32
      - key: COLLAB_INTERNAL_KEY
        generate: random32
      # First-boot install: the API auto-install hard-requires an admin password
      # (verified on a live deploy — without it the API crashloops on startup).
      - key: LEARNHOUSE_INITIAL_ADMIN_PASSWORD
        generate: random32
      - key: LEARNHOUSE_INITIAL_ADMIN_EMAIL
        value: "admin@lms.example.com"
      # --- app config: ${domain} is replaced at provision time with the free
      #     host assigned to learnhouse-app, so these are reachable with no edits.
      - key: LEARNHOUSE_PORT
        value: "9000"
      - key: LEARNHOUSE_DOMAIN
        value: "${domain}"
      # Frontend host used to build invite / signup email links. Without this the
      # API falls back to its shipped default (localhost:3000), so invitation
      # links point at localhost. LEARNHOUSE_SSL makes that fallback build https.
      - key: LEARNHOUSE_FRONTEND_DOMAIN
        value: "${domain}"
      - key: LEARNHOUSE_SSL
        value: "true"
      - key: LEARNHOUSE_COOKIE_DOMAIN
        value: "${domain}"
      - key: NEXT_PUBLIC_LEARNHOUSE_DOMAIN
        value: "${domain}"
      - key: NEXT_PUBLIC_LEARNHOUSE_HTTPS
        value: "true"
      - key: NEXTAUTH_URL
        value: "https://${domain}"
      - key: NEXT_PUBLIC_LEARNHOUSE_API_URL
        value: "https://${domain}/api/v1/" # note the /api/v1/ path + trailing slash
      - key: NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL
        value: "https://${domain}"
      - key: NEXT_PUBLIC_COLLAB_URL
        value: "wss://${domain}/collab"
      # In-product "Documentation" links resolve to the learnhouse-docs
      # component's public domain (https://). The `url` connection follows a
      # kuploy-cloud custom domain set on that component.
      - key: NEXT_PUBLIC_LEARNHOUSE_DOCS_URL
        fromComponent: learnhouse-docs
        field: url
      # --- object storage: a managed bucket on the shared cluster MinIO
      #     (kuploy-k8s#36). The kuploy server provisions a scoped bucket + a
      #     restricted access key and injects endpoint/bucket/key/secret here
      #     server-side — no hand-entered creds, no placeholder. boto3 reads the
      #     standard AWS_* chain; LearnHouse adds the two LEARNHOUSE_S3_API_*
      #     vars. The app does not auto-create the bucket — provisioning does. ---
      - key: LEARNHOUSE_CONTENT_DELIVERY_TYPE
        value: "s3api"
      - key: LEARNHOUSE_S3_API_ENDPOINT_URL
        fromComponent: learnhouse-media
        field: endpoint # http://minio.<ns>:9000 (in-cluster)
      - key: LEARNHOUSE_S3_API_BUCKET_NAME
        fromComponent: learnhouse-media
        field: bucket # the provisioned bucket name (not a hardcoded one)
      - key: AWS_ACCESS_KEY_ID
        fromComponent: learnhouse-media
        field: accessKey # bucket-scoped service-account key
      - key: AWS_SECRET_ACCESS_KEY
        fromComponent: learnhouse-media
        field: secretKey
      # boto3 needs a region; value is arbitrary for MinIO.
      - key: AWS_REGION
        fromComponent: learnhouse-media
        field: region
      - key: AWS_DEFAULT_REGION
        fromComponent: learnhouse-media
        field: region

  - name: livekit-server
    type: web
    serviceType: application
    domain: true
    port: 7880 # WebSocket signaling; RTC/UDP media is auto-wired via STUNner
    source:
      type: docker
      image: livekit/livekit-server:v1.13.1 # pinned (do not use :latest)
    env:
      - key: LIVEKIT_KEYS # "key: secret" — generated at provision time
        generate: keypair
      # Single-port UDP mux + external-IP advertisement (kuploy-k8s#30 "always"
      # prerequisite). MUST be single-line flow YAML — a `value: |` block scalar
      # truncates at the first newline when kuploy re-parses the env string.
      # When STUNner is installed the server merges a turn_servers entry into
      # this value at deploy time; do NOT hardcode a turn block here.
      - key: LIVEKIT_CONFIG # flow YAML; merged with LIVEKIT_KEYS from the env above
        value: "{rtc: {tcp_port: 7881, udp_port: 7882, use_external_ip: true}}"

  - name: learnhouse-docs
    type: web
    serviceType: application
    domain: true
    port: 3000 # standalone Next.js docs server (LearnHouse docs site)
    source:
      type: docker
      # Built from kuploy/learnhouse docs/ by the fork's publish-docs-image
      # workflow. Serves the end-user docs incl. /platform/live-classrooms.
      image: ceduth/learnhouse-docs:main

  - name: learnhouse-postgres
    type: database
    serviceType: postgres
    engine: postgres
    source:
      type: docker
      image: pgvector/pgvector:pg16 # AI/RAG needs the pgvector extension

  - name: learnhouse-redis
    type: database
    serviceType: redis
    engine: redis
    cache: true # ephemeral cache (no PVC, capped, LRU) — cache + collab pub/sub only

  - name: learnhouse-media
    type: storage
    serviceType: bucket # managed object storage on the shared cluster MinIO (kuploy-k8s#36)
    # No source/engine: a bucket runs no per-stack workload. At deploy the kuploy
    # server creates the bucket + a bucket-scoped access key on the shared MinIO
    # and wires endpoint/bucket/accessKey/secretKey into learnhouse-app above.
    # Requires the shared MinIO installed (`deploy.py minio`, kuploy-k8s#36).
