Workspaces
What’s a workspace?
Section titled “What’s a workspace?”A workspace is a directory containing OpenTofu (or Terraform) files. Yaffle runs plan and apply independently for each workspace.
[[workspaces]]path = "infra/network"environments = ["*"]
[[workspaces]]path = "infra/compute"environments = ["*"]Each workspace gets its own state file, runs independently, and can have its own variables.
Multiple workspaces
Section titled “Multiple workspaces”Most projects have multiple workspaces:
repo/├── infra/│ ├── network/ # VPC, subnets, security groups│ ├── database/ # RDS, ElastiCache│ └── compute/ # ECS, Lambda└── yaffle.toml[[workspaces]]path = "infra/network"environments = ["*"]
[[workspaces]]path = "infra/database"environments = ["*"]
[[workspaces]]path = "infra/compute"environments = ["*"]Dependencies
Section titled “Dependencies”Workspaces often depend on each other. Your compute layer needs outputs from your network layer.
Yaffle automatically detects dependencies by scanning your OpenTofu files for module references to other workspaces. When workspace A depends on workspace B:
- B runs first
- B’s outputs become available as a module
- A runs and can consume B’s outputs
Using outputs from other workspaces
Section titled “Using outputs from other workspaces”Reference another workspace’s outputs using the Yaffle module registry:
module "network" { source = "yaffle.dev/acme--platform/infra--network/yaffle"}
resource "aws_ecs_service" "app" { # Use outputs from the network workspace network_configuration { subnets = module.network.private_subnet_ids security_groups = [module.network.app_security_group_id] }}The module source follows this pattern:
yaffle.dev/YAFFLE-ORG--REPO/WORKSPACE--PATH/yaffleWhere:
YAFFLE-ORG--REPOidentifies the producer repositoryWORKSPACE--PATHuses--as the path separator
Examples:
infra/network→infra--networkapps/web/infra→apps--web--infra
Internal vs public outputs
Section titled “Internal vs public outputs”A workspace can expose two different output surfaces:
internaloutputs are available only to downstream workspaces in the same repopublicoutputs are available to explicitly allowlisted workspaces in other repos in the same Yaffle org
Outputs are internal by default. You only need to configure the outputs you want to make public.
[[workspaces]]path = "platform/eks"environments = ["main"]
outputs.cluster_endpoint = { visibility = "public", consumers = ["applications/apps/*"] }outputs.cluster_ca = { visibility = "public", consumers = ["applications/apps/*"] }This means:
- workspaces in the same repo can still consume the full module surface
- allowlisted cross-repo consumers only see the
publicoutputs - cross-org module sharing is not supported
Intra-repo vs cross-repo modules
Section titled “Intra-repo vs cross-repo modules”Yaffle treats module sources differently depending on which repo they point at.
# Same repo -> included in the DAGmodule "shared" { source = "yaffle.dev/acme--applications/infra--shared/yaffle"}
# Different repo in the same Yaffle org -> external module, not a DAG edgemodule "cluster" { source = "yaffle.dev/acme--platform/platform--eks/yaffle"}Rules:
- same-repo modules participate in dependency inference, ordering, and blast-radius analysis
- cross-repo modules resolve through the registry but do not become DAG edges in the current repo
- cross-org module sharing is denied
Execution order
Section titled “Execution order”Yaffle builds a dependency graph and executes workspaces in the right order:
Root workspaces (no dependencies) run first, in parallel. Downstream workspaces wait for their dependencies to complete.
Run lifecycle and approval
Section titled “Run lifecycle and approval”When you open a PR or push to a branch, Yaffle executes runs through several stages:
| Status | Description |
|---|---|
pending | Run is queued, waiting for a runner |
planning | OpenTofu plan is executing |
waiting_approval | Plan complete, awaiting human approval |
applying | Apply in progress (after approval) |
success | Run completed successfully |
failed | Run failed with error |
cancelled | Run was cancelled or rejected |
Workspace variables
Section titled “Workspace variables”Set variables per workspace:
[[workspaces]]path = "infra/compute"environments = ["*"]variables.instance_type = "t3.medium"variables.desired_count = 2Use template variables for environment-specific values:
[[workspaces]]path = "infra/compute"environments = ["*"]variables.environment = "{{ environment }}"variables.domain = "{{ environment }}.example.com"Environment targeting
Section titled “Environment targeting”Control which environments a workspace runs in:
# Runs in all environments (including previews)[[workspaces]]path = "infra/network"environments = ["*"]
# Only production[[workspaces]]path = "infra/production-only"environments = ["production"]
# Only named environments (not previews)[[workspaces]]path = "infra/staging-and-prod"environments = ["staging", "production"]