> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hellocobi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Backend Service

> Environment variables, secrets, and Helm values for the Cobi backend service

## Overview

The backend is a Node.js REST API that handles authentication, business logic, data-connector queries, and LLM orchestration. It runs on port `3000` and is exposed through Gateway API or Ingress, usually on its own API hostname.

Set `CONNECT_SERVICE_URL` to the in-cluster Connect service URL when `connect.enabled: true`, for example `http://cobi-dashboard-connect:8000`.

## Required Secrets

Create the backend Kubernetes Secret **before** running `helm install`. The backend will crash-loop if the Secret is missing or incomplete.

```bash theme={null}
kubectl create secret generic cobi-backend-secrets \
  --namespace cobi \
  # ── Database ─────────────────────────────────────────────────────────
  --from-literal=DATABASE_URL="postgres://postgres:<password>@<pg-host>:5432/dashboard_auth?schema=public" \
  --from-literal=CORE_DATABASE_URL="postgres://postgres:<password>@<pg-host>:5432/cobi_core?schema=public" \
  --from-literal=ENABLE_MIGRATIONS="true" \
  # ── Auth ─────────────────────────────────────────────────────────────
  --from-literal=BETTER_AUTH_SECRET="<random-64-char-string>" \
  --from-literal=BETTER_AUTH_URL="https://api.example.com" \
  --from-literal=FRONTEND_URL="https://app.example.com" \
  --from-literal=CONNECT_SERVICE_URL="http://cobi-dashboard-connect:8000" \
  # ── LLM inference ────────────────────────────────────────────────────
  --from-literal=AI_PROVIDER="openai" \
  --from-literal=OPENAI_API_KEY="not-used" \
  --from-literal=OPENAI_BASE_URL="http://cobi-dashboard-vllmstack-engine:8000/v1" \
  --from-literal=DEFAULT_MODEL_ID="QuantTrio/Qwen3.5-9B-AWQ" \
  --from-literal=DEFAULT_FAST_MODEL_ID="QuantTrio/Qwen3.5-9B-AWQ" \
  --from-literal=DEFAULT_OCR_MODEL_ID="QuantTrio/Qwen3.5-9B-AWQ" \
  # ── Vector store (Qdrant) ────────────────────────────────────────────
  --from-literal=QDRANT_URL="http://cobi-dashboard-qdrant:6333" \
  --from-literal=QDRANT_LOCAL_ONLY="true" \
  --from-literal=QDRANT_API_KEY="" \
  --from-literal=QDRANT_CLUSTER_KEY="" \
  --from-literal=QDRANT_COLLECTION_PREFIX="prod_" \
  # ── Object storage (MinIO or AWS S3) ─────────────────────────────────
  --from-literal=S3_BUCKET="documents" \
  --from-literal=S3_REGION="us-east-1" \
  --from-literal=S3_ENDPOINT="http://cobi-dashboard-minio:9000" \
  --from-literal=S3_ACCESS_KEY_ID="admin" \
  --from-literal=S3_SECRET_ACCESS_KEY="<minio-root-password>" \
  # ── Security ─────────────────────────────────────────────────────────
  --from-literal=DATASOURCES_ENCRYPTION_KEY="<random-64-hex-chars>"
```

<Note>
  After the first successful deployment, change `ENABLE_MIGRATIONS` to `"false"` and re-apply the Secret. Running migrations on every pod restart is not safe in production.
</Note>

## Environment Variable Reference

### Database

| Variable            | Required | Description                                                                     |
| ------------------- | -------- | ------------------------------------------------------------------------------- |
| `DATABASE_URL`      | Yes      | PostgreSQL connection string for the `dashboard_auth` database (auth, sessions) |
| `CORE_DATABASE_URL` | Yes      | PostgreSQL connection string for the `cobi_core` database (application data)    |
| `ENABLE_MIGRATIONS` | Yes      | Set `"true"` on first install to run schema migrations; `"false"` after         |

Both connection strings follow the format:

```
postgres://<user>:<password>@<host>:<port>/<database>?schema=public
```

For the in-cluster PostgreSQL subchart, the host is `<release-name>-postgresql` (e.g. `cobi-dashboard-postgresql`).

### Authentication

| Variable             | Required | Description                                                                   |
| -------------------- | -------- | ----------------------------------------------------------------------------- |
| `BETTER_AUTH_SECRET` | Yes      | Secret key for signing session tokens. Generate with `openssl rand -hex 32`   |
| `BETTER_AUTH_URL`    | Yes      | Public HTTPS URL of the application — used for auth callbacks and CSRF checks |
| `FRONTEND_URL`       | Yes      | Public HTTPS URL of the frontend — used for redirect allowlists               |

`BETTER_AUTH_URL` must match the public API URL. `FRONTEND_URL` must match the public frontend URL.

### LLM Inference

| Variable                | Required | Description                                                                                    |
| ----------------------- | -------- | ---------------------------------------------------------------------------------------------- |
| `AI_PROVIDER`           | Yes      | Always `"openai"` — the backend uses the OpenAI-compatible API                                 |
| `OPENAI_BASE_URL`       | Yes      | Base URL of the vLLM inference service (e.g. `http://cobi-dashboard-vllmstack-engine:8000/v1`) |
| `OPENAI_API_KEY`        | Yes      | API key. Set to any non-empty string for self-hosted vLLM (it ignores the key by default)      |
| `DEFAULT_MODEL_ID`      | Yes      | Model ID passed in inference requests (must match the model loaded in vLLM)                    |
| `DEFAULT_FAST_MODEL_ID` | No       | Model for fast/cheap requests. Defaults to `DEFAULT_MODEL_ID` if not set                       |
| `DEFAULT_OCR_MODEL_ID`  | No       | Model for OCR document extraction. Defaults to `DEFAULT_MODEL_ID` if not set                   |

When using OpenRouter or another external provider instead of vLLM:

```bash theme={null}
--from-literal=OPENAI_BASE_URL="https://openrouter.ai/api/v1" \
--from-literal=OPENAI_API_KEY="sk-or-..." \
--from-literal=DEFAULT_MODEL_ID="qwen/qwen3.5-9b"
```

### Vector Store (Qdrant)

| Variable                   | Required    | Description                                                                                                                                  |
| -------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `QDRANT_URL`               | Yes         | HTTP URL of the Qdrant service (e.g. `http://cobi-dashboard-qdrant:6333`)                                                                    |
| `QDRANT_LOCAL_ONLY`        | Yes         | Set `"true"` for in-cluster or on-prem Qdrant; `"false"` for Qdrant Cloud                                                                    |
| `QDRANT_COLLECTION_PREFIX` | No          | String prefix for all collection names. Useful to namespace multiple environments sharing one Qdrant instance (e.g. `"prod_"`, `"staging_"`) |
| `QDRANT_API_KEY`           | Conditional | Required when using Qdrant Cloud. Leave empty for local                                                                                      |
| `QDRANT_CLUSTER_KEY`       | Conditional | Required when using Qdrant Cloud. Leave empty for local                                                                                      |

### Object Storage

| Variable               | Required                      | Description                                                                                 |
| ---------------------- | ----------------------------- | ------------------------------------------------------------------------------------------- |
| `S3_BUCKET`            | Yes                           | Bucket name for document storage                                                            |
| `S3_REGION`            | Yes                           | Signing region. Set to `"us-east-1"` for MinIO                                              |
| `S3_ENDPOINT`          | Yes for S3-compatible storage | Custom S3 endpoint. Set to `"http://cobi-dashboard-minio:9000"` when using in-cluster MinIO |
| `S3_ACCESS_KEY_ID`     | Yes for static credentials    | S3 access key ID, or MinIO `rootUser`                                                       |
| `S3_SECRET_ACCESS_KEY` | Yes for static credentials    | S3 secret access key, or MinIO `rootPassword`                                               |

<Note>
  When `backend.s3.existingSecret` is set, the Helm chart maps these generic `S3_*` Secret keys to the backend container's runtime environment variables.
</Note>

### Security

| Variable                     | Required | Description                                                                                                       |
| ---------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `DATASOURCES_ENCRYPTION_KEY` | Yes      | 64-character hex key used to encrypt stored data-source credentials at rest. Generate with `openssl rand -hex 32` |

### Observability

| Variable                       | Required | Description                                                                                       |
| ------------------------------ | -------- | ------------------------------------------------------------------------------------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT`  | No       | OTLP receiver URL (e.g. `http://cobi-dashboard-otel-lgtm:4318`). Set when otel-lgtm is enabled    |
| `OTEL_SERVICE_NAME`            | No       | Service name shown in traces (e.g. `"cobi-backend"`)                                              |
| `OTEL_SERVICE_VERSION`         | No       | Service version shown in traces                                                                   |
| `OTEL_METRIC_EXPORT_INTERVAL`  | No       | Metric export interval in milliseconds (default `5000`)                                           |
| `POSTHOG_API_KEY`              | No       | PostHog project API key for product analytics                                                     |
| `POSTHOG_HOST`                 | No       | PostHog ingest host (e.g. `https://eu.i.posthog.com`)                                             |
| `POSTHOG_PERSONAL_API_KEY`     | No       | PostHog personal API key for server-side calls                                                    |
| `LANGFUSE_PUBLIC_KEY`          | No       | Langfuse project public key for LLM tracing                                                       |
| `LANGFUSE_SECRET_KEY`          | No       | Langfuse project secret key                                                                       |
| `LANGFUSE_HOST`                | No       | Langfuse host (e.g. `https://cloud.langfuse.com`)                                                 |
| `LANGFUSE_TRACING_ENVIRONMENT` | No       | Environment label in Langfuse traces (e.g. `"production"`)                                        |
| `LANGFUSE_PROMPT_RUNTIME_MODE` | No       | `"local-only"` to load prompts from bundled files; `"langfuse"` to fetch from Langfuse at runtime |
| `LANGFUSE_PROMPT_LABEL`        | No       | Prompt version label to load from Langfuse (default `"latest"`)                                   |
| `LANGFUSE_PROMPT_BUNDLE_PATH`  | No       | File path of the bundled prompt file when `LANGFUSE_PROMPT_RUNTIME_MODE=local-only`               |

## Helm Values

Reference the Secret in your values file:

```yaml theme={null}
backend:
  enabled: true
  image:
    repository: docker.io/hellocobi/dashboard-backend
    tag: "0.2.6"
    pullPolicy: IfNotPresent
  replicaCount: 1
  service:
    type: ClusterIP
    port: 3000
  envFrom:
    - secretRef:
        name: cobi-backend-secrets
  s3:
    existingSecret: cobi-backend-secrets
  resources:
    requests:
      cpu: 250m
      memory: 512Mi
    limits:
      cpu: "1"
      memory: 1Gi
```

## Generating Secure Keys

```bash theme={null}
# BETTER_AUTH_SECRET — 32-byte hex string
openssl rand -hex 32

# DATASOURCES_ENCRYPTION_KEY — 32-byte hex string (64 chars)
openssl rand -hex 32
```
