Axel setup guide / Heimdall

Local AI infrastructure

Heimdall is one local API entry point for multiple AI backends.

Clients send HTTP requests to Heimdall. Heimdall verifies the bearer token, chooses the backend for that route, translates the request if needed, and returns the response in the format the client expects.

Purpose

Think of it as a front desk for local AI services: one HTTP API for callers, with different local workers behind each route.

SourcePrivate server checkout; exact path omitted
RoleOne authenticated local HTTP entry point
Chat backendCurrent local chat adapter
Embeddings backendPrivate FastAPI/FastEmbed worker
AuthBearer token from host-local runtime config

System Map

Heimdall sits between API clients and local runtimes. The client sees one API-like service; Heimdall handles auth, routing, backend translation, and response normalization.

What It Is

Heimdall is a compatibility layer, not a model host. A shim is a small adapter that makes one interface look like another. A router chooses which backend should handle a request based on the URL path.

In practice, Heimdall makes selected local capabilities look like one authenticated API surface. The backend selected for the route still does the actual work.

Chat

The chat endpoint accepts a familiar /v1/chat/completions request, then spawns claude -p. To the caller it looks like an HTTP model endpoint; on the server it is a local CLI job.

Embeddings

Embeddings are numeric vectors used for search, matching, and similarity. Heimdall exposes them through a familiar /v1/embeddings request shape, while FastEmbed generates the vectors locally.

The useful mental model: one stable HTTP contract, multiple local backends.

Chat Flow

Chat is the current local chat adapter. Heimdall translates a chat-completion request into a local CLI invocation and maps the result back into the response shape clients expect.

Fields That Matter Today

FieldRuntime effect
messagesUsed. System content becomes --system-prompt; user messages are joined into stdin.
streamUsed. Heimdall sends server-sent-event heartbeats and a final chunk, not live token-by-token streaming.
response_format.type = json_objectUsed. Heimdall adds JSON-only instructions and extracts a JSON object from the response.
response_format.type = json_schemaUsed. Heimdall passes a JSON schema to Claude Code and returns the structured output as message content.
heimdall.max_turnsUsed, but clamped by service policy.
heimdall.timeout_msUsed, but cannot exceed the service maximum.
model, max_tokens, temperatureAccepted for client compatibility, but not fully wired through to Claude Code behavior yet.
POST /v1/chat/completions
Authorization: Bearer <token>
Content-Type: application/json

{
  "model": "sonnet",
  "stream": false,
  "messages": [
    { "role": "system", "content": "Return concise JSON." },
    { "role": "user", "content": "Extract the key facts." }
  ],
  "response_format": { "type": "json_object" },
  "heimdall": { "max_turns": 3, "timeout_ms": 180000 }
}

Embeddings Flow

The embeddings route uses the same Heimdall front door but a different backend. Heimdall authenticates the request, then forwards the embedding body to a private FastAPI service that is not part of the public client surface.

  • Backend: FastAPI via Uvicorn.
  • Library: FastEmbed.
  • Model: BAAI/bge-small-en-v1.5.
  • Dimension: 384.
  • Default input limits: up to 32 inputs and 16000 characters per input.
Callers should treat Heimdall as the only supported entry point. Backend bindings, ports, service users, and credential file locations belong in private operator notes, not public docs.
POST /v1/embeddings
Authorization: Bearer <token>
Content-Type: application/json

{
  "model": "BAAI/bge-small-en-v1.5",
  "input": ["text to embed"]
}

Security Model

Heimdall's own security boundary starts at the Node front door. Every documented client route requires a bearer token before any backend is reached. Network exposure is handled outside the application; Heimdall's private backend topology is intentionally not published here.

Secrets

The Heimdall bearer token is loaded from a host-local env file that is not tracked in source. This page intentionally does not publish token values, credential paths, or private identifiers.

Systemd containment

Both services run under systemd with restricted write paths, no ambient capabilities, restart-on-failure behavior, and journald logging. Chat and embeddings run as separate service users.

Operations

The private Heimdall checkout tracks the Node proxy, the embeddings backend, and the systemd unit templates that keep both processes running. The public explanation stops at component roles and client contract; exact paths, ports, users, and env files stay in private operator notes.

ComponentRolePublic detail
Node front doorAuth, routing, chat adapter, metrics, embeddings proxy.Call through the documented API routes only.
Embeddings workerFastAPI embeddings backend with local model cache.Private backend behind the authenticated front door.

Operational status, logs, metrics, and restart commands are deliberately left out of the public client contract. They should live in private runbooks where the real service names and host-specific values can be used safely.

Client Contract

  • Send Authorization: Bearer <token> on every request.
  • Use POST /v1/chat/completions for the current local chat adapter.
  • Use POST /v1/embeddings for local embedding vectors.
  • Use response_format for JSON-only or JSON-schema output.
  • Use heimdall.max_turns and heimdall.timeout_ms only when a call needs tighter runtime controls.

Callers should treat Heimdall as a narrow compatibility contract around selected local backends, not as a full vendor API implementation.

Limits And Caveats

  • The current chat path is backed by a local CLI process, not a production vendor Messages API deployment.
  • Latency can be high and variable because each chat request starts a CLI process and may run for minutes.
  • Concurrency is intentionally low. The service queues requests and returns overload errors when the queue is full or times out.
  • Multi-turn chat is flattened: assistant messages are detected as a warning, but only system and user content are forwarded to claude -p.
  • Streaming is heartbeat plus final response, not true token-by-token streaming.
Treat Heimdall as shared infrastructure with a narrow contract. Add new routes, backends, and request controls deliberately, then document them in the proxy README before clients rely on them.