Terrapod is a free, open-source platform replacement for Terraform Enterprise. This release makes the module registry find its own modules, adds OpenBao (or HashiCorp Vault) as a first-class source for variable values, and makes long plan logs navigable.
Highlights
- OpenBao (or HashiCorp Vault) as a variable value source — a variable can hold a reference (
{mount, path, field}) instead of a literal, resolved per run and delivered through the run's Secret, so the secret stays in the server and nothing is copied into Terrapod's database. Static kv-v2 and dynamic engines both work, so a fresh short-lived credential is minted for each run. Kubernetes auth by default, with JWT, AppRole and token available, a private CA and a path allow-list per instance, and an unresolvable reference fails the run rather than silently delivering nothing. - Secrets delivered as files — a reference can name a file (
gcp/adc.json,~/.aws/credentials), and the variable then holds the file's path, so the secret never appears in the environment or in tfvars. One file can carry several fields of a single read: a logic-less template, a whole-secretjsonorenvformat, or a base64-decoded field. Variables reading the same secret share one read per run. - Lease revocation and read auditing — opt in per instance, and a run phase's dynamic-secret leases are revoked once its Job ends rather than waiting out their TTL. Every read is audited, naming the variables, path, phase and outcome, and never a value.
- Diagnostics for the value source — an admin page and API showing each instance's health, login, token TTL and TLS trust, plus a reference Check that tells you whether a reference would resolve without minting a credential.
- Org-wide module autodiscovery — a module autodiscovery rule's repository URL can now name one repository, a whole org or group, or a pattern over one namespace (
owner/terraform-*). Repositories created after the rule register automatically; the ones that existed when it was made are a baseline you register from explicitly, all of them or a chosen subset. Renames are reported, not acted on. - Module autodiscovery rules — saved rules that find the modules in a repository and register them, with a preview before you commit to anything.
- Registry submodules — a module published from a subdirectory of its repository, rather than requiring a repository per module.
- Jump to any change in a plan log — once a plan log is complete, the viewer offers a picker listing every resource change the plan announces, the summary, and every error and warning. Choosing one scrolls to that line, so the reason a plan failed no longer has to be hunted for.
- Ids work with or without their type prefix —
ws-{uuid}and the bare uuid are accepted interchangeably across the API, so an id copied out of the UI or an MCP response works against any endpoint. Emitted ids are unchanged. - A catalog item's module interface — readable through the API, the SDK, the provider, MCP and the UI.
terrapod_run_retry— retrying a run takes what creating one takes, and keeps the destroy flag.- OpenBao first — OpenBao is named first throughout the documentation and the UI, with HashiCorp Vault equally supported; both speak the same API. Identifiers, config keys and API attributes are unchanged.
Bug Fixes
- A malformed id returns a client error instead of
500 Internal server error. Forty-six request paths reported a bad id as a server fault; each now answers with the status its own endpoint already uses for the mistake. - A failed run says why, instead of only "Runner exited with code 1".
- The plan log keeps following a finished phase until the whole log has arrived, so its tail no longer goes missing until you reload.
- The AI plan summary rates the risk of the change, not of the estate.
- A Vault outage during an apply claim returns the run to
confirmedinstead of wedging it. - A configuration archive that is truncated, or that cannot be written out, fails the run with the real cause instead of being stored.
- Each configuration download gets its own temporary file, removed on return.
- The outbound-URL guard no longer resolves a host that an egress proxy will carry.
- The workspace page shows why a request failed, not just that it did.
terrapod_run_plan_jsonvalidates its input and defaults to a compact view of what changes.- The Terraform provider loads again: the module autodiscovery rule's reserved
providerattribute is renamed. - Registry module subdirectories converge, and retry and destroy follow
can-queue-destroy. - The module interface summary pluralises its counts.
- Module autodiscovery rules and their per-repository scan state now replicate, so a follower can apply a module an autodiscovery rule registered.
- An org-wide "register all" scan, and its rebaseline, are bounded and resume from where they stopped, rather than walking every repository in one request.
- Over-length module autodiscovery rule fields return 422 instead of 500.
- The Vault lease watcher reads its pending set in batches instead of walking all of it every reconcile cycle.
- The per-run Secret is sized as a whole before it is created, so a Vault file that fits its own cap but pushes the Secret past Kubernetes' limit fails with a clear message.
terrapod_module_autodiscovery_rule_repositoriesreturns the candidates, skips, previous locations and next-check time the API already provides.- Setting a module autodiscovery limit to
0in Helm values now takes effect; the chart previously replaced a zero with the default. - Module autodiscovery's glob walk runs off the event loop in preview, polling and scans.
Security
- The outbound-URL guard unwraps IPv4 embedded in NAT64 (
64:ff9b::/96) and 6to4 addresses, so a forbidden address cannot be reached through its IPv6 spelling. - The Vault reference check writes a
vault.readaudit entry, like every other read. - The reference check no longer lists a secret's key names for a workspace using local execution, where a Vault-sourced variable could never have been delivered anyway.
Upgrade notes
Three changes alter behaviour for an existing configuration. Two are deliberate uses of the one MINOR exception — tightening a permission that was wrongly open — and they are named here because an operator should meet them before their next apply fails rather than after.
- Retrying a run now needs what creating that run needs. It used to need
run:cancel, which is aplan-tier capability; on an auto-applying workspace a retry is an apply. It now needsrun:planfor a plan-only run, andrun:apply(plusrun:apply-destroyfor a destroy) for an apply-capable one. A role holdingrun:cancelwithoutrun:applywill get a 403 where it previously succeeded — grant the capability the run's kind requires. See RBAC capabilities. - Notification webhooks and run-task callbacks are no longer delivered to loopback or link-local addresses. Neither has a legitimate receiver on it and both are a request-forgery primitive, so they are refused whatever the configuration. A webhook pointing at
127.0.0.1, alocalhostname or169.254.xstops delivering on upgrade; name the host inapi.config.outbound_requests.allowed_hosts, or its network inallowed_cidrs, to keep it. Private space (RFC1918) is unaffected by default — delivering to an in-cluster service stays the ordinary case. The refusal is an ordinary failed delivery whose message names the address and the allow-list that would permit it. See the runbook. - Provider:
terrapod_registry_module.vcs_tag_patternbecomes Optional+Computed, fixing a perpetual plan diff for anyone who left it unset. The one consequence: removing it from configuration no longer resets it to the server default, it keeps the current value.
Otherwise:
- One expand-only migration per change, and the release line stays a prefix of main. Upgrading from 1.6 runs only the migrations it has not seen. A revision-graph test now guards that property.
- Nothing else in this release is on by default that was not before. The value source is off until
api.config.vault.enabledand at least one instance are set; lease revocation is opt-in per instance. - File delivery needs listeners on 1.7. An older listener ignores the run's
vault-files, and the run then fails naming a file that was never written. Upgrade listeners before using it. - v1.5 is out of support from this release. v1.6 is now the supported previous minor and receives security fixes.
Status
Stable — the seventh minor release.
Full Changelog: v1.6.4...v1.7.0