Documentation

Architecture

Fendlemark is four things in one binary: a control plane, a set of adapters, a reconciler we call the lattice, and an embedded console. This page explains each and how a request moves between them.

Overview

Fendlemark architecture: four adapters feed the lattice, which the console renders. MLflowtracking server Metaflowmetadata service Argo Workflowsargo server Kubeflowpipelines api fendlemark (single binary) mlflow adapter metaflow adapter argo adapter kubeflow adapter lattice facets → entities correlation reconciliation query console Vue 3, embedded views + facets /api/v1 + SSE
Adapters read from the tools you already run. The lattice merges what they report into one entity graph. The console renders the lattice and never talks to a tool directly.

The control plane

The fendlemark binary is a Go 1.23 program. On start it loads fendlemark.yaml, constructs each enabled adapter, runs an initial reconciliation, and begins serving the console and its API on server.listen (default 127.0.0.1:7411). It is stateless by design: everything in the lattice can be rebuilt from the adapters at any time, and restarts are measured in hundreds of milliseconds for platforms with tens of thousands of runs.

There is no database, no message broker, and no sidecar. State that must survive a restart (console preferences, Respiratory Identity enrolments) is written to a small on-disk store under server.data_dir. The Keepers consider the absence of a database to be a feature and have declined, across eleven Decision Records, to add one.

Adapters

An adapter is a Go package that knows how to talk to one external system and translate what it finds into facets. Fendlemark ships four, compiled into the binary:

AdapterReads fromFacets contributed
mlflowMLflow tracking server REST API (/api/2.0/mlflow/...)Experiment, Run, Metric series, Param set, Artifact listing, Registered Model, Model Version
metaflowMetaflow metadata service; datastore for artifact linksFlow, Run, Step, Task, Artifact listing, Tags
argoArgo Server REST API; optional direct Kubernetes watchWorkflow, Node (step), WorkflowTemplate, CronWorkflow, Pod logs link
kubeflowKubeflow Pipelines API; Katib and Notebooks via the Kubernetes APIPipeline, Pipeline Run, Recurring Run, Katib Experiment, Notebook Server

Adapters are read-only. Fendlemark does not create, modify, or delete anything in the systems it integrates. This is a deliberate boundary: the console is a place to understand your platform, and the tools remain the place to operate it. See the adapters reference for what each adapter surfaces and what it leaves alone, and Writing an adapter for the interface.

The lattice

The lattice is Fendlemark's in-memory entity graph. It exists because the same real-world thing shows up in several tools under several names. A training job might be an Argo Workflow, whose step launched a Metaflow Run, which logged an MLflow Run, whose best checkpoint became an MLflow Model Version. Four tools, four identifiers, one thing.

The lattice has three concepts:

Correlation keys

Reconciliation is deterministic and rule-based. There is no fuzzy matching and no machine learning in the reconciler, a decision the Keepers describe as "the only responsible choice for software whose job is to be believed". Each adapter declares the correlation keys it can provide, and the reconciler matches on exact equality within a configurable window (lattice.correlation.window, default 90s). Built-in keys include:

KeyProvided byMatches
argo.workflow.nameargo; mlflow (tag fendlemark.argo_workflow or argo.workflow.name)An MLflow run to the Argo workflow that produced it
metaflow.run_idmetaflow; mlflow (tag metaflow.run_id); kubeflow (pipeline parameter)A Metaflow run to MLflow runs it logged and KFP runs that launched it
kfp.run_idkubeflow; argo (label pipeline/runid on the underlying Workflow)A Kubeflow Pipelines run to its Argo Workflow
mlflow.run_idmlflow; any adapter that records it as a tag, label, or parameterAnything back to the MLflow run
git.shaall adapters where availableWeak key; links entities only when both also share git.repo

Kubeflow Pipelines runs on Argo Workflows by default, so a KFP run and its Workflow are nearly always correlated on first reconciliation. The other links depend on your pipelines recording identifiers across tool boundaries, which most teams already do with tags. The adapters reference lists the tag and label names each adapter looks for.

Reconciliation

Reconciliation runs in full on start and incrementally thereafter. Adapters that support watching (argo, kubeflow) push events; adapters that do not (mlflow, metaflow) are polled on adapters.<name>.poll_interval. Each cycle is a pure function from the set of facets to the set of entities and correlations, so two Fendlemark instances with the same adapters produce the same lattice. The console shows the lattice's generation number in its footer; if two people see different numbers, one of them should refresh.

The console

The console is a Vue 3 single-page application built with Vite, embedded into the Go binary at build time with embed. It talks to the control plane over /api/v1 (JSON) for queries and /api/v1/events (server-sent events) for live updates. It never talks to MLflow, Metaflow, Argo, or Kubeflow directly; everything goes through the lattice.

The console is organised into views, each of which renders entities of a kind and composes facet panels contributed by adapters. The Run view, for instance, shows the MLflow metrics panel, the Argo node tree panel, and the Metaflow task panel for a single correlated run, with the panels for tools that did not contribute facets simply absent. See The console for the component library and theming.

Life of a request

  1. A user opens /runs/lt_8f2c… in the console.
  2. The console requests GET /api/v1/entities/lt_8f2c….
  3. The control plane answers from the lattice: the entity, its facets, and its correlated neighbours. No adapter is called; the response is served from memory in under a millisecond.
  4. The console subscribes to /api/v1/events?entity=lt_8f2c…. When the argo adapter's watch sees the underlying Workflow change phase, the reconciler updates the entity and an event is pushed. The view updates in place.
  5. The user clicks "Open in MLflow". The console deep-links to the tracking server's own UI for that run. Fendlemark shows; the tools operate.

Non-goals