Modules

What a Facets module is, why it is shaped as an API, and the anatomy of a facets.yaml: types, spec, inputs, and outputs.

A module is an infrastructure building block: a database, a cache, a network, a service. You assemble your infrastructure from modules the way you build a model from Lego bricks.

What turns a piece of Terraform into a brick is its contract. Alongside the Terraform code, a module declares exactly what it takes in and gives back: a spec (the fields a developer fills in), typed inputs from other modules, and outputs other modules can read. Facets generates the Terraform from that contract. The contract is the studs on the brick, anyone can see where a module fits and what it connects to in the wider infrastructure, without reading the Terraform underneath.

Facets adds that structure on top of ordinary Terraform modules, so they become organization-level building blocks: versioned, discoverable, and safe to use across teams, even by people who do not write Terraform.

Modules are APIs, not libraries

A Terraform module is a library: it works, but you have to know how to use it. A Facets module is an API: self-contained, documented, and safe to consume even if you did not write it.

Platform teams define a module once and publish it. Developers pick a capability and set a small, focused spec, in the UI or as JSON, with no Terraform knowledge required. That works because a module is designed for clarity, not exhaustiveness: it exposes only what a developer needs to set, in terms they already use (region, instance, environment), and hides the rest.

Who writes them, who uses them

  • Platform engineers write, version, and publish modules to the registry.
  • Developers configure and use them without touching Terraform, usually through a form.

You do not start from a blank page

A new control plane has no modules. You get your first set by importing a project type, which loads the official modules and output types for your cloud. Most teams adapt those rather than author from scratch.

Module anatomy

These are the building blocks of a facets.yaml: the type system, the developer-facing spec, cross-module inputs, and outputs. For the higher-level model they fit into (Intent, Flavor, Project Type, Blueprint), see Core concepts.

╭───────────────────────────────╮        ╭───────────────────────────────╮        ╭───────────────────────────────╮
│ Module A (VPC)                │        │ Type System                   │        │ Module B (EKS)                │
│ ─ Outputs:                    │        │ - Types are contracts         │        │ ─ Inputs:                     │
│   - default:                  │──────▶ │ - Declared in facets.yaml     │◀────── │   - network:                  │
│     @facets/aws-vpc-details   │        │ - Matched exactly by name     │        │     @facets/aws-vpc-details   │
╰───────────────────────────────╯        ╰───────────────────────────────╯        ╰───────────────────────────────╯

╭────────────────────────╮
│     Developer View     │
│ ─ spec:                │
│   - region             │
│   - node_count         │
│   - schedule           │
╰────────────────────────╯
            ▲
            │
Consumed via Facets UI/API

Module A writes an output bound to a type; Module B declares an input expecting that same type. Both name the type in their facets.yaml, and the Control Plane wires them together only when the names match. The spec, shown separately, is the configuration a developer fills in, and it is unrelated to this wiring.

Types

Types are the shared contracts that let modules snap together, even when they were built independently. They define the shape and purpose of the values that flow between modules, and follow the pattern @namespace/name. @facets is the namespace the official catalogue ships under; @custom is the convention for types your organisation defines, for example @facets/aws-vpc-details, @facets/s3, or @custom/my-type.

Namespaces are matched exactly, so two types with the same name under different namespaces are not interchangeable. Older modules predating the current catalogue declare types under an @outputs namespace, which the Control Plane still accepts. Confirm the namespace a type is registered under before wiring an input to it:

raptor module get-output-type @NAMESPACE/NAME

Types are defined by module authors, stored and versioned in the Facets registry, and reused across modules to keep them compatible.

Developer inputs (spec)

The spec is the set of inputs a developer is allowed to configure. These map to familiar concepts such as region, node_count, or project_id. They are structured and validated, they power both the form UI and the JSON input, and they let a developer focus only on what matters to them. The spec is the developer-facing contract; it is unrelated to how modules wire to each other.

Inputs from other modules

A module can also accept values that come from the outputs of other modules, declared in the inputs: section. Each entry names a key (for example network) and the type it expects (for example @facets/aws-vpc-details). At deployment time, Facets connects the input to a matching output from an upstream module.

For example, a Kubernetes cluster module might require a VPC of type @facets/aws-vpc-details. Any module exposing that type, a VPC module or a landing-zone setup, can satisfy it, so modules stay reusable across environments and use cases.

Outputs

A module exposes one or more named outputs that other modules consume. Each output has a name (for example default or replication) and an output type it is bound to (for example @facets/s3 or @custom/s3_replication).

An output declares no fields of its own. The fields a consumer reads, such as attributes.vpc_cidr_block, come from the schema of the output type. That is what makes outputs interchangeable: any module exposing @facets/aws-vpc-details can satisfy an input expecting @facets/aws-vpc-details.

For example, an S3 module might expose default of type @facets/s3 and replication of type @custom/s3_replication, declared with:

raptor module add-output --name default --type @facets/s3

Outputs make a module usable by others; types make that use safe and predictable. To turn these building blocks into a working module, build one with an agent or by hand.