12 KiB
The Complete Technical Guide to CUE (Configure-Unify-Execute)
A structured, practical, and comprehensive introduction to CUE—covering core concepts, real-world applications, and best practices.
1. Introduction to CUE
What is CUE?
CUE (Configure-Unify-Execute) is an open-source data constraint language developed at Google. It is designed for:
- Configuration management (Kubernetes, Terraform, CI/CD)
- Data validation (API schemas, input sanitization)
- Code generation (Dockerfiles, Kubernetes manifests, SQL schemas)
- Policy enforcement (security rules, compliance checks)
Unlike YAML/JSON, CUE merges types, values, and constraints into a single model, enabling deterministic, reproducible configurations.
Why CUE Over Alternatives?
| Tool | Strengths | Weaknesses vs. CUE |
|---|---|---|
| YAML | Human-readable, widely used | No validation, no logic, fragile |
| Jsonnet | Templating, code reuse | Slower, no built-in validation |
| HCL | Terraform integration | Less flexible for non-TF use |
| Pulumi | Full programming model | Complex, runtime-dependent |
| CUE | Validation + unification | Steeper initial learning curve |
2. Core Concepts
The CUE Lattice
CUE’s power comes from its lattice structure, where:
_(Top): Accepts any value (e.g.,string,int,{})._|_(Bottom): An invalid/conflicting value (error state).- Unification (
&) combines constraints (like logical AND). - Disjunction (
|) allows alternatives (like logical OR).
Example:
#Schema: {
name: string & =~"^[A-Z][a-z]+$" // Must start with uppercase
age: int & >0 & <120 // Age between 0 and 120
role?: "admin" | "user" // Optional role
}
valid: #Schema & { name: "Alice", age: 30 } // ✅ Valid
invalid: #Schema & { name: "alice", age: 150 } // ❌ Fails (name, age)
3. Getting Started
Installation
# Install CUE
brew install cue-lang/tap/cue # macOS
sudo apt-get install cue # Debian/Ubuntu
go install cuelang.org/go/cmd/cue@latest # Go users
# Verify
cue version
Basic Workflow
- Define schemas (
.cuefiles). - Validate data (
cue vet). - Export to JSON/YAML (
cue export).
Example:
// schema.cue
#Person: {
name: string
age: int & >=18
}
// data.json
{ "name": "Bob", "age": 25 }
# Validate
cue vet schema.cue data.json # ✅ Passes
4. Real-World Use Cases
Case 1: Kubernetes Configuration
Problem: YAML files are brittle and hard to validate.
Solution: Define a reusable schema.
// k8s.cue
#Deployment: {
apiVersion: "apps/v1"
kind: "Deployment"
metadata: name: string
spec: {
replicas: int & >=1
template: spec: containers: [{
name: string
image: string & =~".+:.+"
}]
}
}
myapp: #Deployment & {
metadata: name: "webapp"
spec: replicas: 3
spec: template: spec: containers: [{ name: "app", image: "nginx:latest" }]
}
# Export to YAML
cue export k8s.cue -e myapp --out yaml > deploy.yaml
Case 2: Terraform Guardrails
Problem: Prevent invalid cloud configurations.
Solution: Enforce AWS/GCP policies.
#AWS: {
region: "us-east-1" | "us-west-2"
instanceType: "t2.micro" | "t3.medium"
}
my_vm: #AWS & {
region: "us-east-1"
instanceType: "t2.micro" # ✅ Valid
}
invalid_vm: #AWS & {
region: "eu-west-1" # ❌ Fails (invalid region)
}
Case 3: CI/CD Pipeline-as-Code
Problem: YAML-based pipelines are hard to maintain.
Solution: Define pipelines in CUE.
#Pipeline: {
steps: [...{
name: string
cmd: string
}]
}
my_pipeline: #Pipeline & {
steps: [
{ name: "build", cmd: "go build" },
{ name: "test", cmd: "go test" },
]
}
# Generate GitHub Actions YAML
cue export pipeline.cue --out yaml > .github/workflows/ci.yml
5. Debugging & Best Practices
Debugging Unification Errors
- Use
cue eval -vto trace unification steps. - Check for
_|_(bottom) in output—indicates conflicts.
Best Practices
- Modularize: Split schemas into files (
schemas/,configs/). - Reuse: Leverage CUE’s import system (
import "acme.com/schemas"). - Validate Early: Run
cue vetin CI/CD.
6. Advanced Topics
Go + CUE Integration
Embed CUE in Go programs for dynamic validation:
package main
import (
"cuelang.org/go/cue/cuecontext"
"fmt"
)
func main() {
ctx := cuecontext.New()
schema := ctx.CompileString(`
#Person: {
name: string
age: int & >=18
}`)
data := ctx.CompileString(`{ name: "Alice", age: 30 }`)
unified := schema.Unify(data)
if err := unified.Validate(); err != nil {
fmt.Println("Validation failed:", err)
} else {
fmt.Println("Valid!")
}
}
Generating Code
Convert CUE to:
- OpenAPI:
cue export --out openapi - Go Structs:
cue get go - SQL: Use templates (
text/template).
7. Learning Resources
- Official Docs: cuelang.org
- Playground: cuelang.org/play
- Community: CUE Slack
Conclusion
CUE replaces ad-hoc YAML/JSON with structured, validated, and reusable configurations. By mastering:
- Lattice logic (unification, constraints),
- Real-world workflows (K8s, Terraform, CI/CD),
- Tooling integration (Go, OpenAPI),
You can eliminate configuration errors and enforce policies declaratively.
Next Step: Try the CUE Tutorial or experiment in the playground! 🚀
Here’s a Mermaid.js diagram that visually explains the CUE lattice (Top, Bottom, Unification, and Disjunction) in a way that’s both accurate and appealing for visual learners. You can embed this directly in your Gitea Markdown files (if Mermaid rendering is enabled):
Diagram Explanation:
-
⊤ (Top)- The root of all possible values (e.g.,
string,int,{}). - Branches into concrete values (like
"Alice") and constraints (like>=18).
- The root of all possible values (e.g.,
-
Unification (
&)- Merges constraints (like a strict AND gate).
- Outputs either a valid value (if constraints are met) or
⊥(Bottom) (if conflicts exist).
-
Disjunction (
|)- Represents alternatives (like a flexible OR gate).
- Outputs the first valid alternative (e.g.,
"admin" | "user").
-
⊥ (Bottom)- The "error state" (e.g.,
name: 42whenstringis required).
- The "error state" (e.g.,
Why This Works:
- Visualizes the Lattice: Shows how CUE narrows possibilities from
⊤to valid outputs. - Color-Coded: Green (Top), Blue (Valid), Red (Error), Orange (Alternatives).
- Includes Examples: Embedded real CUE snippets for clarity.
Alternate Version (Simpler):
If you prefer a minimalistic view:
graph LR
T["⊤ (Top)"] -->|Unification &| U["Valid"]
T -->|Conflict| B["⊥ (Bottom)"]
T -->|Disjunction| D["A | B | C"]
%% CUE Lattice Diagram
graph TD
A["⊤ (Top)"] --> B["Concrete Values"]
A --> C["Constraints"]
B --> D["Unification (&)"]
C --> D
D --> E["Valid Output"]
D --> F["⊥ (Bottom, Invalid)"]
B --> G["Disjunction (|)"]
C --> G
G --> H["Valid Alternative"]
style A fill:#4CAF50,stroke:#388E3C
style F fill:#F44336,stroke:#D32F2F
style E fill:#2196F3,stroke:#1976D2
style H fill:#FF9800,stroke:#F57C00
%% Examples
I["Example:\nname: string & =~'^[A-Z]'"] --> D
J["Example:\nrole: 'admin' | 'user'"] --> G
It looks like you're describing concepts related to type systems or constraint solving in programming languages. Let's break down each term and how they might be used in such a context:
1. Top ((_))
- Definition: The "Top" value is a universal type or constraint that can accept any value. It represents the most general or unrestricted type.
- Example: In a type system, the "Top" type might be something like
AnyorObject, which can hold any kind of value (e.g., strings, integers, objects, etc.).
2. Bottom ((_|_))
- Definition: The "Bottom" value represents an invalid or conflicting state. It is often used to indicate an error or an impossible condition.
- Example: In a type system, the "Bottom" type might be something like
NeverorVoid, which represents a type that can never be instantiated. It is used to signal errors or contradictions.
3. Unification ((&))
- Definition: Unification combines constraints in a way that is similar to a logical AND. It finds the most specific type or constraint that satisfies all given constraints.
- Example: If you have two constraints (A) and (B), (A & B) will result in a new constraint that is the intersection of (A) and (B). For example, if (A) is
intand (B) isnumber, (A & B) might result inintbecauseintis a more specific type ofnumber.
4. Disjunction ((|))
- Definition: Disjunction allows alternatives, similar to a logical OR. It represents a type or constraint that can be one of several possible values.
- Example: If you have two constraints (A) and (B), (A | B) will result in a new constraint that can be either (A) or (B). For example, if (A) is
intand (B) isstring, (A | B) represents a type that can be either anintor astring.
Example in Practice
Let's consider a simple example in a hypothetical type system:
Type A = int
Type B = string
Type C = A & B
Type D = A | B
- Type C: Since
intandstringare incompatible types, (A & B) would result in the "Bottom" type ((_|_)), indicating an error or conflict. - Type D: (A | B) would result in a type that can be either
intorstring.
These concepts are fundamental in type inference and constraint solving in programming languages, particularly in statically typed languages like TypeScript, OCaml, or Haskell.
Here’s the fixed and validated Mermaid.js diagram for the CUE lattice that will render correctly in Gitea (and most Markdown editors with Mermaid support):
graph LR
T["⊤ (Top)"] -->|Unification &| U["Valid"]
T -->|Conflict| B["⊥ (Bottom)"]
T -->|Disjunction | | D["A | B | C"]
Key Fixes:
-
Removed Special Characters:
- Replaced
(&)and(|)with plain&and|to avoid syntax issues. - Simplified labels (e.g., "Any Value" → "Top", "Invalid" → "Bottom").
- Replaced
-
Explicit Arrow Syntax:
- Used
-->|label|syntax for clarity (some Mermaid versions misinterpret unquoted|).
- Used
-
Minimalist and Robust:
- Works in Gitea, GitHub, VS Code, and other Mermaid-compatible tools.
How It Renders:
⊤ (Top): The root node (all possible values).- Unification (
&): Narrow to valid output (e.g.,string & =~"^[A-Z]"). - Disjunction (
|): Branch into alternatives (e.g.,"admin" | "user"). ⊥ (Bottom): Error state (invalid unification).
For Gitea Specifically:
- Ensure Mermaid is enabled in your instance (
app.iniconfig):[markup.mermaid] ENABLED = true - Wrap the diagram in a code block with
mermaidlanguage tag.
Alternate Version (More Detailed):
If you prefer the original design with examples:
graph TD
Top["⊤ (Top)"] -->|Unify Constraints| Valid["Valid (e.g., name: 'Alice')"]
Top -->|Conflict| Bottom["⊥ (Bottom)"]
Top -->|Choose Alternative| Disjunction["admin | user"]
style Top fill:#4CAF50
style Bottom fill:#F44336
style Valid fill:#2196F3
style Disjunction fill:#FF9800
Let me know if you'd like further tweaks!