Skip to content

Architecture

FINOS CALM

banlieue describes its own architecture as code, in the FINOS Common Architecture Language Model (CALM). The model lives at docs/architecture/calm/architecture.json and the diagrams under this section are rendered from it by the CALM CLI — they are never hand-edited.

Why CALM

A README that says "the controller talks to the K8s API" is unverifiable. A CALM document encodes the same claim as structured data: there is a relationship of type connects from service-banlieue-controller to service-kubernetes-api, and the protocol is HTTPS. The renderer turns that into a Mermaid diagram; the validator turns it into a build failure when the model and the reality diverge.

The model also encodes our non-negotiables as controls (see below), so a reviewer can confirm at a glance that the architecture-as-code still matches the project's stated principles.

What's in the model

Section Count Examples
Nodes 33 actors, the management cluster, the controllers, every CRD, each backend, the OIDC issuer
Relationships 30 every wire (connects), every container (composed-of), every consumer (interacts)
Flows 10 create / swap / delete a VirtualMachine, provision a CAPI cluster, build and import a VMImage, register and upgrade a backend, claim a pool member, deliver a credential
Controls 7 top-level + 22 scoped CRD-only contract, code-first CRDs, admission-policy validation, least-privilege secret access, supply-chain scanning — plus controls attached to individual nodes, relationships and flows

Each provider node carries its development phase in its display name ((planned, Phase 1B), etc.) so the picture is honest about what's implemented today vs the target architecture.

Rendered artefacts

  • System Diagram — every node and the wires between them, in a single Mermaid flowchart LR.
  • Architecture Flows — one Mermaid flowchart TD per business flow, with each step labelled. A flow whose name says PROPOSED is modelled but not implemented: CALM is updated when the decision is recorded, not when the code lands, which is the point of ADR → CALM → TDD.

Both pages are regenerated by:

make calm-diagrams

make docs runs calm-diagrams first, so the published site is always in sync with the CALM model on disk.

Controls modelled in CALM

The CALM controls block is where banlieue's invariants are encoded as machine-readable requirements with links to evidence files and NIST references. The top-level controls are below; further controls are attached to individual nodes (crd-only-contract, least-privilege), relationships (tls-trust-byoc, libvirt-mutual-tls) and flows (finalizer-ordering, claim-subject-attribution, bind-once-optimistic-concurrency, no-credential-at-rest), where they are scoped to the thing they actually constrain.

Control What it asserts Evidence
crd-only-contract The controller MUST NOT speak RPC, REST, or gRPC to providers — only CRDs via the K8s API. CLAUDE.md, reasoning/crd-only-contract.md — NIST SP 800-53 SC-7, AC-4
code-first-crds All CRD YAML is generated from the Rust type system in banlieue-api via the crdgen binary; no hand-edited YAML. crates/banlieue-api/src/bin/crdgen.rs — NIST SP 800-218 SSDF PW.4, PS.1
capi-v1beta2-infra-machine-contract Every provider's infrastructure CRD satisfies the upstream Cluster API v1beta2 InfraMachine contract. crates/banlieue-api/src/infrastructure/vsphere_machine.rs, concepts/infra-crds-capi.md — CAPI v1beta2 InfraMachine
supply-chain-scanning CodeQL, cargo-deny, OpenSSF Scorecard, and SPDX headers on every source file. .github/workflows/, deny.toml — NIST SP 800-218 SSDF PO.5, PS.1, PW.4
capi-v1beta2-infra-cluster-contract Every provider's infrastructure cluster CRD satisfies the CAPI v1beta2 InfraCluster contract, including failure-domain aggregation. crates/banlieue-api/src/infrastructure/, ADR-0002 — CAPI v1beta2 InfraCluster
admission-policy-validation Invariants a CRD schema cannot express are enforced in the API server with CEL, never a webhook: reference immutability, connection hardening, credentialsRef and userData authorization, and VirtualMachineClaim subject attribution. deploy/admission/, ADR-0007, ADR-0042, ADR-0047 — NIST SP 800-218 SSDF PW.5, RV.1
secret-access-least-privilege No shared role grants Secret access: the operator builds a per-instance Role scoped by resourceNames to the objects one Provider actually names. ADR-0003, ADR-0041 — NIST SP 800-53 AC-6, IA-5
release-artifact-provenance Every released binary and image is signed, SBOM'd and scanned before publication. .github/workflows/, ADR-0006 — NIST SP 800-218 SSDF PS.2, PS.3

Working with the model

Validate

make calm-validate

Validates architecture.json against the CALM 1.2 meta-schema. The CI workflow calm-test.yaml runs this on every push.

Re-render diagrams

make calm-diagrams

Re-runs the Handlebars templates in docs/architecture/calm/templates/mermaid/ and writes the output back into this directory.

Generate a richer doc site

make calm-docify

Uses calm docify (instead of calm template) and accepts the same template directory. The two commands produce equivalent output today; the docify form is reserved for future template bundles that emit multi-page documentation sites.

Add a new node, relationship, or flow

When you ship a new provider, subsystem, or backend:

  1. Add a node to architecture.json with a stable unique-id.
  2. Wire it into the right deployed-in / composed-of relationship (almost always rel-mgmt-cluster-contains-controllers or rel-kube-api-stores-crs).
  3. Add a connects relationship to the K8s API (every provider has one) and a second connects relationship to its backend.
  4. If a flow now traverses the new edge, add a transition to the relevant flow.
  5. Run make calm-validate && make calm-diagrams.

The full editor's guide lives at docs/architecture/calm/README.md.