Skip to content

Beacon — Investigation Workbench

Status & scope

Status: Implemented (partial) — beacon/ Angular frontend + FastAPI backend live; pages (Home, Monitor, Explore, Insight, Edition, Inbox, Auditor) shipping. Outstanding gaps tracked in beacon/frontend/GAP_ANALYSIS.md.

Package: beacon

Depends on: platform.axonis-core, platform.service-contract, platform.ingress-routing

Milestone: P2

Purpose

Beacon is the investigation workbench: an Angular frontend served by a Python/FastAPI backend. The backend is a first-class Axonis service (conforms to platform.service-contract), not a simple proxy. It provides:

  1. Static file serving — serves the Angular frontend as an SPA
  2. MCP proxy — forwards tool calls from the frontend to the appropriate backend service
  3. Chat routing — routes chat requests to Oracle (when available) or Cortex (standalone), via the agentspace ingress
  4. Optional LLM — Beacon may have its own LLM for frontend-specific orchestration
  5. Memory access — uses axonis-core MemoryService for session memory (conversation history, preferences)

Architecture

Browser → Beacon (/api/mcp, /api/v1/chat) → agentspace.cluster.local → Oracle or Cortex
Browser → Beacon (/)                       → Angular SPA (static files)

Beacon does not detect Oracle availability at the application level. It uses the platform's ingress topology (platform.ingress-routing): all chat and MCP calls go to agentspace.cluster.local, which Traefik routes to Oracle (when deployed) or Cortex (fallback). Beacon is agnostic to which backend is active.

Package Structure

beacon/
  beacon/
    __init__.py
    config.py           # Settings: AGENTSPACE_URL, CORTEX_URL, ORACLE_URL, BEACON_LLM_*
    core/
      __init__.py
  server/
    __init__.py
    __main__.py         # Starlette: /agentspace (proxy), /api/v1, /health, /service-info, / (SPA)
    api/
      __init__.py
      routes.py         # REST API routes (/chat, /chat/history, /chat/{conv_id})
    mcp/
      __init__.py
      server.py         # Beacon's own MCP tools (if any)
      proxy.py          # MCP proxy — forwards calls to agentspace ingress
    middleware/
      __init__.py
      auth.py           # Token validation (forwards Bearer token to backend services verbatim)
  frontend/             # Angular source
    src/
    dist/               # Built output, served as static files
  charts/beacon/        # Bitnami Helm chart
  .gitlab-ci.yml
  .gitlab-ci-templates/
  Dockerfile
  pyproject.toml

Endpoints

Method Path Purpose
GET / Angular SPA (index.html)
POST /api/mcp MCP proxy → agentspace ingress
POST /api/v1/chat Chat proxy → agentspace /api/v1/chat
GET /api/v1/chat/history Memory recall via MemoryService
DELETE /api/v1/chat/{conv_id} Clear conversation (ConversationStore)
GET /health Standard health check
GET /service-info Standard service info

Static file serving

The Angular dist/ directory is mounted at /. Beacon's backend serves index.html for all unmatched paths so the Angular router handles client-side navigation. Backend API routes (/api/*, /health, /service-info) take priority over the SPA fallback.

Backend Routing

All AI calls are forwarded to the agentspace ingress (platform.ingress-routing), which resolves to Oracle or Cortex automatically:

  • MCP tool callsPOST {AGENTSPACE_URL}/agentspace/mcp
  • ChatPOST {AGENTSPACE_URL}/api/v1/chat

Direct service URLs are available for exceptional cases (admin tooling, explicit bypass):

  • Direct Cortex{CORTEX_URL} (bypasses Oracle and the agentspace ingress)
  • Direct Oracle{ORACLE_URL} (bypasses the agentspace ingress)

Beacon uses httpx.AsyncClient for all upstream calls. Bearer tokens are forwarded verbatim.

Configuration

AGENTSPACE_URL      http://agentspace.cluster.local   # Primary routing target (ingress-resolved)
CORTEX_URL          http://cortex:8002                # Direct Cortex (bypass ingress)
ORACLE_URL          http://oracle:8001                # Direct Oracle (bypass ingress)
BEACON_LLM_PROVIDER (optional)                        # LLM provider for Beacon's own use
BEACON_LLM_MODEL    (optional)
BEACON_LLM_API_KEY  (optional)

AGENTSPACE_URL defaults to http://agentspace.cluster.local in K8s. For local development (Cortex only, no K8s), set it to http://localhost:8002 to point directly at Cortex.

Multi-Agent Sessions

Beacon supports multiple concurrent agent conversations per user. Each conversation has its own conversation_id. Beacon generates a new conversation_id when none is provided, and passes it through to the backend on every request. MemoryService scoping by conversation_id prevents cross-session context contamination (see platform.axonis-core).

Example: a user may have one conversation running a broad Oracle analysis (conversation_id=A) and simultaneously working on a focused Cortex investigation (conversation_id=B). Both are active through Beacon; their memory contexts are fully isolated.

class ChatRequest(BaseModel):
    message: str
    conversation_id: str = ""   # empty = new conversation; Beacon generates a UUID if absent
    model: str = "default"

On each chat request, if conversation_id is empty, Beacon generates a UUID and returns it in the response. The frontend tracks conversation_id per session and includes it in subsequent requests.

Training dashboard

Beacon provides a workbench surface that renders live training progress for in-flight model runs. The data source is titan's training-metrics surface (component.titan.runtime#training-metrics); Beacon owns only the UI — it surfaces what titan exposes and does not compute or persist training metrics itself.

  • #REQ.training-dashboard-view — Beacon MUST provide a workbench view that displays live training progress for a run: per-run status/stage and accumulating quality metrics sourced from component.titan.runtime#REQ.training-run-metrics, refreshed while the run is in flight.
  • #REQ.training-dask-metrics-view — the view MUST surface the run's live Dask metrics (cluster/worker status, task progress, resource utilisation) sourced from component.titan.runtime#REQ.training-dask-metrics, e.g. by embedding or linking the run's Dask scheduler dashboard.

LLM Capability (Optional)

If BEACON_LLM_* is configured, Beacon may run its own LLM for frontend-specific orchestration (e.g., interpreting user intent, summarising chat history, generating suggested next steps) before forwarding to the backend. Configured via LLMSpec.from_env(prefix="BEACON_LLM") per platform.service-contract.

Falls back to simple passthrough if no LLM is configured. llm_spec.is_configured() must be checked before every LLM call.

Memory

Beacon uses MemoryService from axonis.memory.service with service="beacon". It stores conversation metadata (not the full LLM transcript — that is stored by Oracle or Cortex). Retrieval via GET /api/v1/chat/history surfaces the last N turns from the relevant conversation_id.

from axonis.memory.service import MemoryService

memory = MemoryService(service="beacon")
history = memory.recall(query="", conversation_id=conv_id, token=caller_token)

Entry Point

# server/__main__.py

from starlette.applications import Starlette
from starlette.routing import Mount, Route
from starlette.staticfiles import StaticFiles

SERVICE_NAME = "beacon"
SERVICE_VERSION = "<version>"

app = Starlette(
    routes=[
        Route("/health", health),
        Route("/service-info", service_info),
        Mount("/api/v1", app=rest_app),
        Mount("/api/mcp", app=mcp_proxy_app),
        Mount("/", app=StaticFiles(directory="frontend/dist", html=True)),
    ],
    lifespan=lifespan,
)

Dependencies

[project]
dependencies = [
    "axonis-core",
    "starlette>=0.36.0",
    "fastapi>=0.110.0",
    "uvicorn[standard]>=0.29.0",
    "httpx>=0.27.0",
]

Invariants

  1. Beacon never calls backend services from the Angular frontend directly. All calls go through Beacon's backend.
  2. Beacon does not implement domain logic. It proxies, routes, and optionally orchestrates.
  3. Bearer tokens are forwarded verbatim to backend services. Beacon does not validate, modify, or mint tokens.
  4. MCP tool calls always go to the agentspace ingress unless a specific direct URL is explicitly configured and the caller knows it is bypassing Oracle.
  5. Beacon is the only service that may serve static files (the Angular frontend).
  6. AGENTSPACE_URL must always be configurable via environment variable. Beacon must not hard-code any service hostname.

Test Expectations

  • MCP proxy tests (mock agentspace backend, verify forwarding and token passthrough)
  • Chat proxy tests (verify conversation_id generation and forwarding)
  • Multi-session isolation tests (two concurrent conversation_ids, verify no context bleed)
  • Memory tests (MemoryService recall with forwarded token)
  • Static file serving tests (SPA fallback for unmatched paths)
  • Optional LLM tests (configured and unconfigured paths)
  • Auth tests (token forwarded, not validated or modified by Beacon)
  • Health check and service-info tests

Depends on: platform.axonis-core, platform.ingress-routing, platform.service-contract

Required by: component.beacon.ticketing, component.oracle.apollo