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.
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 (/
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 |
||
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 withvulnetix config plan: it shows each object as create, update, delete or no change, with aplanHashthat apply then holds to. Apply writes in one transaction. - Manager. Each object records the manager that applied it (
terraform,consoleorgitops:<repo>).--prunedeletes 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
baseUrland 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.
apiKeyis a secret. The client resolves it (envorfile) 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 anapiKey.envplaceholder, 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
AiFirewallPolicythat 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.