Skip to main content

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.
The provider is not installable yet. It has no released version: it is not on the Terraform Registry or the OpenTofu registry, and its source is not public, so you cannot run this page today. It documents the flow ahead of the release. To set up the same route, environments and secret now, follow the quick start in the dashboard. See the Terraform provider guide.

Prerequisites

  1. 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.
  2. The provider — not installable yet; see the notice above and the Terraform provider guide.
  3. An OAuth client for your tenant, exported as KNOXCALL_CLIENT_ID and KNOXCALL_CLIENT_SECRET. Create one under Settings → API.
  4. Your tenant slug, and the upstream credential you want KnoxCall to hold.

The module

Around 40 lines. Copy it into main.tf, change the names and the upstream URL, and read the notes underneath before you apply.
Four things in there are worth understanding rather than copying:
  • production is not declared. A route’s base_environment defaults to production, and knoxcall_route owns that environment’s configuration row itself. knoxcall_route_environment refuses 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_wo is write-only and value_wo_version is the rotation trigger. Terraform never sees a write-only value, so it cannot notice you changed one. Changing value_wo alone does nothing at all; the integer is what says “this is different now”.
  • The for_each on knoxcall_route_environment iterates the environment resources, not a list of names. That is what makes Terraform create the environment before the override that references it.
Each environment needs its own value for the secret, and the provider cannot write those yet. knoxcall_secret.value_wo sets the value for the secret’s base environment — the tenant’s default. A request carrying x-knoxcall-environment: staging looks up the staging value and, if nobody has set one, fails with
Add the other environments’ values in the dashboard (Secrets → your secret → Environments) or through the API:
A per-environment value is not a Terraform resource today. That is a real gap, not a recommendation — it is stated here rather than left for you to discover on the first staging request.

Apply

Call the route

The route is live as soon as the apply finishes:
KnoxCall matched the route by the 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.
Changing value_wo without bumping value_wo_version is inert: the new value is not sent, no diff appears, and the apply reports success. Drive both from one variable where you can.

What ends up in your state file

The point of the module above is what is missing from terraform.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 be Computed, and the plugin framework forbids Computed together with write-only. Each has a documented alternative; the webhook one is to sign with hmac_key_id instead, so no signing secret exists anywhere in Terraform.
  • A rotation performed outside Terraform is invisible to plan unless you pin value_version. Terraform has no copy of the value to compare.

Where to go next