Skip to content

Architecture

The Finch stack has two distinct planes: a data plane that carries observability signals from agents to storage, and a control plane that manages agents and configuration.


Data plane

Agents collect logs, metrics, and profiles from the hosts they run on and push them to the stack over HTTPS. Traefik authenticates each write using JWT forwarding authentication before delivering to the storage backend.

┌─────────────────────────────────────┐
│  Agent host                         │
│                                     │
│  Application ──► Alloy              │
│  systemd journal ──► Alloy          │
│  Docker containers ──► Alloy        │
│  Prometheus endpoints ──► Alloy     │
│  pprof endpoints ──► Alloy          │
└──────────────┬──────────────────────┘
               │ HTTPS  Bearer <JWT>
┌─────────────────────────────────────┐
│  Stack server                       │
│                                     │
│  Traefik :443                       │
│    /loki ──► ForwardAuth            │
│    /mimir ──► ForwardAuth    ┐      │
│    /pyroscope ──► ForwardAuth│      │
│              │               │      │
│              ▼               │      │
│       Finch :3002/auth ◄─────┘      │
│         (validate JWT)              │
│              │ 200 OK               │
│              ▼                      │
│       Loki / Mimir / Pyroscope      │
│              │                      │
│              ▼                      │
│           Grafana                   │
└─────────────────────────────────────┘

Write path (per agent request):

  1. Alloy sends a write request to /loki or /mimir, /pyroscope) with an Authorization: Bearer <token> header.
  2. Traefik intercepts the request and calls Finch's internal auth server.
  3. Finch validates the JWT signature, claims, expiry, and checks that the agent's RID still exists in the database. Returns HTTP 200 or 401.
  4. On 200, Traefik strips the path prefix forwards the request to the appropriate backend.
  5. The backend stores the data. Grafana queries backends on demand.

Control plane

finchctl communicates with Finch over gRPC, secured by mTLS. Traefik passes the client certificate to Finch, which validates it before processing any request.

┌──────────────────┐
│  Operator        │
│                  │
│  finchctl        │
│  ~/.config/      │
│  finch.json      │
│  (cert + key)    │
└──────┬───────────┘
       │ HTTPS/gRPC  mTLS client cert
┌─────────────────────────────────────┐
│  Stack server                       │
│                                     │
│  Traefik :443                       │
│    /  ──► finch-passtls middleware  │
│    (passes cert PEM as header)      │
│              │                      │
│              ▼                      │
│       Finch gRPC :3000              │
│    (validates cert, processes RPC)  │
│              │                      │
│              ▼                      │
│        SQLite database              │
│   /var/lib/finch/finch.db           │
└─────────────────────────────────────┘

gRPC request path:

  1. finchctl opens a TLS connection to port 443, presenting the client certificate stored in the client's configuration file (~/.config/finch/finch.json).
  2. Traefik extracts the client certificate PEM and adds it as the X-Forwarded-Tls-Client-Cert header.
  3. The Finch gRPC server reads the header and validates the certificate against the client-related CA file.
  4. On success, the RPC is processed and the response returned.

Dashboard

Browser access goes through Traefik to Finch's read-only dashboard. The dashboard uses a WebSocket connection for live updates.

Browser
  │ HTTPS  dashboard JWT (cookie)
Traefik :443
  /dashboard, /login, /logout, /ws
Finch HTTP :3001
  ├── REST handlers (login, logout, dashboard HTML)
  └── WebSocket /ws  (live agent events, token/config actions)

The dashboard JWT is generated by finchctl service dashboard and opened in the browser as a login URL. After login it is stored as an HttpOnly cookie and validated on every request and every 30 seconds on the WebSocket connection.


Network

All containers share a single internal bridge network named finch. Only Traefik exposes ports to the host (80 and 443). All inter-service communication uses container DNS names (loki, mimir, finch, etc.) and plain HTTP within the network.