Module anatomy
This section explains the building blocks of a Facets module. You’ll learn how modules declare what they offer, what they require, and how they connect with others using a shared type system.
This section introduces the foundational constructs that define how modules work in Facets. These concepts make it easier to build modular, composable infrastructure by standardizing how configuration and connections are defined.
We’ll start with the most fundamental building block: types, and then explore how they apply to inputs, outputs, and wiring.
🧭 Module Anatomy Overview
╭───────────────────────────────╮ ╭───────────────────────────────╮ ╭───────────────────────────────╮
│ 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/APIModule A writes an output bound to a type; Module B declares an input expecting that same type. Both sides name the type in their facets.yaml, and the Control Plane wires them together only when the names match. The spec, shown separately above, is the configuration a developer fills in, and it is unrelated to this wiring.
🧬 Types
Types define the shape and purpose of values that flow between modules. They follow the pattern @namespace/name. @facets is the namespace the official module catalogue ships under; @custom is the conventional namespace for types your organisation defines:
@facets/kubernetes-details,@facets/aws-vpc-details,@facets/s3,@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 actually registered under before wiring an input to it:
raptor module get-output-type @NAMESPACE/NAMETypes are:
- Defined by module authors or organizations
- Stored and versioned in the Facets registry
- Reused across modules to ensure compatibility
Think of types as shared contracts. They let modules snap together, even if they were developed independently.
📊 Developer Inputs (spec)
Each module defines a spec, which represents the set of inputs that a developer is allowed to configure. These inputs map to familiar concepts in your organization such as region, node_count, or project_id.
These values:
- Are structured and validated
- Power the form-based UI and JSON input options
- Allow developers to focus only on what they need to configure
The
specdefines the developer-facing contract. It is used in the Facets UI and API to provide a clean configuration experience.
🧩 Inputs from Other Modules
Modules can also accept values that come from the outputs of other modules. These are defined in the inputs: section of the module's definition.
Each entry in the inputs: section defines:
- A key name (e.g.,
network) - The expected type (e.g.,
@facets/aws-vpc-details)
At deployment time, Facets connects these inputs to outputs from upstream modules of the same type.
Example:
- A Kubernetes cluster module may require a VPC with
@facets/aws-vpc-details - That input could be fulfilled by any module exposing that type, such as a VPC module or a landing zone setup
This model allows modules to be reused flexibly across environments and use cases.
📤 Outputs
Modules expose one or more named outputs that can be consumed by other modules. Each output has:
- A name (e.g.,
default,replication) - An output type it is bound to (e.g.,
@facets/s3,@custom/s3_replication)
An output does not declare fields of its own. The fields a consumer can read, such as attributes.vpc_cidr_block, come from the schema of the output type the output is bound to. That is what makes outputs interchangeable: any module exposing @facets/aws-vpc-details can satisfy an input expecting @facets/aws-vpc-details.
Example:
An S3 module might expose:
default, of type@facets/s3replication, of type@custom/s3_replication
Declared with:
raptor module add-output --name default --type @facets/s3Outputs make a module usable by others. Types make that usage safe and predictable.
In the next section, we’ll walk through how to structure and build a Facets module using these principles. Coding specifics will be covered separately.
Introduction
How Facets modules turn Terraform into reusable, versioned capabilities with typed inputs and outputs, so developers can self-serve infrastructure.
Building a Facets Module
This section explains how to turn a module concept into a working Facets module. You'll define the module interface using `facets.yaml`, connect it to Terraform logic, and use the Facets CLI to scaffold and validate your work.