Skip to main content
Every ubx project has configuration — the runtime, the default environment, the project name, shared tags, AI feature flags, security scan settings. Traditionally this lived in ubx.yaml, a YAML file separate from your .iac source. The ubx { } block is an equal alternative: the same configuration, expressed in .iac syntax alongside your resources. The advantage isn’t just aesthetics — the ubx { } block unlocks capabilities that YAML can’t express. You can reference resource outputs inside it, making scan.enabled a Pending<T> value that resolves from a feature flag stored in SSM Parameter Store. You can use conditional expressions to vary settings by environment. You can use extend "ubx" { } in an environment override file to change AI settings or scan severity in prod without touching the base config. Config as code, not config as YAML.

What you’ll learn

  • How to express all project config in a ubx { } block instead of ubx.yaml
  • The sub-blocks: tags, ai, scan, history, telemetry
  • How scan.enabled can be a Pending<T> value referencing a resource output

Why this matters

The ubx { } block and ubx.yaml are permanent equals — neither is deprecated. Choose the one that fits your project. Use ubx { } when you want config close to your resources, conditional config based on inputs, or pending resource references in config values. Use ubx.yaml when you prefer explicit YAML or when your config is completely static.

The source code

# ubx {} provides all project config — no ubx.yaml required.
ubx {
  name        = "ubx-block-config-as-code"
  runtime     = "typescript"
  default_env = "dev"
  version     = ">= 0.1.0"
  stacks      = ["dev", "staging", "prod"]

  tags {
    project = "myapp"
    env     = "${input.env}"   # string interpolation in tags
  }

  ai {
    enabled  = true
    model    = "claude-haiku-4-5-20251001"
    features = ["plan_summary", "cost_estimate"]
  }

  scan {
    # scan.enabled can be Pending<T> — evaluated at plan time.
    # The feature flag value in SSM controls whether scanning runs.
    enabled  = unit.aws_ssm_parameter.feature_flags.value
    severity = "high"
  }

  history {
    enabled = true
  }
}

unit "aws_ssm_parameter" "feature_flags" {
  name  = "/myapp/${input.env}/scan-enabled"
  type  = "String"
  value = "true"
}

unit "aws_s3_bucket_v2" "store" {
  bucket = "myapp-${input.env}-store"
}

How it works

1

The ubx block is parsed and merged into Viper config

During initConfig(), ubx reads the ubx { } block from .iac files alongside ubx.yaml (if present). The .iac block wins on any overlapping keys. Both sources are active simultaneously — this is the merge, not a replacement.
2

Resolved attributes are applied immediately

runtime, default_env, version, name, and literal tag values are Resolved<T> — they’re applied to the Viper config immediately. The stack validates against these resolved values as if they came from ubx.yaml.
3

Pending attributes are deferred to plan time

scan.enabled = unit.aws_ssm_parameter.feature_flags.value is Pending<T>. ubx stores this in PendingFields and resolves it at plan time — after the SSM parameter exists and has a value. Until then, scan uses a safe default.

ubx block sub-blocks reference

Sub-blockPurpose
tags { }Shared tags applied to all resources; supports resource output refs and interpolation
ai { }AI feature flags: enabled, model, features
scan { }Security scan config: enabled (can be Pending<T>), severity
history { }Apply history: enabled
telemetry { }Opt-in telemetry: enabled, endpoint

Common mistakes

name, stacks, and backend have no equivalent in the ubx { } block yet — these must still be declared in ubx.yaml or the corresponding .iac blocks (backend "s3" { } for backend). This is a known gap being addressed in a future release.
Use extend "ubx" { } in an environment override file to change ubx { } sub-block values per environment. For example, envs/prod/overrides.iac can set scan { severity = "critical" } to tighten scan severity in prod without changing the base config.

Run it

ubx validate   # ubx block is parsed and validated
ubx plan       # Pending scan.enabled resolved at plan time

What you learned

The ubx { } block is a full alternative to ubx.yaml — both are permanently supported
Sub-blocks tags, ai, scan, history, telemetry cover all project-level settings
Pending<T> values in config (like scan.enabled) are deferred to plan time

Next steps

ubx block vs ubx.yaml merge

What happens when both formats are present simultaneously

ubx block reference

Full ubx block syntax and all sub-blocks