Skip to content
Docs

Modules

What is a module?

A module is a versioned directory that implements the cluster lifecycle operations for a specific cloud provider or infrastructure target. Modules contain shell scripts and workflow YAMLs for:

  • create — provision a new cluster
  • delete — deprovision a cluster
  • status — query the current cluster status
  • auth — merge the cluster kubeconfig into ~/.kube/config
  • scale (optional) — apply param changes to a live cluster without reprovisioning
  • describe (optional) — report a live cluster’s params, for hyve cluster adopt

Hyve does not embed any cloud SDKs. All cloud operations are delegated to modules, which in turn invoke whatever CLI tools are already installed in your environment (the Civo CLI, AWS CLI, gcloud, az, etc.).

Why modules?

The module system decouples Hyve’s core reconciliation logic from cloud-provider specifics:

  • Any provider, any implementation — if you can produce the right HYVE_KEY=value outputs, you can write a module for it, in whatever language or tool fits the provider best
  • Your credentials stay in your environment — modules read credentials from the same env vars and config files that your cloud CLIs use; Hyve never stores them
  • Versioned and locked — modules are pinned in hyve.lock with a SHA256 content digest, ensuring reproducible reconciles across your team and CI/CD
  • Composable — modules can be combined with lifecycle workflows (beforeCreate, onCreate, afterCreate, onDelete, afterDelete) for full pipeline automation

Module directory structure

<module>/
module.yaml — manifest: apiVersion, kind: Module, params, requirements
create.yaml — kind: Workflow; provisions the cluster; emits HYVE_* outputs
delete.yaml — kind: Workflow; deprovisions the cluster
status.yaml — kind: Workflow; emits HYVE_CLUSTER_STATUS=<value>
auth.yaml — kind: ClusterAuth; one or more named auth methods
scale.yaml — kind: Workflow; optional; applies param changes
describe.yaml — kind: Workflow; optional; used by `hyve cluster adopt`
README.md

module.yaml

The manifest describes the module, declares params, and lists any requirements — environment variables and tools that must be in PATH:

apiVersion: v1
kind: Module
metadata:
name: civo
description: Provision Civo Kubernetes clusters
version: v1.0.0
spec:
params:
- name: node_size
required: true
description: Civo node size (e.g. g4s.kube.medium)
- name: node_count
required: false
default: "1"
- name: cluster_type
required: false
default: k3s
choices: [k3s, talos]
requirements:
env:
- name: CIVO_TOKEN
description: "Civo API token. Alternative: run `civo apikey save` before reconciling."
tools:
- name: civo
description: Civo CLI — https://github.com/civo/cli
- name: kubectl
description: Kubernetes CLI — required for auth

In local mode, spec.requirements.tools is checked with a plain PATH lookup before every module operation — create, delete, status, scale, and auth — not just once at install time. A missing tool fails fast with a clear error naming every missing tool, before the operation’s script ever runs. (In cluster mode operations run in a Job container, so the tools must be in its image — see Running in cluster mode.)

module tool requirements not met:
- required tool 'civo' not found in PATH (Civo CLI — https://github.com/civo/cli)

spec.requirements.env is enforced in cluster mode: a missing variable fails the operation with a clear error before its script runs. It isn’t enforced in local mode, where a credential may legitimately come from somewhere else (a CLI’s own config file).

spec.params is documentation only — shown by hyve module info. default, required, and choices aren’t applied or enforced, so scripts must supply their own fallbacks (${HYVE_PARAM_NODE_COUNT:-1}).

Module sources

Modules are referenced by source string in templates:

Source formatExampleWhen to use
Git repositorygithub.com/your-org/hyve-my-providerA module in its own repository, on any git host
Git repository with subdirectorygithub.com/your-org/modules//my-providerA monorepo holding several modules
Local path (./, ../, or absolute)../hyve-my-providerDeveloping and testing a module, relative to the consuming repo’s root

Versions: omitted or latest resolves to the newest semver tag (or HEAD if there are none), a semver constraint to the best matching tag, and anything else is used as a branch, tag, or commit. Private repositories work with the machine’s own git credentials, plus GITHUB_TOKEN for github.com.

Module params

Params declared in module.yaml map from cluster.spec.params to environment variables injected into every operation.

In the cluster YAML:

spec:
driver:
source: github.com/your-org/hyve-my-provider
version: v1.0.0
params:
node_size: g4s.kube.medium
node_count: "3"
cluster_type: k3s

Injected as environment variables:

cluster.spec.params keyEnvironment variable
node_sizeHYVE_PARAM_NODE_SIZE
node_countHYVE_PARAM_NODE_COUNT
cluster_typeHYVE_PARAM_CLUSTER_TYPE

The rule is: HYVE_PARAM_ + the key uppercased. Nothing else is converted, so use snake_case keys — a key like node-size produces an unusable variable name.

Standard environment variables

In addition to HYVE_PARAM_* variables, every module operation also receives:

VariableValue
HYVE_CLUSTER_NAMEcluster.metadata.name
HYVE_CLUSTER_REGIONcluster.spec.region
every driverOutputs keyvalues create printed, re-injected as-is

Scripts run with the working directory set to the consuming repository’s root, not the module’s own directory — so don’t source another file from your module by relative path.

Management clusters

Some modules don’t talk to a cloud directly but to another Kubernetes cluster that does the work — a Cluster API module creates Cluster objects on a CAPI management cluster. Name it on the template or cluster, and every operation gets a kubeconfig for it as HYVE_MGMT_KUBECONFIG:

apiVersion: hyve.io/v1alpha1
kind: Template
metadata:
name: gke
spec:
mgmtCluster: capi-mgmt # a cluster hyve already manages
driver:
source: github.com/acme/hyve-capi-module
version: v0.2.0

hyve template create --mgmt-cluster capi-mgmt sets the same field; clusters created from the template inherit it, and a cluster can set its own. A module can also declare a default (spec.requirements.mgmtCluster in module.yaml), which spec.mgmtCluster overrides — but a module meant to be shared across installs should leave it unset, since it names one install’s cluster. With neither set, the module gets no HYVE_MGMT_KUBECONFIG and is expected to manage its own (say, a kind cluster it creates).

The name resolves like any cluster reference: in cluster mode a short name means that cluster in the same environment (capi-mgmt → default-capi-mgmt). Reconcile checks that it exists before running any operation. When the management cluster is hyve’s own host (access.method: primary), cluster mode mints a fresh cluster-admin kubeconfig for each operation; otherwise it’s the kubeconfig the management cluster’s own auth last wrote, so that cluster must have reconciled (or you must have run hyve cluster auth <name>) first. A cluster in another organization can name the host only if a superadmin allows it — see Multi-Tenancy.

Outputs protocol

Operations emit outputs by printing HYVE_KEY=value lines to stdout (anything else on stdout is ignored — log to stderr). Outputs from create are stored in the cluster’s driverOutputs; other operations’ outputs are only read for that run.

Terminal window
# Inside create.yaml — a shell step outputs values for later use
echo "HYVE_CLUSTER_ID=${CLUSTER_ID}"
echo "HYVE_CLUSTER_ENDPOINT=${ENDPOINT}"
echo "HYVE_CLUSTER_STATUS=active"

On every subsequent reconcile, all keys in cluster.spec.driverOutputs are re-injected verbatim as environment variables. This allows, for example, the create operation to output a VPC ID that the delete operation later references.

Status values

The status operation must emit HYVE_CLUSTER_STATUS=<value>, uppercase:

ValueWhat the reconciler does
NOT_FOUNDRuns create — or, if the cluster is marked delete: true, treats it as gone and removes its definition
ACTIVERuns auth, scale on param drift, then applies spec.resources — or runs delete if marked for deletion
FAILEDRetries create — or runs delete if marked for deletion
CREATING / UPDATING / DELETINGSkips this cycle while the operation finishes

Any other value, or none, is reported as an error. Only emit NOT_FOUND when certain — if the provider can’t be reached, exit non-zero instead; a false NOT_FOUND on a cluster marked for deletion drops its definition without running delete.

How auth works

The auth.yaml file has kind: ClusterAuth. Hyve runs it when hyve cluster auth <name> is called, and on every reconcile of an ACTIVE cluster (to apply spec.resources). With exports: KUBECONFIG, hyve sets $KUBECONFIG to a per-cluster file before the script runs; the script writes the kubeconfig there, and hyve cluster auth merges it into ~/.kube/config.

A module can declare one or more named auth methods under spec.methods. Hyve runs the first method by default; pass --method <name> to select a specific one.

auth.yaml
apiVersion: v1
kind: ClusterAuth
metadata:
name: civo-auth
spec:
methods:
- name: local
description: Auth via the Civo CLI. Requires the civo CLI and kubectl.
deps:
- civo
- kubectl
auth:
script: |
civo kubernetes config "$HYVE_CLUSTER_NAME" \
--region "$HYVE_CLUSTER_REGION" > "$KUBECONFIG"
exports: KUBECONFIG

Modules that support more than one auth path — for example an interactive CLI flow and a non-interactive API-based flow — declare each as a separate named method:

spec:
methods:
- name: local
description: Full auth flow via aws-cli. Supports SSO and MFA.
deps: [aws-cli]
auth:
script: |
aws eks update-kubeconfig \
--name "${HYVE_CLUSTER_NAME}" \
--region "${HYVE_CLUSTER_REGION}"
exports: KUBECONFIG
- name: headless
description: Non-interactive auth using the AWS STS HTTP API via curl. No aws-cli required.
deps: []
auth:
script: |
# construct pre-signed STS URL, build kubeconfig with kubectl
export KEEPER_KUBECONFIG="$(cat /tmp/kubeconfig-${HYVE_CLUSTER_NAME}.yaml)"
exports: KEEPER_KUBECONFIG

Method fields

FieldDescription
nameUnique identifier within the module. Used with --method.
descriptionHuman-readable explanation of this auth path.
depsTools the script requires (informational; checked before execution).
auth.scriptShell script that performs the auth. Receives all HYVE_* env vars.
exportsKUBECONFIG (or KEEPER_KUBECONFIG) — hyve points $KUBECONFIG at a per-cluster file before running the script, and the script writes the kubeconfig there. Works unchanged in cluster mode, where hyve relays the file back from the Job.

Backward compatibility

Single-method auth.yaml files using the legacy spec.bootstrap / spec.verify shape continue to work without modification. Hyve wraps them internally as a single method named "default".

hyve.lock — content addressing

hyve.lock maps each source@version reference to a SHA256 digest and download URL:

version: 1
modules:
github.com/your-org/hyve-my-provider@v1.0.0:
source: github.com/your-org/hyve-my-provider@v1.0.0
resolved: https://github.com/your-org/hyve-my-provider/archive/refs/tags/v1.0.0.tar.gz
sha256: "abc123..."

When Hyve downloads a module, it verifies the digest. If the content has changed (e.g. a tag was force-pushed), Hyve refuses to use the module and reports an integrity error.

Keep hyve.lock in your context directory and share it however you share the rest of that directory (committed to git, if you’re using it — see Contexts). This ensures:

  • Every team member uses the exact same module version
  • CI/CD pipelines reproduce the same reconcile behavior
  • Supply-chain tampering is detected

driverOutputs

cluster.spec.driverOutputs is a key-value map populated by the reconciler from a successful create’s HYVE_KEY=value lines, plus hyve’s own HYVE_LAST_PARAMS_HASH. It’s written to a reconciler-owned sidecar file (cluster-state/<name>.state.yaml, a sibling of the clusters/ directory the desired-state YAML lives in), not inline in it — see Reconciler state file. If your context directory is git-backed, that sidecar file gets committed like any other change; hyve itself never commits it for you.

spec:
driverOutputs:
HYVE_CLUSTER_ID: abc-123
HYVE_CLUSTER_ENDPOINT: https://abc-123.k8s.civo.com
HYVE_LAST_PARAMS_HASH: sha256:def456...

Writing a custom module

See Writing a Module for a step-by-step guide — scaffolding with hyve module init, writing each operation, testing from a local path, and publishing.

Auth-only modules

Every clusters/*.yaml requires spec.driver — there is no “driverless” cluster type. But not every cluster needs to be provisionable by Hyve. For a cluster that already exists and was never meant to be created or destroyed by Hyve — a local k3d cluster, or any pre-existing cluster you just want to track and hand out kubeconfig access for — declare the module metadata.type: authOnly and implement only auth.yaml (plus, optionally, status.yaml if you want Hyve to detect the cluster disappearing).

module.yaml
apiVersion: v1
kind: Module
metadata:
name: k3d-auth
version: 0.1.0
description: Kubeconfig access for a local k3d cluster
type: authOnly
# auth.yaml — merges the k3d cluster's kubeconfig
apiVersion: v1
kind: ClusterAuth
metadata:
name: k3d-auth
spec:
methods:
- name: local
auth:
script: |
k3d kubeconfig merge "$HYVE_CLUSTER_NAME" --kubeconfig-merge-default
exports: KUBECONFIG

Scaffold this shape directly with hyve module init <name> --auth-only, which writes only module.yaml (with type: authOnly set) and auth.sh — no create/delete/status/scale stubs.

What changes for an authOnly module:

  • The reconciler treats a missing (or empty) HYVE_CLUSTER_STATUS as ACTIVE instead of reporting an unrecognized status — so hyve reconcile runs the auth operation and applies spec.resources every cycle without needing a status.yaml that reports ACTIVE.
  • spec.delete: true still works: it runs any onDelete workflows, calls the (absent) delete operation as a no-op, and removes the cluster’s YAML from the repository — deregistering it from Hyve without touching the real cluster.
  • hyve module validate requires auth.yaml/auth.sh to be present specifically for authOnly modules, rather than accepting any single operation file.

If a module also declares spec.requirements.tools (e.g. k3d), Hyve checks each tool is on PATH before running any operation and fails with a clear error if one is missing — useful for surfacing “install k3d first” instead of an opaque shell failure.