> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ubiquex.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> How ubx reads configuration — the resolution chain, what goes where, environment overrides, and migration from ubx.yaml.

ubx reads all stack configuration from `.xcl` files. There is no `ubx.yaml` for XCL stacks — engine, backend, AI settings, scan settings, and history settings are all declared in the XCL source.

## The resolution chain

Every configuration field is resolved through a four-level chain. Lower levels set defaults; higher levels override.

```
1. workspace {}         ← universal defaults for all stacks
2. workspace { match {} }  ← workspace scoped to matching stacks
3. context {}           ← named group a stack opts into
4. meta {} inside stack {}  ← per-stack overrides (highest priority)
```

Fields that are not additive (engine, runtime, backend type, AI model, scan severity) follow **last-wins**: the most specific block that declares the field wins. Fields that are additive (tags, hooks) are **merged**: all levels contribute.

A stack that declares no `meta {}` and belongs to no `context {}` receives only the universal `workspace {}` values, or ubx's built-in defaults if no workspace block is present.

## What goes where

### workspace {} — workspace-wide defaults

Use `workspace {}` for settings that apply to every stack in the compilation unit unless a more specific block overrides them.

```xcl theme={"theme":"css-variables"}
workspace {
    engine  = "native"
    runtime = "xcl"

    backend s3 {
        bucket = "my-state-bucket"
        key    = "platform"
    }

    ai {
        enabled = true
        model   = "claude-haiku-4-5"
    }

    scan {
        enabled  = true
        severity = "high"
    }

    history {
        enabled = true
    }

    tags {
        project    = "platform"
        managed_by = "ubx"
    }
}
```

A `workspace {}` block with a `match {}` sub-block applies only to stacks whose effective labels match. A universal `workspace {}` (no `match {}`) applies first; a label-scoped `workspace {}` overlays it.

### context {} — group configuration

Use `context {}` to declare a named configuration group that multiple stacks opt into. A context sits between the workspace and per-stack overrides in the resolution chain.

```xcl theme={"theme":"css-variables"}
context prod_eu {
    labels {
        env    = "prod"
        region = "eu-west-1"
    }
    backend s3 {
        bucket = "state-eu-prod"
        key    = "platform/prod"
    }
    scan {
        severity = "critical"
    }
}
```

A stack opts in via `meta { context = "prod_eu" }`. All fields declared in the context override the workspace for that stack.

### meta {} inside stack {} — per-stack overrides

Use `meta {}` for settings specific to a single stack. `meta {}` is the highest priority in the chain — it overrides everything.

```xcl theme={"theme":"css-variables"}
stack "payments" {
    meta {
        context = "prod_eu"
        ai {
            enabled = false
        }
    }

    // stack body...
}
```

`meta {}` can both opt the stack into a `context {}` and declare per-stack field overrides. Fields declared directly in `meta {}` override the context.

## The one exception: API keys

API keys are secrets, not source configuration. The Anthropic API key is never read from `.xcl` files.

Set it via environment variable:

```sh theme={"theme":"css-variables"}
export UBX_AI_API_KEY="sk-ant-..."
```

Or via `ai.api_key` in `ubx.yaml` (deprecated path — supported for backward compatibility, but prefer the env var). The env var always takes priority.

AI features are enabled when `UBX_AI_API_KEY` is set **and** the workspace `ai { enabled = true }` is declared (or no workspace block disables it). If the API key is absent, AI features are silently skipped regardless of what the workspace block declares.

## Runtime environment overrides

Some settings can be overridden at runtime via environment variables without touching source files. These are useful for CI and per-deployment customisation.

| Environment variable     | What it overrides                                                       |
| ------------------------ | ----------------------------------------------------------------------- |
| `UBX_AI_API_KEY`         | Anthropic API key — always takes priority over `ai.api_key` in ubx.yaml |
| `PULUMI_BACKEND_URL`     | State backend URL — overrides the `backend {}` block in the workspace   |
| `UBX_TELEMETRY_ENDPOINT` | Telemetry collection endpoint — overrides the built-in default          |

`PULUMI_BACKEND_URL` is useful when the same source files must deploy to different backends in different environments (e.g., local development vs. CI vs. production). The XCL `backend {}` block is lower priority than this env var.

`UBX_TELEMETRY_ENDPOINT` is intended for self-hosted telemetry. The built-in default points to Ubiquex infrastructure; set this variable to redirect telemetry to your own collector.

## History storage

Apply history is controlled by the `history {}` sub-block in `workspace {}`, `context {}`, or `meta {}`:

```xcl theme={"theme":"css-variables"}
workspace {
    history {
        enabled = true
    }
}
```

When enabled, ubx writes `ubx-history.json` to the same storage location as the Pulumi state file. For S3, GCS, and Azure Blob backends, history is stored in the same bucket/container alongside the stack state. For the local file backend, history is stored at `.ubx/ubx-history.json` in the stack directory.

History is **enabled by default** when no `history {}` block is declared. Declare `history { enabled = false }` to opt out.

The `history {}` block can appear at any level of the resolution chain. A `meta {}` override takes priority:

```xcl theme={"theme":"css-variables"}
// Workspace enables history for all stacks...
workspace {
    history { enabled = true }
}

// ...but this stack opts out
stack "scratch" {
    meta {
        history { enabled = false }
    }
    // ...
}
```

## Migration guide: from ubx.yaml

If you have an existing `ubx.yaml`, the table below maps each key to its new location in a `workspace {}` block.

| ubx.yaml key                       | New location                                                      |
| ---------------------------------- | ----------------------------------------------------------------- |
| `backend: s3://bucket/state`       | `workspace { backend s3 { bucket = "bucket" key = "state" } }`    |
| `backend: file:///abs/path`        | `workspace { backend local { path = "/abs/path" } }`              |
| `backend: https://api.pulumi.com`  | `workspace { backend pulumi { } }`                                |
| `history.enabled: false`           | `workspace { history { enabled = false } }`                       |
| `ai.enabled: true`                 | `workspace { ai { enabled = true } }`                             |
| `ai.model: claude-opus-4-8`        | `workspace { ai { model = "claude-opus-4-8" } }`                  |
| `ai.features.plan_summary: true`   | `workspace { ai { features { plan_summary = true } } }`           |
| `ai.features.cost_estimate: false` | `workspace { ai { features { cost_estimate = false } } }`         |
| `scan.enabled: true`               | `workspace { scan { enabled = true } }`                           |
| `scan.severity: critical`          | `workspace { scan { severity = "critical" } }`                    |
| `ai.api_key: "sk-ant-..."`         | Set `UBX_AI_API_KEY` env var — do not put secrets in source files |
| `telemetry.endpoint: "..."`        | Set `UBX_TELEMETRY_ENDPOINT` env var                              |

Fields `project:`, `stack:`, and `default_tags:` remain in `ubx.yaml` and continue to be read from there. Only the fields listed above have moved to XCL workspace blocks.

### A complete workspace block replacing a typical ubx.yaml

```xcl theme={"theme":"css-variables"}
// workspace.xcl — replaces the stack-level keys that were in ubx.yaml

workspace {
    backend s3 {
        bucket = "my-state-bucket"
        key    = "platform/prod"
        encryption {
            provider = "awskms"
            key      = "alias/pulumi-secrets"
        }
    }

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

    scan {
        enabled  = true
        severity = "high"
    }

    history {
        enabled = true
    }
}
```

Place this file in any `.xcl` file in your stack directory (e.g. `workspace.xcl`). ubx discovers all `.xcl` files in the directory — there is no required filename for `workspace {}` blocks.

## See also

* [workspace](/v1/xcl/language/workspace) — full workspace block reference
* [context](/v1/xcl/language/context) — context block reference
* [meta](/v1/xcl/language/meta) — meta block reference
* [backend](/v1/xcl/language/backend) — backend sub-block reference
