Skip to content

Glyphs - Infrastructure as Code Templates

Comprehensive guide to the glyph system in kast. Glyphs are reusable Helm named templates that generate Kubernetes resources for infrastructure integration.

Overview

Glyph: Reusable Helm template that generates one or more Kubernetes resources based on input parameters.

Purpose: - Encapsulate infrastructure patterns (vault, istio, certificates, databases) - Provide consistent interfaces across deployments - Enable composition of complex systems from simple definitions - Reduce YAML duplication and boilerplate - Use infrastructure discovery via Lexicon

Core Concept: "Spell once, use everywhere"

Architecture

┌─────────────────────────────────────────────────────┐
│ Spell Configuration (glyphs definition)             │
│ - Declarative infrastructure requirements          │
└──────────────────────┬──────────────────────────────┘
┌──────────────────────▼──────────────────────────────┐
│ Kaster Orchestrator                                 │
│ - Iterates glyph definitions                        │
│ - Invokes glyph templates                           │
│ - Passes context (spellbook, chapter, lexicon)     │
└──────────────────────┬──────────────────────────────┘
        ┌──────────────┴──────────────┐
        │                             │
┌───────▼──────────┐      ┌──────────▼────────────┐
│ Glyph Templates  │      │ Runic Indexer         │
│ - Named templates│      │ - Infrastructure      │
│ - Resource gen   │      │   discovery           │
└────────┬─────────┘      │ - Lexicon queries     │
         │                └──────────┬────────────┘
         │                           │
         └──────────────┬────────────┘
        ┌───────────────▼───────────────┐
        │   Kubernetes Resources        │
        │ - Deployments, Services       │
        │ - CRDs (VaultSecret, etc.)    │
        │ - Configuration (ConfigMaps)  │
        └───────────────────────────────┘

See Kaster for orchestration details and Lexicon for infrastructure discovery.

Key Concepts

1. Glyph Definition

User-facing configuration that declares infrastructure requirements using a map structure where the key is the resource name:

glyphs:
  vault:                        # Glyph package name
    database-creds:             # Resource name (map key)
      type: secret              # Template type within package
      format: env               # Glyph-specific parameters
      keys: [username, password]

IMPORTANT: Glyphs use a map structure (not a list). The resource name is the map key, not a name: field inside the definition.

2. Glyph Template

Helm named template that generates Kubernetes resources:

{{- define "vault.secret" -}}
{{- $root := index . 0 -}}
{{- $glyphDefinition := index . 1 }}
---
apiVersion: redhatcop.redhat.io/v1alpha1
kind: VaultSecret
metadata:
  name: {{ $glyphDefinition.name }}
spec:
  # Generate VaultSecret based on $glyphDefinition
{{- end }}

3. Glyph Package

Collection of related templates organized in a chart:

charts/glyphs/vault/
├── Chart.yaml
├── templates/
│   ├── secret.tpl           # vault.secret template
│   ├── prolicy.tpl          # vault.prolicy template
│   ├── crypto-key.tpl       # vault.cryptoKey template
│   └── _helpers.tpl
└── examples/
    ├── secrets.yaml
    └── prolicy-test.yaml

4. Runic Indexer

Query system for discovering infrastructure from Lexicon:

{{- $vaults := get (include "runicIndexer.runicIndexer"
     (list $root.Values.lexicon
           $glyphDefinition.selector
           "vault"
           $root.Values.chapter.name) | fromJson) "results" }}

Pattern: Glyphs use runic indexer to find infrastructure (vault servers, databases, gateways) from lexicon based on selectors or defaults. See Lexicon for query patterns.

How Glyphs Work

Step 1: Declaration in Spell

User declares infrastructure requirements:

name: my-app

glyphs:
  vault:
    app-config:                 # Resource name as map key
      type: secret
      keys: [api_key]

  istio:
    app-routing:                # Resource name as map key
      type: virtualService
      hosts: [app.example.com]

Step 2: Kaster Detection

Librarian detects glyphs: field and adds Kaster as multi-source:

apiVersion: argoproj.io/v1alpha1
kind: Application
spec:
  sources:
    - path: ./charts/summon        # Main application
    - path: ./charts/kaster        # Glyphs orchestrator (auto-added)
      helm:
        values: |
          glyphs:              # Glyph definitions passed
            vault: {...}
            istio: {...}
          spellbook: {...}     # Context passed
          chapter: {...}
          lexicon: [...]

See Kaster for orchestration mechanics.

Step 3: Glyph Iteration

Kaster iterates over glyph definitions and invokes templates:

{{- range $glyphName, $glyphMap := .Values.glyphs }}
  {{- range $resourceName, $glyphDefinition := $glyphMap }}
    {{- $templateName := printf "%s.%s" $glyphName $glyphDefinition.type }}
    {{- include $templateName (list $ (merge (dict "name" $resourceName) $glyphDefinition)) }}
  {{- end }}
{{- end }}

Step 4: Template Execution

Each glyph template: 1. Receives $root (chart context) and $glyphDefinition (user config with resource name) 2. Queries lexicon via runic indexer (if needed) - see Lexicon 3. Generates Kubernetes resources 4. Returns YAML manifests

Step 5: Resource Deployment

ArgoCD deploys generated resources to Kubernetes: - VaultSecret resources (see Vault Integration) - Istio VirtualServices - Certificate requests - Custom resources

Glyph Categories

Infrastructure Integration

Purpose: Connect applications to infrastructure services

Glyphs: - vault - Secrets management, authentication, policies (see Vault Integration) - istio - Service mesh, routing, security - certManager - TLS certificates, DNS records - argo-events - Event-driven workflows

Usage Pattern:

glyphs:
  vault:
    app-policy:                 # Resource name as map key
      type: prolicy             # Create policy
    app-secret:                 # Resource name as map key
      type: secret              # Sync secrets
  istio:
    app-routing:                # Resource name as map key
      type: virtualService      # Configure routing

Documentation: - Vault Integration - Vault integration - docs/glyphs/istio.md - Istio integration - docs/glyphs/certmanager.md - Certificate management - docs/glyphs/argo-events.md - Event workflows

Cloud Providers

Purpose: Provision cloud resources declaratively

Glyphs: - gcp - Google Cloud Platform resources - crossplane - Multi-cloud resource provisioning - s3 - S3-compatible storage

Usage Pattern:

glyphs:
  gcp:
    my-bucket:                  # Resource name as map key
      type: bucket              # Create GCS bucket
    my-sa:                      # Resource name as map key
      type: serviceAccount      # Create GCP SA
    app-identity:               # Resource name as map key
      type: workloadIdentity    # Bind K8s SA to GCP SA

Documentation: - docs/glyphs/gcp.md - docs/glyphs/crossplane.md - docs/glyphs/s3.md

Database Management

Purpose: Database provisioning and access management

Glyphs: - postgresql - Cloud-hosted PostgreSQL clusters - vault (databaseEngine) - Dynamic database credentials

Usage Pattern:

glyphs:
  postgresql:
    app-db:                     # Resource name as map key
      type: cluster             # Create PostgreSQL cluster
      replicas: 3

  vault:
    postgres-engine:            # Resource name as map key
      type: databaseEngine      # Dynamic credentials
      roles:
        - name: readonly
          dbName: myapp

Documentation: - docs/glyphs/postgresql.md - Vault Integration (databaseEngine section)

Identity & Access

Purpose: Authentication, authorization, identity management

Glyphs: - keycloak - OIDC/SAML identity provider - vault (oidc-auth, kube-auth) - Authentication backends

Usage Pattern:

glyphs:
  keycloak:
    myapp-realm:                # Resource name as map key
      type: realm               # Create realm
    myapp-client:               # Resource name as map key
      type: client              # OIDC client
      clientId: myapp

Documentation: - docs/glyphs/keycloak.md - Vault Integration (authentication section)

System Utilities

Purpose: Common patterns and utilities

Glyphs: - common - Shared utilities (labels, names, annotations) - freeForm - Pass-through YAML - runic-system - Runic indexer and discovery

Usage Pattern:

# common glyph (used by other glyphs, not directly)
labels:
  {{- include "common.labels" $root | nindent 4 }}

# freeForm glyph (for arbitrary resources)
glyphs:
  freeForm:
    custom-config:              # Resource name as map key
      type: resource
      yaml: |
        apiVersion: v1
        kind: ConfigMap
        metadata:
          name: custom-config

Documentation: - docs/glyphs/common.md - docs/glyphs/freeform.md - docs/glyphs/runic-system.md

Common Patterns

Pattern 1: Infrastructure Discovery

Use Lexicon for environment-aware configuration:

# Lexicon (in book _lexicon/)
lexicon:
  - name: production-vault
    type: vault
    url: https://vault.prod.svc:8200
    labels:
      environment: production
      default: chapter
    chapter: production

# Spell (automatically finds production-vault in production chapter)
glyphs:
  vault:
    app-secret:                 # Resource name as map key
      type: secret
      # Uses chapter default vault automatically via runic indexer

Benefits: - Same spell works across environments - Infrastructure changes don't require spell updates - Centralized infrastructure configuration in Lexicon

Pattern 2: Multi-Glyph Composition

Combine glyphs to build complete infrastructure:

name: payment-service

glyphs:
  # Authentication & Authorization
  vault:
    payment-policy:             # Resource name as map key
      type: prolicy
      serviceAccount: payment-service
    database-creds:             # Resource name as map key
      type: secret
      keys: [username, password]
    stripe-api-key:             # Resource name as map key
      type: secret
      keys: [api_key]

  # Service Mesh
  istio:
    payment-api:                # Resource name as map key
      type: virtualService
      hosts: [payments.example.com]
      circuitBreaking:
        enabled: true

  # TLS Certificates
  certManager:
    payment-tls:                # Resource name as map key
      type: certificate
      dnsNames: [payments.example.com]
      issuerRef:
        name: letsencrypt-prod

Result: Complete infrastructure stack from declarative configuration

Pattern 3: Hierarchical Configuration

Use spellbook/chapter context for scoping:

# Book-level vault policy (all chapters inherit)
spellbook:
  prolicy:
    extraPolicy:
      - path: shared/certificates/*
        capabilities: [read]

# Chapter-level vault policy (production only)
chapter:
  prolicy:
    extraPolicy:
      - path: production/database/*
        capabilities: [read, list]

# Spell-level vault policy (application-specific)
glyphs:
  vault:
    app-policy:                 # Resource name as map key
      type: prolicy
      extraPolicy:
        - path: applications/payment/secrets
          capabilities: [create, read, update]

Result: Policy merges book + chapter + spell extraPolicy

Pattern 4: Conditional Infrastructure

Enable infrastructure based on environment:

# Development
name: my-app
glyphs:
  vault:
    dev-credentials:            # Resource name as map key
      type: secret
      # Development uses simpler auth

# Production
name: my-app
glyphs:
  vault:
    prod-credentials:           # Resource name as map key
      type: secret
      # Production uses mTLS

  istio:
    app-routing:                # Resource name as map key
      type: virtualService
      # Only production has external routing
      hosts: [app.production.com]

  certManager:
    app-tls:                    # Resource name as map key
      type: certificate
      # Only production has TLS certificates
      dnsNames: [app.production.com]

Pattern 5: Secret Injection

Inject secrets at different scopes:

glyphs:
  vault:
    # Book-level (shared across all chapters)
    registry-credentials:       # Resource name as map key
      type: secret
      path: book
      keys: [username, password]

    # Chapter-level (shared within chapter)
    database-credentials:       # Resource name as map key
      type: secret
      path: chapter
      keys: [host, port, database]

    # Namespace-level (application-specific)
    app-private-key:            # Resource name as map key
      type: secret
      # Default path (namespace-scoped)
      keys: [private_key]

See HIERARCHY_SYSTEMS.md for path hierarchy details

Glyph Template Anatomy

Basic Structure

{{/*kast - Kubernetes arcane spelling technology
Copyright (C) 2023 namenmalkv@gmail.com
Licensed under the GNU GPL v3. See LICENSE file for details.

glyph-name.template-type description

Parameters:
- $root: Chart root context (index . 0)
- $glyphDefinition: Configuration (index . 1)
  - name: Resource name (injected from map key)
  - field1: Description (optional)

Example:
  glyph-name:
    my-resource:              # Map key becomes name
      type: template-type
      field1: value
*/}}

{{- define "glyph-name.template-type" -}}
{{- $root := index . 0 -}}
{{- $glyphDefinition := index . 1 }}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ $glyphDefinition.name }}
  labels:
    {{- include "common.labels" $root | nindent 4 }}
data:
  field1: {{ $glyphDefinition.field1 }}
{{- end }}

Parameter Extraction

{{- define "myglyph.resource" -}}
{{- $root := index . 0 -}}           # Chart context
{{- $glyphDefinition := index . 1 }} # User configuration (includes name from map key)

# Access root context
{{- $spellbookName := $root.Values.spellbook.name }}
{{- $chapterName := $root.Values.chapter.name }}
{{- $namespace := $root.Release.Namespace }}

# Access glyph definition
{{- $resourceName := $glyphDefinition.name }}  # Injected from map key
{{- $enabled := default true $glyphDefinition.enabled }}
{{- $replicas := default 1 $glyphDefinition.replicas }}
{{- end }}

Runic Indexer Integration

{{- define "myglyph.resource" -}}
{{- $root := index . 0 -}}
{{- $glyphDefinition := index . 1 }}

{{/* Query lexicon for infrastructure (see LEXICON.md) */}}
{{- $results := get (include "runicIndexer.runicIndexer"
     (list $root.Values.lexicon
           (default dict $glyphDefinition.selector)
           "resource-type"
           $root.Values.chapter.name) | fromJson) "results" }}

{{- range $resource := $results }}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ $glyphDefinition.name }}
data:
  url: {{ $resource.url }}
  namespace: {{ $resource.namespace }}
{{- end }}
{{- end }}

See Lexicon for runic indexer query patterns.

Common Glyph Integration

{{- define "myglyph.resource" -}}
{{- $root := index . 0 -}}
{{- $glyphDefinition := index . 1 }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ include "common.name" $root }}
  labels:
    {{- include "common.labels" $root | nindent 4 }}
  annotations:
    {{- include "common.annotations" $root | nindent 4 }}
spec:
  selector:
    {{- include "common.selectorLabels" $root | nindent 4 }}
{{- end }}

Glyph Testing

TDD Workflow

1. RED - Write failing test:

# Create example
cat > charts/glyphs/myglyph/examples/basic.yaml <<EOF
lexicon:
  - name: test-resource
    type: mytype
    url: http://test.svc
    labels:
      default: book

glyphs:
  myglyph:
    test-resource:              # Map key is resource name
      type: resource
EOF

# Verify test fails (template doesn't exist)
make glyphs myglyph
# Output: ❌ myglyph-basic (template not found)

2. GREEN - Implement template:

# Create template
cat > charts/glyphs/myglyph/templates/resource.tpl <<'EOF'
{{- define "myglyph.resource" -}}
{{- $root := index . 0 -}}
{{- $glyphDefinition := index . 1 }}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ $glyphDefinition.name }}
data:
  test: value
{{- end }}
EOF

# Verify test passes
make glyphs myglyph
# Output: ✅ myglyph-basic

3. REFACTOR - Generate expected output:

# Lock in expected output
make generate-expected GLYPH=myglyph

# Verify diff validation
make glyphs myglyph
# Output: ✅ myglyph-basic (output matches expected)

See Glyph Development for complete TDD workflow and TDD Commands for testing commands.

Testing Commands

# Test specific glyph
make glyphs vault

# Test all glyphs
make test-glyphs-all

# List available glyphs
make list-glyphs

# Generate expected outputs
make generate-expected GLYPH=vault

# Show differences
make show-glyph-diff GLYPH=vault EXAMPLE=secrets

# Clean test outputs
make clean-output-tests

See TDD Commands for complete command reference.

Best Practices

Design for Reusability

Good (reusable):

glyphs:
  vault:
    app-secret:                 # Map key is resource name
      type: secret
      selector: {environment: production}  # Environment-aware via lexicon

Avoid (hardcoded):

glyphs:
  vault:
    app-secret:                 # Map key is resource name
      type: secret
      vaultURL: https://vault.prod.svc:8200  # Hardcoded

Use Lexicon for Discovery

Use runic indexer with Lexicon:

# Lexicon defines infrastructure
lexicon:
  - name: production-db
    type: database
    host: postgres.prod.svc
    labels:
      environment: production
      default: chapter

# Glyph discovers automatically
glyphs:
  database:
    app-connection:             # Map key is resource name
      type: connection
      # Finds production-db in production chapter via runic indexer

Validate Inputs

{{- define "myglyph.resource" -}}
{{- $root := index . 0 -}}
{{- $glyphDefinition := index . 1 }}

{{/* Validate required fields */}}
{{- if not $glyphDefinition.name }}
  {{- fail "myglyph.resource requires 'name' field (injected from map key)" }}
{{- end }}

{{- if not (hasKey $glyphDefinition "type") }}
  {{- fail "myglyph.resource requires 'type' field" }}
{{- end }}
{{- end }}

Provide Sensible Defaults

# Always provide defaults for optional fields
enabled: {{ default true $glyphDefinition.enabled }}
replicas: {{ default 1 $glyphDefinition.replicas }}
format: {{ default "plain" $glyphDefinition.format }}

Document Parameters

{{/*
myglyph.resource creates a ConfigMap

Parameters:
- $root: Chart root context (index . 0)
- $glyphDefinition: Configuration (index . 1)
  - name: ConfigMap name (injected from map key)
  - enabled: Enable resource (optional, default: true)
  - data: ConfigMap data (optional)

Example:
  myglyph:
    my-config:              # Map key becomes name
      type: resource
      enabled: true
      data:
        key: value
*/}}

Test Thoroughly

examples/
├── basic.yaml              # Minimal configuration
├── advanced.yaml           # All features
├── edge-cases.yaml         # Boundary conditions
├── lexicon-integration.yaml  # With runic indexer
└── multi-resource.yaml     # Multiple instances

See Glyph Development for testing best practices.

Creating New Glyphs

Quick Start

# 1. Create structure
mkdir -p charts/glyphs/myglyph/{templates,examples}

# 2. Create Chart.yaml
cat > charts/glyphs/myglyph/Chart.yaml <<EOF
apiVersion: v2
name: myglyph
description: My custom glyph
version: 1.0.0
EOF

# 3. Create test (RED)
cat > charts/glyphs/myglyph/examples/basic.yaml <<EOF
glyphs:
  myglyph:
    test-resource:              # Map key is resource name
      type: resource
EOF

# 4. Run test (expect failure)
make glyphs myglyph

# 5. Create template (GREEN)
cat > charts/glyphs/myglyph/templates/resource.tpl <<'EOF'
{{- define "myglyph.resource" -}}
{{- $root := index . 0 -}}
{{- $glyphDefinition := index . 1 }}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ $glyphDefinition.name }}
{{- end }}
EOF

# 6. Run test (expect success)
make glyphs myglyph

# 7. Generate expected output (REFACTOR)
make generate-expected GLYPH=myglyph

See Glyph Development for complete development guide.

Troubleshooting

Glyph Not Invoked

Check:

# Verify kaster source added (see KASTER.md)
helm template my-book librarian --set name=my-book \
  | yq '.spec.sources[] | select(.path | contains("kaster"))'

# Verify glyph configuration passed
make glyphs <name>

See Kaster for orchestration troubleshooting.

Template Not Found

Check:

# Verify template exists
ls charts/glyphs/<name>/templates/

# Check template name
grep -r "define.*<name>.<type>" charts/glyphs/<name>/

Lexicon Query Returns Empty

Check:

# Verify lexicon entry exists
yq '.lexicon[] | select(.type == "<type>")' \
  charts/glyphs/<name>/examples/test.yaml

# Check selector matches labels
# Selector: {environment: production}
# Labels: {environment: production}  # Must match

See Lexicon for query troubleshooting.

Resources Not Created

Check:

# Check ArgoCD manifests
argocd app manifests <app-name>

# Verify CRDs installed
kubectl get crd | grep <resource-type>

# Check resource namespace
kubectl get <resource> -n <namespace>

Examples

Complete Infrastructure Stack

name: production-app

glyphs:
  # Secrets & Authentication
  vault:
    app-policy:                 # Map key is resource name
      type: prolicy
      serviceAccount: production-app
      extraPolicy:
        - path: database/creds/app-role
          capabilities: [read]

    app-config:                 # Map key is resource name
      type: secret
      format: env
      keys: [api_key, webhook_secret]

    database-creds:             # Map key is resource name
      type: secret
      format: env
      keys: [username, password]

  # Service Mesh
  istio:
    app-routing:                # Map key is resource name
      type: virtualService
      hosts: [app.example.com]
      http:
        - match:
            - uri:
                prefix: /api
          route:
            - destination:
                host: production-app
                port:
                  number: 80
      circuitBreaking:
        enabled: true
        maxConnections: 100

  # TLS Certificates
  certManager:
    app-tls:                    # Map key is resource name
      type: certificate
      dnsNames:
        - app.example.com
        - api.example.com
      issuerRef:
        name: letsencrypt-prod
        kind: ClusterIssuer

  # Database
  postgresql:
    app-database:               # Map key is resource name
      type: cluster
      replicas: 3
      size: 100Gi

See chart examples directories for more: - charts/glyphs/*/examples/ - Glyph-specific examples - bookrack/example-tdd-book/infrastructure/ - Real-world usage - charts/summon/examples/ - Workload + glyph integration

Summary

Glyphs: - Reusable infrastructure templates - Orchestrated by Kaster - Discovered via Lexicon and Runic Indexer - Tested via TDD methodology - Composable and environment-aware

Key Benefits: - Infrastructure as Code - Environment portability - Reduced boilerplate - Consistent patterns - Testable and maintainable

Pattern: Declare infrastructure requirements with map structure, glyphs handle implementation details.