Architecture¶
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 TDper 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 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¶
Validates architecture.json against the CALM 1.2 meta-schema. The CI
workflow calm-test.yaml
runs this on every push.
Re-render 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¶
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:
- Add a node to
architecture.jsonwith a stableunique-id. - Wire it into the right
deployed-in/composed-ofrelationship (almost alwaysrel-mgmt-cluster-contains-controllersorrel-kube-api-stores-crs). - Add a
connectsrelationship to the K8s API (every provider has one) and a secondconnectsrelationship to its backend. - If a flow now traverses the new edge, add a transition to the relevant flow.
- Run
make calm-validate && make calm-diagrams.
The full editor's guide lives at
docs/architecture/calm/README.md.