# LangFuse Self-Hosted Setup (Prague T1a)

**Status:** Operational runbook — Ben's in-person checklist
**Date:** 2026-04-20

LangFuse is the observability backend the classroom uses for per-turn
traces (prompts, retrievals, scores). Axiom's tracing layer
auto-wires LangFuse when specific env vars are present — no code
change is needed to enable or disable it.

## Node-operator checklist

1. **Deploy LangFuse on Rascal** (or any always-on node):
   ```bash
   # Simplest: Docker Compose, PG-backed
   git clone https://github.com/langfuse/langfuse.git
   cd langfuse
   docker compose up -d
   ```
   Default listens on `:3000`. Share PG with existing Rascal Postgres
   by editing `docker-compose.yml` and pointing `DATABASE_URL` at the
   shared instance.

2. **Create a project** in the LangFuse UI (http://rascal.local:3000).
   Copy the public key + secret key from Settings → API Keys.

3. **Set env vars on every Axiom node** that should emit traces
   (Rascal, Prague EU node, instructor laptops):
   ```bash
   export LANGFUSE_PUBLIC_KEY="pk-lf-..."
   export LANGFUSE_SECRET_KEY="sk-lf-..."
   export LANGFUSE_HOST="https://langfuse.rascal.internal:3000"
   # Optional — force off at a site even when keys are present:
   # export AXIOM_TRACE_BACKEND=null
   ```

4. **Verify** on any classroom node:
   ```bash
   python -c "
   from axiom.infra.tracing.env import build_trace_provider_from_env
   p = build_trace_provider_from_env()
   print(type(p).__name__)
   "
   # Expected: LangfuseTraceProvider
   ```

## What Axiom auto-wires

Code reaches tracing via one entry point:

- ``axiom.extensions.builtins.classroom.composition_boot.build_classroom_tracer(classroom_id, course_id, composition)``
  — returns a ``ClassroomTracer`` with the env-configured provider.

Every classroom caller that uses this helper gets LangFuse traces for
free when the env vars are set.

## Auto-detection precedence

1. ``AXIOM_TRACE_BACKEND`` — explicit backend override (``null``,
   ``in_memory``, ``langfuse``). Wins over keys-based detection.
2. Both ``LANGFUSE_PUBLIC_KEY`` AND ``LANGFUSE_SECRET_KEY`` present →
   LangFuse backend selected.
3. Otherwise → null (no-op).

Partial LangFuse keys (one set, one missing) → null, with no error.
This prevents a half-configured node from crashing mid-trace-emit.

## GDPR decision pending

Prague EU students' trace data: currently flows to Rascal's LangFuse
(Austin). If the data-residency review determines EU student traces
must stay in EU, the Prague node should run its own LangFuse instance
and set different env vars per node. The two-provider topology is
supported by the existing factory — just deploy a second LangFuse
and update the Prague node's env.

See ``project_prague_deployment_requirements`` memory for the broader
Prague context.
_Copyright (c) 2026 The University of Texas at Austin and B-Tree Labs. Apache-2.0 licensed._
