Skip to content

Anatomy of the control cluster

The control cluster is where Flui itself runs. It also holds the observability backend that every other cluster sends data to, and the few platform pieces every Flui cluster needs to work. This chapter goes through those components by role.

The components below are installed with the control cluster and belong only to it, except for the baseline ones (certificates, ingress, in-cluster DNS) that every cluster carries. The cluster’s Overview shows the health of each.

Control cluster Flui Database Queue Identity Dashboard API Observability Metrics Logs Alert rules Alert router On every cluster, ingress included Certificates Cluster DNS Storage Metrics agents Ingress You browser, CLI, agents HTTPS Workload clusters metrics and logs private network reachable from outside, through the ingress internal to the cluster
What runs on the control cluster. Only the API, the dashboard and the identity provider are reachable from outside; workload clusters send their telemetry over the private network.

The platform itself

These components make this cluster the Flui control plane. None of them appear on workload clusters.

Flui API

The service every CLI command, dashboard request and third-party integration talks to. It runs on the cluster it controls. There is no cloud-hosted Flui API: the API that operates your installation is the one you installed. It is reached at a public hostname through the cluster’s ingress.

Not replaceable: it is the control plane.

Flui dashboard

The web interface of the installation: cluster lists, application views, monitoring, deploy flows, identity, and the panel where you answer an agent.

The dashboard and the CLI both call the Flui API, and for most work either one will do. They differ in a few places. The control cluster is created from the CLI or from app.flui.cloud, never from the dashboard, because the dashboard does not exist until that cluster is up. An agent’s request for permission is answered in the dashboard, or against the API directly; there is no flui command for it.

Identity provider (Zitadel)

The OIDC authority for the installation. It issues the credentials the CLI uses and the tokens the dashboard uses, and federates with external identity providers when configured. Every installation has one.

Any OIDC-compliant identity provider could replace it in principle, but Zitadel is the one Flui ships and tests.

Database

The installation’s state lives here: clusters, nodes, applications, releases, builds, operations, encrypted credentials, DNS zones, certificate metadata. Logs and metrics are kept in the observability backend below instead.

It also holds two records about agents rather than infrastructure: the action-cycle store and the agent activity register. Both have their own section further down.

PostgreSQL is the engine Flui ships and tests against.

Cache and queue backend

Every operation Flui drives (provisioning, deploys, builds, lifecycle changes) goes through a job queue. The same component also serves the caches the API uses to keep small pieces of state fast. Redis is the engine Flui ships and tests against.

What the control cluster keeps for agents

Two records in the database above are about agents. Neither is a service you can point at. They are listed here because, besides the state of your infrastructure, the control cluster holds the record of what your agents asked for and what they did.

The action-cycle store

When an agent asks for something that changes the installation, the call does not run. It becomes a question waiting for a person. Nothing holds a connection open while somebody decides, so the question is written down here.

It holds two kinds of row:

  • Pending requests. What was asked for, in one sentence, under which credential, and, when the action costs money, the price at the time of asking. A request expires after an hour, so nobody is offered a stale quote.
  • Standing permissions. The “always” answers. Each one is tied to a specific action, a specific resource, and the single credential that asked. Each one can be taken back.

None of this grants anything. A standing permission only removes a pause on something the person’s own access already allowed. The reasoning is in The action cycle.

The agent activity register

Every call an agent makes leaves a row here. The register belongs to the credential, not to any one surface: a call through the agent tools, a turn of the in-product assistant, and a plain HTTP call made with curl all land in the same place. An agent key presented straight to the API meets the same guards as the tool surface and leaves the same trace.

A row records which credential acted, whether the call was allowed, how the call ended, and the long-running operation it started, if any. Arguments are redacted: values that come from a fixed set of choices are stored as they were, and anything else is withheld.

Reading it needs no special access. Anyone can read their own rows, since you cannot decide whether to keep an agent connected without seeing what it has been doing. Someone who administers access reads the whole installation. Somebody else’s row reads as absent, not as forbidden, because a register that answers “you may not see this” has already confirmed that the hidden call took place.

The observability backend

These components make this cluster the place where every other cluster sends its telemetry. The stores and the alert components run on the control cluster only. The agents that feed them run on every cluster, the control cluster included.

Metrics store

A time-series database that receives the metrics every cluster pushes. It exposes a query API in a well-known dialect, which the CLI and the dashboard read from when they show app metrics. It runs as a single instance.

Log store

The central destination for application logs from every cluster. It indexes them by label and serves a query interface that both the dashboard and the CLI use through the Flui API.

Metrics scraper

The agent that collects metrics from cluster objects and applications and pushes them to the metrics store. It runs on every cluster, including the control cluster, so the control cluster monitors itself with the same tools it uses on every workload cluster.

Alert evaluator

Watches the metrics store continuously and turns raw data into signals. It fires alerts when a metric stays past a defined threshold for long enough. It also pre-computes heavier queries (rolling averages, per-app aggregates) so the dashboard can read them from the store instead of recalculating each time. A baseline set of alerts and aggregates ships predefined.

Alert router

Receives every alert the evaluator fires and hands it to the Flui API, which decides who hears about it: a notice in the dashboard, an email, or a destination somebody added. See Alerts are episodes, not messages.

Object metrics exporter

Turns the state of cluster objects (deployments, pods, services) into metrics that the scraper picks up. Like the scraper, it runs on every cluster. It provides the per-app baseline metrics (replica counts, restart counts, health) the dashboard shows for every application.

The cluster baseline

These run on every Flui cluster, control and workload. They are listed here because the control cluster needs them too, but the full discussion is in the cluster chapters.

Ingress

The L7 entry point of the cluster. It terminates TLS, routes requests by hostname and path, and exposes every public surface the cluster offers. On the control cluster that includes the Flui API, the dashboard and the identity provider.

Certificate lifecycle

Issues and renews TLS certificates for every public hostname the cluster serves, against the configured ACME endpoint (production or staging; see TLS and certificates). Where the zone sits on a provider whose DNS Flui can drive (see which), an extension lets the issuer solve DNS-based challenges through it.

In-cluster DNS

Service discovery inside the cluster. It ships with the cluster runtime and Flui uses it unchanged.

Short-lived metrics source

The per-node and per-pod CPU and memory readings that some in-cluster autoscaling decisions read from. It is separate from the observability backend above: it is the cluster runtime’s own short-window source, not where Flui’s long-term metrics live.

Default storage

A baseline storage class for cluster workloads. On a cluster with shared storage enabled, applications can choose the shared volume or a node-local fallback. On a cluster without shared storage, the fallback is the only option.

Optional cluster-side authentication gate

A small service the ingress asks before letting a request through. It checks the Flui sign-in with the installation’s API. On the control cluster the API answers this itself. On a workload cluster it is an optional relay, flui-authz. With it enabled, any endpoint on the cluster can be put behind OIDC login without the application handling authentication itself.

Flui uses a dedicated subdomain for this: an endpoint published under <app>.internal.<cluster-zone> is treated as gated and goes through the authentication check automatically, while the same app on its plain <app>.<cluster-zone> endpoint stays public. See DNS integration for details.

On a workload cluster it is off by default because it adds an extra service on every request path, and that cost only pays off on clusters that need gated endpoints. Clusters that don’t need it stay lighter. It is specific to Flui and cannot be replaced.

What is user-facing, what is internal

The cluster exposes a small public surface and keeps everything else internal.

  • Public, through the cluster’s ingress: the Flui API, the dashboard and the identity provider. These are the addresses you put in a browser or call from the CLI.
  • Cluster-internal: the database, the cache/queue, the metrics store, the log store and their agents. They are reachable only from inside the cluster, with two exceptions. The metrics and log stores also accept connections from other clusters over the private network, so those clusters can send their telemetry. For development, the CLI can open a temporary tunnel against the master.

The public surface is what you build on. The internals can change underneath without changing what you can call from outside.

Where this chapter goes from here