Integration guide
Wire Terramantle into your existing Terraform and OpenTofu workflows: configure Pipeline Trust, point the state backend at a workspace, publish modules from CI, and understand how providers are signed.
Overview
If you know Terraform or OpenTofu, you already know how to use Terramantle. There is no new CLI to learn, no plugin to install, and no wrapper around your workflow. Terramantle speaks the registry and HTTP backend protocols your tooling already speaks - point it at Terramantle, and your existing configuration runs unchanged.
Under the hood it is a private module, provider, and state registry that scans, versions, and signs everything it stores - but from your side it is just another registry endpoint.
Three things you can do with Terramantle:
- Module registry - host and version your private modules with semver constraints
- Provider registry - mirror and scan providers before they reach your environment
- State backend - store state with scanning, locking, and audit logs built in
All integrations authenticate via a registry token (tm_tok_...) or Pipeline Trust from GitHub Actions or GitLab CI. Sign up at app.terramantle.dev to get started.
Terramantle stores, scans, versions, and serves artefacts - it does not run your pipelines or decide version numbers. Computing versions, cutting releases, and ordering plan/apply stay with your CI, where that logic belongs. Terramantle takes what your pipeline produces and makes it immutable, signed, and discoverable.
Pipeline Trust (OIDC)
Pipeline Trust is the backbone of Terramantle. Instead of minting and rotating long-lived tokens for every pipeline, your CI runner proves who it is with a short-lived OIDC token that GitHub Actions or GitLab CI already issues natively. Terramantle verifies that token against a trust rule you control - no secrets stored, nothing to leak, nothing to rotate.
That same handshake underpins every interaction with the registry. Whether a pipeline is publishing a module, mirroring a provider, or reading and writing state, the identity check is identical: your runner presents its OIDC token and Terramantle scopes access to exactly that operation. One trust model, enforced consistently across modules, providers, and state - so authentication stops being a per-secret problem and becomes a property of the pipeline itself.
Setup
- In Terramantle, navigate to Settings → Pipeline Trust and add a trust rule for your GitHub repository.
- Update your Terraform / OpenTofu backend configuration
- Add a step in your respective pipeline tool to add token permissions;
permissions:
id-token: write
contents: read
steps:
- name: Publish via OIDC (indicitive purposes only)
run: |
TOKEN=$(curl -sH "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=terramantle" | jq -r '.value')
tar -czf module.tar.gz -C . .
curl -X PUT \
"https://registry.terramantle.dev/v1/modules/<YOUR_ORG>/vpc/aws/1.2.3" \
-u "terramantle-bot:$TOKEN" \
--data-binary @module.tar.gzNo credentials are stored in CI. The OIDC token is exchanged at runtime and scoped to the specific publish operation.
State backend
Terramantle implements the standard HTTP State Backend, so Terraform and OpenTofu talk to it natively. No wrapper, no migration tooling. Point the http backend at your workspace endpoint and state lives in Terramantle with versioning, locking, and audit logging built in.
Every state push is scanned automatically for secrets, exposed endpoints, and known vulnerabilities, so drift and accidental exposure surface at the point they happen rather than in a later review.
Backend configuration
terraform {
backend "http" {
address = "https://registry.terramantle.dev/state/<YOUR_ORG>/<YOUR_WORKSPACE>"
username = "tm_tok_..."
lock_address = "https://registry.terramantle.dev/state/<YOUR_ORG>/<YOUR_WORKSPACE>.lock"
unlock_address = "https://registry.terramantle.dev/state/<YOUR_ORG>/<YOUR_WORKSPACE>.lock"
}
}Replace <YOUR_ORG> and <YOUR_WORKSPACE> with your organisation and workspace names from the Terramantle dashboard. Your token is available under Settings → Bot Tokens.
Terramantle is the backend: it persists, versions, locks, and scans state. It does not run plan or apply, order workspaces, or trigger deployments - that orchestration stays in your pipeline, exactly where it does today.
Point the backend at Terramantle and your existing run flow is unchanged. See terramantle/state-sample-mono-repo for a working setup where CI drives the runs and Terramantle holds the state.
Module publishing
Publish a module by uploading a gzipped tar archive with an HTTP PUT. The URL path encodes the namespace, module name, provider, and version, so a publish is an explicit, immutable release rather than a mutable Git ref. Drive it straight from CI on tag - authenticate with Pipeline Trust and consumers resolve the new version through the standard registry protocol.
The version in that URL is yours to decide. Terramantle records exactly what you publish and refuses to overwrite an existing version, but it does not compute version numbers or bump them for you - that is the author's responsibility, handled in CI. A common pattern is to derive the version from conventional commits with semantic-release so each module in a monorepo versions independently on merge.
terramantle/modules-sample-mono-repo is a real monorepo you can review. It uses semantic-release to compute a per-module version from conventional commits and publishes each module to Terramantle over OIDC - no stored secrets, and versioning owned entirely by the pipeline.
# Package your module
tar -czf module.tar.gz -C /path/to/module .
# Publish
curl -X PUT \
"https://registry.terramantle.dev/v1/modules/<YOUR_ORG>/vpc/aws/1.2.3" \
-u "terramantle-bot:tm_tok_..." \
--data-binary @module.tar.gzState locking
Terramantle uses the standard Terraform HTTP backend locking convention. Lock requests are sent as LOCK HTTP method to the lock address; unlock uses UNLOCK.
The lock address is your state address with a .lock suffix:
# State address
https://registry.terramantle.dev/state/<YOUR_ORG>/<YOUR_WORKSPACE>
# Lock address
https://registry.terramantle.dev/state/<YOUR_ORG>/<YOUR_WORKSPACE>.lockTerraform and OpenTofu manage locking automatically during plan and apply, so most teams never touch it directly. If a lock is left stuck (for example after a crashed or cancelled run), force-unlock with the ID from the conflict message:
terraform force-unlock <LOCK_ID> # or: tofu force-unlock <LOCK_ID>The lock ID appears in the error output when a conflict occurs, and in the Terramantle dashboard under the workspace's state history.
Provider signing
Every provider you run receives your infrastructure credentials, so what you consume matters as much as what you write. Most provider signatures only prove who signed - that a key signed some bytes. They say nothing about whether anyone looked at the code. A Terramantle signature is bound to a pipeline of gates that all pass before the HSM signs, so it makes a claim you can actually defend to a security team:
"Terramantle signed it" means "Terramantle compiled it, ran SAST + AI intent analysis + AV, and all passed - a defensible trust claim, not a blind one."
The signing pipeline
Every gate runs trusted-side, independently of the build, and every gate has to pass before signing happens. A gate computed inside the thing it gates is worthless. Select a gate to see what it does.
Semgrep runs against the full source tree with vendored rules. Static analysis before a single byte is compiled.
Secure by design
The stages are separated so a compromise in one cannot forge trust in another. The builder compiles but holds no signing key, has no outbound network egress, and cannot reach the signer - so a remote code execution (RCE) in the build cannot mint a valid signature. A separate hasher re-reads the artefacts from object storage and computes the authoritative SHA256SUMS, and the signer (an HSM, RSA-4096 over PKCS#11) only signs hashes it can map back to a passed gate verdict.
High-confidence findings - an anti-malware hit, or malicious intent flagged with high confidence - fail the pipeline outright and require an explicit human override to release. Nothing ships on a maybe.
Troubleshooting
401 Unauthorized
Your token is missing, expired, or lacks permission for the operation. Check that your token is being sent (HTTP Basic via curl -u "terramantle-bot:$TOKEN") and that it has the correct namespace scope. Bot tokens are scoped to specific namespaces - verify in Settings → Bot Tokens.
404 Not Found on module source
The module source path must match the format registry.terramantle.dev/<org>/<name>/<provider> exactly. Confirm the module published successfully in the Terramantle dashboard, and that the version constraint in your configuration can resolve to a published version.
State lock conflict
Another process holds the lock. Wait for it to finish, or force-unlock with force-unlock <LOCK_ID> if the process has crashed. The lock ID is in the error output.
Enable debug logging
TF_LOG=DEBUG terraform initDebug output includes HTTP request and response details. Look for registry.terramantle.dev entries to trace the registry protocol exchange.
Still stuck?
Email issue@terramantle.dev with the debug log output and your workspace URL. We'll get back to you promptly.