Skip to content

AiProvider

A custom AI Firewall provider owned by the tenant: a model server the gateway forwards to, beside the built-in catalogue. Fields, YAML, HCL and CLI samples, and how apply treats it.

Checked against config-schemas@v1.18.0, terraform-provider-resolve@7584dbc, cli@v4.10.2

A custom AI Firewall provider owned by the tenant: a model server the gateway forwards to, beside the built-in catalogue. Only this tenant sees or uses it. metadata.name is the provider slug, which becomes the gateway path prefix (///v1/…) and is what AiFirewallPolicy providers and models refer to. A tenant has at most 10 custom providers, and a slug that a built-in provider uses is refused at apply.

apiVersion vulnetix.com/v1
kind AiProvider
Group AI firewall
Status Served. The config engine plans and applies it.
Objects per tenant many, keyed by metadata.name
Token and role An owner, or a service account with the developer role (its ApiKey), or an Authentik access token for either. A member is refused with HTTP 403. See Credentials for automation.
Secret fields spec.apiKey: read on the client from valueFrom, sent once, never exported or shown in a plan
Terraform resource vulnetix_ai_provider, imported by name (see Terraform and OpenTofu)
CLI vulnetix config apply, CLI v3.157.0 and later.
Pinned schema v1.18.0/AiProvider.schema.json
Newest schema latest/AiProvider.schema.json
First released in v1.1.0
Print the built-in copy vulnetix config schema AiProvider

Minimal

Only the fields the kind requires. From examples/AiProvider/minimal.yaml.

YAML

# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/AiProvider.schema.json
apiVersion: vulnetix.com/v1
kind: AiProvider
metadata:
  name: acme-vllm
spec:
  protocol: openai-chat
  baseUrl: https://llm.acme.example
  auth: bearer

HCL (resource vulnetix_ai_provider)

# Only the arguments the kind requires. From config-schemas examples/AiProvider/minimal.yaml.

resource "vulnetix_ai_provider" "acme_vllm" {
  name     = "acme-vllm"
  auth     = "bearer"
  base_url = "https://llm.acme.example"
  protocol = "openai-chat"
}

CLI (applies it)

export VULNETIX_ORG_ID=...    # the organization UUID
export VULNETIX_API_KEY=...   # the ApiKey of a developer or owner service account
vulnetix config apply -f - --manager gitops:acme/platform --auto-approve <<'EOF'
# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/AiProvider.schema.json
apiVersion: vulnetix.com/v1
kind: AiProvider
metadata:
  name: acme-vllm
spec:
  protocol: openai-chat
  baseUrl: https://llm.acme.example
  auth: bearer
EOF

Advanced

Every field the kind supports, with a when expression where it takes one. From examples/AiProvider/advanced.yaml.

YAML

# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/AiProvider.schema.json
apiVersion: vulnetix.com/v1
kind: AiProvider
metadata:
  name: acme-vllm
  labels:
    team: ml-platform
  annotations:
    runbook: https://wiki.example.com/ml-platform/vllm
spec:
  displayName: ACME vLLM cluster
  serverKind: openai-compatible
  protocol: openai-responses
  baseUrl: https://llm.acme.example
  auth: bearer
  apiKey:
    env: ACME_VLLM_KEY
  models:
    - id: qwen3-coder-30b
      name: Qwen3 Coder 30B
      contextWindow: 262144
      maxTokens: 65536
      images: false
    - id: qwen2.5-vl-72b
      name: Qwen2.5 VL 72B
      contextWindow: 131072
      maxTokens: 8192
      images: true
---
apiVersion: vulnetix.com/v1
kind: AiProvider
metadata:
  name: acme-ollama
spec:
  displayName: ACME Ollama
  serverKind: ollama
  protocol: openai-chat
  baseUrl: https://ollama.acme.example:11434
  auth: x-api-key
  apiKey:
    env: ACME_OLLAMA_KEY
  models:
    - id: llama3.1:70b
      name: Llama 3.1 70B
      contextWindow: 131072
      maxTokens: 0

HCL (resource vulnetix_ai_provider)

# Every argument the kind supports. From config-schemas examples/AiProvider/advanced.yaml.

variable "acme_vllm_api_key" {
  type      = string
  sensitive = true
  ephemeral = true
}
variable "acme_ollama_api_key" {
  type      = string
  sensitive = true
  ephemeral = true
}

resource "vulnetix_ai_provider" "acme_vllm" {
  name               = "acme-vllm"
  api_key_wo         = var.acme_vllm_api_key
  api_key_wo_version = 1
  auth               = "bearer"
  base_url           = "https://llm.acme.example"
  display_name       = "ACME vLLM cluster"
  models = [
    {
      context_window = 262144
      id             = "qwen3-coder-30b"
      images         = false
      max_tokens     = 65536
      name           = "Qwen3 Coder 30B"
    },
    {
      context_window = 131072
      id             = "qwen2.5-vl-72b"
      images         = true
      max_tokens     = 8192
      name           = "Qwen2.5 VL 72B"
    },
  ]
  protocol    = "openai-responses"
  server_kind = "openai-compatible"
  labels = {
    team = "ml-platform"
  }
  annotations = {
    runbook = "https://wiki.example.com/ml-platform/vllm"
  }
}

resource "vulnetix_ai_provider" "acme_ollama" {
  name               = "acme-ollama"
  api_key_wo         = var.acme_ollama_api_key
  api_key_wo_version = 1
  auth               = "x-api-key"
  base_url           = "https://ollama.acme.example:11434"
  display_name       = "ACME Ollama"
  models = [
    {
      context_window = 131072
      id             = "llama3.1:70b"
      max_tokens     = 0
      name           = "Llama 3.1 70B"
    },
  ]
  protocol    = "openai-chat"
  server_kind = "ollama"
}

CLI (applies it)

export VULNETIX_ORG_ID=...    # the organization UUID
export VULNETIX_API_KEY=...   # the ApiKey of a developer or owner service account
export ACME_VLLM_KEY=...   # read on this machine by valueFrom, never written to the document
export ACME_OLLAMA_KEY=...   # read on this machine by valueFrom, never written to the document
vulnetix config apply -f - --manager gitops:acme/platform --auto-approve <<'EOF'
# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/AiProvider.schema.json
apiVersion: vulnetix.com/v1
kind: AiProvider
metadata:
  name: acme-vllm
  labels:
    team: ml-platform
  annotations:
    runbook: https://wiki.example.com/ml-platform/vllm
spec:
  displayName: ACME vLLM cluster
  serverKind: openai-compatible
  protocol: openai-responses
  baseUrl: https://llm.acme.example
  auth: bearer
  apiKey:
    env: ACME_VLLM_KEY
  models:
    - id: qwen3-coder-30b
      name: Qwen3 Coder 30B
      contextWindow: 262144
      maxTokens: 65536
      images: false
    - id: qwen2.5-vl-72b
      name: Qwen2.5 VL 72B
      contextWindow: 131072
      maxTokens: 8192
      images: true
---
apiVersion: vulnetix.com/v1
kind: AiProvider
metadata:
  name: acme-ollama
spec:
  displayName: ACME Ollama
  serverKind: ollama
  protocol: openai-chat
  baseUrl: https://ollama.acme.example:11434
  auth: x-api-key
  apiKey:
    env: ACME_OLLAMA_KEY
  models:
    - id: llama3.1:70b
      name: Llama 3.1 70B
      contextWindow: 131072
      maxTokens: 0
EOF

Fields

Paths use dots for nesting and [] for the items of a list. Required means required whenever the parent object is present. A default is what the server uses when the field is left out.

Field Type Required Default Allowed values Secret Description
apiVersion string yes exactly vulnetix.com/v1 The document format version. Every kind in this release is vulnetix.com/v1.
kind string yes exactly AiProvider Always AiProvider.
metadata object yes no other keys
metadata.name string yes not v1, api, health, models, openai, anthropic, cloudflare-workers-ai, cloudflare-ai-gateway, openrouter, google-gemini, ollama, llama-server, groq, deepseek, fireworks, mistral, together, xai, moonshot, minimax, alibaba, github-copilot, kiro, huggingface, openai-compatible, typesafe, strands-decider; pattern ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ The object’s name, unique per kind within a tenant. It is the natural key: plan, apply, export and the Terraform provider all address an object by kind and name.
metadata.org string no format uuid The tenant (organization UUID) the document belongs to. When set, plan and apply refuse to run against any other tenant.
metadata.labels object no map of string to string (length 0 to 256); keys match ^[a-z0-9]([a-z0-9._/-]{0,61}[a-z0-9])?$ Free key/value labels. Stored with the object, never interpreted by the server.
metadata.annotations object no map of string to string (length 0 to 4096) Free key/value notes for people and tools, such as a ticket link.
spec object yes no other keys
spec.displayName string no pattern ^[^\u0000-\u001f\u007f]*$; length 1 to 64 The label the console shows. Defaults to the slug.
spec.serverKind string no openai-compatible openai-compatible, ollama, llama-server The server type. ollama and llama-server speak openai-chat only.
spec.protocol string yes openai-chat, openai-responses, anthropic-messages The request surface the server speaks: openai-chat (Chat Completions), openai-responses (Responses, which Codex needs) or anthropic-messages (Messages, which Claude Code speaks). The gateway refuses requests on any other surface.
spec.baseUrl string yes pattern ^https://[^/?#\s]+/?$; format uri; length 0 to 1024 https://host[:port] and nothing more: no path, query or fragment. The gateway adds the protocol’s own path. The host must be publicly reachable; private, loopback and link-local addresses are refused at apply, and again at every connection.
spec.auth string yes bearer, x-api-key, cf-aig How the API key is sent. bearer: Authorization: Bearer . x-api-key: an x-api-key header, plus anthropic-version for anthropic-messages. cf-aig: a cf-aig-authorization header, for Cloudflare AI Gateway.
spec.apiKey object no 1 to 1 keys; no other keys yes Where the client reads the API key. It is stored encrypted for this tenant and provider, and is never exported or shown in a plan. Left out, the key on the server is kept; with no key at all, the gateway refuses requests with provider_key_missing.
spec.apiKey.env string no pattern ^[A-Za-z_][A-Za-z0-9_]*$ Name of an environment variable.
spec.apiKey.file string no length 1 to any Path to a file holding the value. Trailing newlines are trimmed.
spec.models array of object no 0 to 100 items The models the server offers.
spec.models[].id string yes pattern ^[^\s\u0000-\u001f\u007f]+$; length 1 to 128 The model id the server expects, without spaces.
spec.models[].name string no pattern ^[^\u0000-\u001f\u007f]*$; length 0 to 128
spec.models[].contextWindow integer no 0 to 10000000 Tokens. 0 is unknown.
spec.models[].maxTokens integer no 0 to 10000000 Output tokens. 0 is unknown.
spec.models[].images boolean no Accepts image input.

Rules

Conditions the schema enforces beyond each field’s own type. vulnetix config validate and the engine’s plan report a document that breaks one.

On Rule
spec When serverKind is one of ollama, llama-server: protocol must be openai-chat.

Applying this kind

  • Command. vulnetix config apply -f <file>; the CLI line above says from which release. Plan first with vulnetix config plan: it shows each object as create, update, delete or no change, with a planHash that apply then holds to. Apply writes in one transaction.
  • Manager. Each object records the manager that applied it (terraform, console or gitops:<repo>). --prune deletes only objects its own manager owns. See The manager and the config engine.
  • Status. Applied, Unchanged or Deleted, with no other status fields. The phases the engine reports are explained under status phases.

How apply treats it

  • Apply runs the console’s own checks: the provider validator, the reserved built-in slugs, the public-host check on baseUrl and the cap of ten custom providers per tenant.
  • The console has no update. Apply rewrites the same columns in place and keeps the provider’s uuid, which the stored key’s encryption context is bound to.
  • apiKey is a secret. The client resolves it (env or file) on its own machine and sends it once. It is encrypted with the BYOK KMS key, never read back into a spec, exported or hashed. An export writes an apiKey.env placeholder, and a plan shows the key as changed only when the value you supply differs from the stored one.
  • Deleting the object revokes the vault entries filed against the slug first, as deleting the provider in the console does.
  • An AiFirewallPolicy that names the provider is applied after it.

Terraform and OpenTofu

The resource is vulnetix_ai_provider in the vulnetix/resolve provider. Its arguments are the spec in snake case, beside name, labels and annotations from metadata. The Minimal and Advanced HCL are above. The provider’s own reference is vulnetix_ai_provider.

Import an object that already exists by its name:

# Import an existing AiProvider by its name (metadata.name).
terraform import vulnetix_ai_provider.acme_vllm acme-vllm

A secret is two arguments, never stored in the plan or the state: api_key_wo (write-only) and api_key_wo_version. Change the version number to send a new value. See Secrets.

Install, authentication and how state is read: Terraform and OpenTofu.

More examples

examples/AiProvider/vllm.yaml from Vulnetix/config-schemas:

# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/AiProvider.schema.json
apiVersion: vulnetix.com/v1
kind: AiProvider
metadata:
  name: acme-vllm
spec:
  displayName: ACME vLLM cluster
  serverKind: openai-compatible
  protocol: openai-responses
  baseUrl: https://llm.acme.example
  auth: bearer
  apiKey:
    env: ACME_VLLM_KEY
  models:
    - id: qwen3-coder-30b
      name: Qwen3 Coder 30B
      contextWindow: 262144
      maxTokens: 65536

Check a file with vulnetix config validate -f <file>. See Validate documents. In an editor, start the file with the schema comment.

Edit this pageSources: config-schemas/schemas/AiProvider.schema.json, config-schemas/schemas/common.schema.json, config-schemas/examples/AiProvider/minimal.yaml, config-schemas/examples/AiProvider/advanced.yaml, config-schemas/examples/AiProvider/vllm.yaml, terraform-provider-resolve/examples/resources/vulnetix_ai_provider/minimal.tf, terraform-provider-resolve/examples/resources/vulnetix_ai_provider/advanced.tf, vdb-site/docs/platform-iac-engine.md, vdb-site/api/internal/handler/config_engine_kinds.go, vdb-site/api/internal/config/aiprovider.go
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.