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 published yet. It is not on the Terraform Registry and not on the OpenTofu registry, so terraform init cannot fetch it. Until it is released you build it and point the CLI at the binary with dev_overrides, which is what the prerequisites below set up. See the Terraform provider guide for the full explanation.

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. A local build of the provider, with a dev_overrides block pointing at it. Install instructions.
  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

Terraform prints a warning that provider development overrides are in effect. That is expected, and it is also your reminder that this is a local build rather than a released provider.

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