Skip to content

polyedre/hexol

Repository files navigation

Hexol

Write your config in a real programming language.

An inventory is a program. You run it, and it builds one big JSON-compatible data structure — your whole config, assembled dynamically. From there hexol renders it to whatever you need: Kubernetes manifests, Terraform JSON, Ansible playbooks, SQL, plain YAML.

Hexol is both:

  • a library — the Scheme you write inventories with, plus target libraries (Kubernetes, Terraform, …) layered on top, and
  • a CLI — to act on an inventory: render it, apply it, inspect what it produced.

Example

An inventory is a program that builds your config. Here's a Kubernetes one (examples/kubernetes.scm):

(use-modules (hexol k8s))

(hx-ops
  (with-namespace "loulou"
    ;; A Deployment (+ its Service). Its ConfigMap and Secret are pulled in by
    ;; name — one becomes `envFrom`, the other a mounted volume. Positional
    ;; name, then `(key value)` entries; values are evaluated Scheme.
    (app "loulou"
         (image "secure.io/loulou:2.1") (port 9000) (replicas 2)
         (env-from (cm "loulou-config"))
         (volumes  (mount (sec "loulou-secret") "/etc/loulou/secret"))
         (resources "200m-*/256Mi"))

    ;; The config the Deployment above consumes — ordinary Scheme data.
    (configmap "loulou-config"
               (data (LOG_LEVEL "debug") (CACHE_SIZE "256")))
    (secret    "loulou-secret"
               (data (DB_PASSWORD "bG91bG91LXNlY3JldA=="))))

  ;; Cross-cutting pass: hash every Deployment's referenced ConfigMaps and
  ;; Secrets, and stamp the result onto the pod template as an annotation —
  ;; so a config change forces a rollout. No templating, just code.
  (checksum-config))

A few things this buys you over plain manifests:

  • The Secret is attached to the Deployment without hand-writing the volumes and volumeMounts sections — (mount …) wires both ends.
  • "200m-*/256Mi" expands to the full resources block (CPU request 200m, memory request + limit 256Mi).
  • checksum-config runs after every resource is built, finds each Deployment's ConfigMaps and Secrets, hashes them, and stamps a config/checksum annotation onto the pod template — so a config change forces a rollout, for every workload, for free. In Helm you'd hand-write a sha256sum per Deployment.

Install

Hexol runs on Guile 3.x and needs two Guile libraries: guile-json (the (json) module) and guile-libyaml (the (yaml) module). It also uses jq. All of these are declared in manifest.scm, which is the source of truth for dependencies.

The easy path is Guix, which reads that manifest directly — no manual install:

git clone https://github.com/Polyedre/hexol && cd hexol
guix shell -m manifest.scm -- ./bin/hexol render -i examples/kubernetes.scm

(The repo's .envrc does this automatically under direnv.)

Without Guix, install Guile 3.x plus guile-json, guile-libyaml, and jq however your distro provides them, then:

./bin/hexol render -i examples/kubernetes.scm   # auto-compiles on first use

The CLI itself shells out to nothing. Individual features do, and only when you use them: helm/yq to expand charts, sops for inline secrets, and tofu/kubectl/talosctl for apply. Install whichever you need.

CLI Usage

./bin/hexol render -o json -i examples/kubernetes.scm

# act on it (appliers an inventory registers via `applies-with`)
./bin/hexol apply --list -i examples/kubernetes.scm                       # show the applier pipeline
./bin/hexol apply --dry-run -i examples/kubernetes.scm                    # delegate to each tool's dry-run

# introspect: rendering is not opaque
./bin/hexol tree -i examples/kubernetes.scm                               # op tree (with hashes)
./bin/hexol show OP_HASH -i examples/kubernetes.scm                       # one op: source + delta
./bin/hexol explain regions.alpha5.network.cni -i examples/inventory.scm  # what touched a path

The Library

While the kernel is target-agnostic, the library provide a few syntaxic sugar helpers:

  • Kubernetes — Deployments, Services, ConfigMaps, Secrets, and cross-cutting passes like checksum-config. Render to a manifest stream with -o yaml.
  • Terraform — providers, resources, and data sources as Scheme, rendered to terraform init-ready JSON with -o terraform. See examples/terraform.scm.
  • Ansible — plays and tasks accumulated into a playbook, rendered with -o ansible. See examples/ansible.scm.
  • Secrets (SOPS) — secrets live inline in the inventory, encrypted at rest with sops: no separate *.sops.yaml files to keep in sync. (secret-ref 'key) is a cheap marker, so only render shells out to sops; manage the store with the hexol secret subcommands. examples/secrets.scm is self-contained — it ships a throwaway age key, so hexol render -o yaml -i examples/secrets.scm decrypts and substitutes real plaintext on a fresh clone. See also examples/homelab.scm and docs/authoring.md.

Inventories can also register their own renderers via renders-with — the database-schema.scm and ledger.scm examples add -o sql and -o ledger that way.

FAQ

How is this different from Pulumi, CDK, jsonnet, or Dhall? Those build your config in memory and hand it off. Hexol keeps the build itself around: resolving an inventory is a chain of small labeled steps over a growing data structure, and every value remembers the step that set it — its source, its file and line, the exact change it made. So you can point at any value in the output and ask "what set this, and to what?" — tree, show, and explain answer it. No other config tool does that.

Is this a serious Kubernetes/Terraform tool, or a Scheme demo? The Kubernetes and Terraform libraries are the real ones — examples/homelab.scm deploys a 3-node Talos cluster to OVH for real, end to end. The SQL and Ledger renderers are tiny example-file extensions, there to show the kernel doesn't care what it's building. Take them as proof the engine is general, not as products.

Is it production-ready? It's young: one author, no outside production users. It has a test suite (make test, run on every push and PR via GitHub Actions) and it does run a live homelab. Kick the tires before you bet a cluster on it.

Documentation

  • docs/model.md — the engine model and its rationale.
  • docs/authoring.md — writing inventories: surface forms, helpers, file splitting, ordering, and the repository layout.
  • docs/extending.md — building target libraries, the kernel/library/example boundary, worked Terraform and Helm conversions, and introspection.

License

Copyright (C) 2026 Polyedre. GNU General Public License v3.0 or later; see LICENSE for the full text.

About

Your configuration as code

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors