Provision with Terraform
Everything in the quick start can be done in the dashboard. This page is the same onboarding in HCL, for teams whose answer to “where is that configured?” has to be “in the repo”. By the end you will have a route, three environments with their own upstream configuration, and one secret whose value is not in your state file.Prerequisites
- Terraform >= 1.11 or OpenTofu >= 1.11. Write-only attributes — the mechanism that keeps secret values out of state — do not exist below that in either tool.
- The provider — not installable yet; see the notice above and the Terraform provider guide.
- An OAuth client for your tenant, exported as
KNOXCALL_CLIENT_IDandKNOXCALL_CLIENT_SECRET. Create one under Settings → API. - Your tenant slug, and the upstream credential you want KnoxCall to hold.
The module
Around 40 lines. Copy it intomain.tf, change the names and the upstream URL, and read the notes underneath before you apply.
productionis not declared. A route’sbase_environmentdefaults toproduction, andknoxcall_routeowns that environment’s configuration row itself.knoxcall_route_environmentrefuses the base environment for exactly that reason — two resources managing one row is how a module fights itself.{{secret_id:<uuid>}}is resolved server-side, at proxy time. The header your upstream receives carries the real credential; the header your route configuration carries is a reference to a UUID. Nothing in Terraform ever holds the value.value_wois write-only andvalue_wo_versionis the rotation trigger. Terraform never sees a write-only value, so it cannot notice you changed one. Changingvalue_woalone does nothing at all; the integer is what says “this is different now”.- The
for_eachonknoxcall_route_environmentiterates the environment resources, not a list of names. That is what makes Terraform create the environment before the override that references it.
Apply
Call the route
The route is live as soon as the apply finishes:x-knoxcall-route header, decrypted the secret referenced by the route’s injected Authorization header, and forwarded the request upstream with it. Your caller never held the Stripe key, and neither does your state file.
Add -H "x-knoxcall-environment: staging" and the same route resolves through the staging row instead — its own upstream URL, its own rate limit, its own value for the secret. Omit the header and you get the tenant’s default environment.
Rotate the secret
Rotation is one integer:terraform apply sends the new value and nothing else. The plan shows the version change, never the value. Server-side it is recorded as a rotate event in your tenant’s audit log, which is where the history of a value Terraform cannot display actually lives.
What ends up in your state file
The point of the module above is what is missing fromterraform.tfstate: the Stripe key. A value you write into value_wo is read from your configuration, sent to the API, and nullified by Terraform before it can reach a plan or a state file — and KnoxCall’s API never returns a stored secret value, so there is nothing to read back either.
Two things this does not cover, both stated in full on the provider page:
- Credentials KnoxCall mints and hands back once — a webhook’s generated
secret_key, an API key’s plaintext, an OAuth client’s secret, an issued client-certificate private key — are captured into state, because a value the server generates has to beComputed, and the plugin framework forbidsComputedtogether with write-only. Each has a documented alternative; the webhook one is to sign withhmac_key_idinstead, so no signing secret exists anywhere in Terraform. - A rotation performed outside Terraform is invisible to
planunless you pinvalue_version. Terraform has no copy of the value to compare.
Where to go next
- Terraform provider guide — authentication modes, the sandbox data space, the full resource and import-ID map, and the client’s retry/idempotency behaviour.
- Creating your first route — what a route actually does at request time.
- Environments — how per-environment overrides resolve.