VaultEntry
One secret in the tenant's vault: an AI provider key, an environment variable for Pix sandboxes and Belai hosts, or a provider management key. Fields, YAML, HCL and CLI samples, and how apply treats it.
One secret in the tenant’s vault: an AI provider key, an environment variable for Pix sandboxes and Belai hosts, or a provider management key, with the grants that say where it may be used. The value is encrypted with KMS before it is stored, is sent once by the client, and is never exported, shown in a plan or returned by any endpoint. No grant means no access, and inside one entry a deny beats an allow. Two active entries that fill the same slot (a provider for provider keys, a variable name for environment variables) may not cover the same subject: a plan that would create such an overlap is refused. Deleting the document revokes the entry: its value is destroyed and it covers nothing, and the vault keeps a stub with its history. Connector secrets (forge tokens, the Wiz client secret) are not VaultEntry documents: they are created through their integration kinds. Automatic rotation is set up in the console and an apply leaves it as it is.
apiVersion |
vulnetix.com/v1 |
kind |
VaultEntry |
| Group | Vault |
| Status | Served. The config engine plans and applies it. |
| Objects per tenant | many, keyed by metadata.name |
| Token and role | An owner or admin session, or a vmt_ config management token that lists VaultEntry in its kinds and has the owner role. An admin or member token cannot carry this kind. |
| Secret fields | spec.value: read on the client from valueFrom, sent once, never exported or shown in a plan |
| Terraform resource | vulnetix_vault_entry, imported by name (see Terraform and OpenTofu) |
| CLI | vulnetix config apply, CLI v3.168.0 and later. |
| Pinned schema | v1.15.0/VaultEntry.schema.json |
| Newest schema | latest/VaultEntry.schema.json |
| First released in | v1.5.0 |
| Print the built-in copy | vulnetix config schema VaultEntry |
Minimal
Only the fields the kind requires. From examples/VaultEntry/minimal.yaml.
YAML
# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/VaultEntry.schema.json
apiVersion: vulnetix.com/v1
kind: VaultEntry
metadata:
name: npm-token
spec:
type: env
name: NPM_TOKEN
HCL (resource vulnetix_vault_entry)
# Only the arguments the kind requires. From config-schemas examples/VaultEntry/minimal.yaml.
resource "vulnetix_vault_entry" "npm_token" {
name = "npm-token"
entry_name = "NPM_TOKEN"
type = "env"
}
CLI (applies it)
export VULNETIX_CONFIG_TOKEN=vmt_... # a config management token
vulnetix config apply -f - --manager gitops:acme/platform --auto-approve <<'EOF'
# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/VaultEntry.schema.json
apiVersion: vulnetix.com/v1
kind: VaultEntry
metadata:
name: npm-token
spec:
type: env
name: NPM_TOKEN
EOF
Advanced
Every field the kind supports, with a when expression where it takes one. From examples/VaultEntry/advanced.yaml.
YAML
# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/VaultEntry.schema.json
# An Anthropic key for the AI Firewall and two sandboxes, an environment
# variable for every Belai host but one, and the OpenAI admin key the vault
# rotates provider keys with.
apiVersion: vulnetix.com/v1
kind: VaultEntry
metadata:
name: anthropic-production
labels:
team: platform
annotations:
ticket: https://tracker.example.com/SEC-218
spec:
type: provider_key
provider: anthropic
name: Anthropic production
value: { env: VULNETIX_ANTHROPIC_KEY }
grants:
- surface: ai_firewall
effect: allow
all: true
- surface: pix_sandbox
effect: allow
ids:
- 3f1c2a9e-8b4d-4c7a-9e21-5d6f7a8b9c01
- 7a2e4b6c-1d3f-4e5a-8b7c-9d0e1f2a3b4c
expiresAt: 2027-06-30T00:00:00Z
---
apiVersion: vulnetix.com/v1
kind: VaultEntry
metadata:
name: npm-token
spec:
type: env
name: NPM_TOKEN
value: { env: VULNETIX_NPM_TOKEN }
grants:
- surface: belai_host
all: true
- surface: belai_host
effect: deny
ids:
- 0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e
---
apiVersion: vulnetix.com/v1
kind: VaultEntry
metadata:
name: openai-admin
spec:
type: management
provider: openai
name: OpenAI admin
value: { env: VULNETIX_OPENAI_ADMIN_KEY }
HCL (resource vulnetix_vault_entry)
# Every argument the kind supports. From config-schemas examples/VaultEntry/advanced.yaml.
variable "anthropic_production_value" {
type = string
sensitive = true
ephemeral = true
}
variable "npm_token_value" {
type = string
sensitive = true
ephemeral = true
}
variable "openai_admin_value" {
type = string
sensitive = true
ephemeral = true
}
resource "vulnetix_vault_entry" "anthropic_production" {
name = "anthropic-production"
entry_name = "Anthropic production"
expires_at = "2027-06-30T00:00:00Z"
grants = [
{
all = true
effect = "allow"
surface = "ai_firewall"
},
{
effect = "allow"
ids = ["3f1c2a9e-8b4d-4c7a-9e21-5d6f7a8b9c01", "7a2e4b6c-1d3f-4e5a-8b7c-9d0e1f2a3b4c"]
surface = "pix_sandbox"
},
]
provider_slug = "anthropic"
type = "provider_key"
value_wo = var.anthropic_production_value
value_wo_version = 1
labels = {
team = "platform"
}
annotations = {
ticket = "https://tracker.example.com/SEC-218"
}
}
resource "vulnetix_vault_entry" "npm_token" {
name = "npm-token"
entry_name = "NPM_TOKEN"
grants = [
{
all = true
surface = "belai_host"
},
{
effect = "deny"
ids = ["0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e"]
surface = "belai_host"
},
]
type = "env"
value_wo = var.npm_token_value
value_wo_version = 1
}
resource "vulnetix_vault_entry" "openai_admin" {
name = "openai-admin"
entry_name = "OpenAI admin"
provider_slug = "openai"
type = "management"
value_wo = var.openai_admin_value
value_wo_version = 1
}
CLI (applies it)
export VULNETIX_CONFIG_TOKEN=vmt_... # a config management token
export VULNETIX_ANTHROPIC_KEY=... # read on this machine by valueFrom, never written to the document
export VULNETIX_NPM_TOKEN=... # read on this machine by valueFrom, never written to the document
export VULNETIX_OPENAI_ADMIN_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/VaultEntry.schema.json
# An Anthropic key for the AI Firewall and two sandboxes, an environment
# variable for every Belai host but one, and the OpenAI admin key the vault
# rotates provider keys with.
apiVersion: vulnetix.com/v1
kind: VaultEntry
metadata:
name: anthropic-production
labels:
team: platform
annotations:
ticket: https://tracker.example.com/SEC-218
spec:
type: provider_key
provider: anthropic
name: Anthropic production
value: { env: VULNETIX_ANTHROPIC_KEY }
grants:
- surface: ai_firewall
effect: allow
all: true
- surface: pix_sandbox
effect: allow
ids:
- 3f1c2a9e-8b4d-4c7a-9e21-5d6f7a8b9c01
- 7a2e4b6c-1d3f-4e5a-8b7c-9d0e1f2a3b4c
expiresAt: 2027-06-30T00:00:00Z
---
apiVersion: vulnetix.com/v1
kind: VaultEntry
metadata:
name: npm-token
spec:
type: env
name: NPM_TOKEN
value: { env: VULNETIX_NPM_TOKEN }
grants:
- surface: belai_host
all: true
- surface: belai_host
effect: deny
ids:
- 0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e
---
apiVersion: vulnetix.com/v1
kind: VaultEntry
metadata:
name: openai-admin
spec:
type: management
provider: openai
name: OpenAI admin
value: { env: VULNETIX_OPENAI_ADMIN_KEY }
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 VaultEntry |
Always VaultEntry. | ||
metadata |
object | yes | no other keys | |||
metadata.name |
string | yes | 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.type |
string | yes | provider_key, env, management |
provider_key: an AI provider’s API key, handed to the AI Firewall gateway, Pix sandboxes and Belai hosts that hold a grant. env: an environment variable set in Pix sandboxes and on Belai hosts that hold a grant. management: a provider admin key that the vault uses to rotate provider keys; it is never granted to a surface. The type cannot change: declare a new entry instead. | ||
spec.provider |
string | no | pattern ^[a-z0-9][a-z0-9._-]{0,63}$ |
The AI Firewall provider slug the key is for, a built-in provider or one of the tenant’s AiProvider objects. Required for provider_key and management, and not allowed for env. The provider must exist and be enabled when the plan runs. It cannot change: declare a new entry instead. | ||
spec.name |
string | yes | length 1 to 64 | For env, the variable name: upper case letters, digits and underscore, starting with a letter or underscore. Names such as PATH, HOME and LD_PRELOAD, and names starting LD_, BELAI_, VULNETIX_, VX_, BASH_ or SSH_, are reserved and refused at plan. A variable’s name cannot change: declare a new entry instead. For provider_key and management, the label the console shows: 1 to 64 letters, digits, spaces or . _ : / @ + -. | ||
spec.value |
object | no | 1 to 1 keys; no other keys | yes | Where the client reads the secret value. Required when the entry is created; left out, the stored value is kept. Supplying the value the entry already holds is no change; a different value makes a new version and keeps the old one’s fingerprint in the entry’s history. An env value is at least 8 characters, so that it can be scrubbed from output reliably, and at most 4096; a key has no line breaks. | |
spec.value.env |
string | no | pattern ^[A-Za-z_][A-Za-z0-9_]*$ |
Name of an environment variable. | ||
spec.value.file |
string | no | length 1 to any | Path to a file holding the value. Trailing newlines are trimmed. | ||
spec.grants |
array of object | no | 0 to 12 items | Where the value may be used. Each grant names a surface and either every subject on it or a list of subject ids. Left out or empty, the entry is stored but nothing may use it. At most 12 grants. | ||
spec.grants[].surface |
string | yes | pix_sandbox, belai_host, ai_firewall |
pix_sandbox: Pix sandboxes, by their canonical sandbox id, which survives relaunches. belai_host: self-hosted Belai hosts, by host id. ai_firewall: AI Firewall callers, by the id of the principal that owns the Vulnetix API key making the request. | ||
spec.grants[].effect |
string | no | allow |
allow, deny |
allow lets the subjects use the value; deny takes subjects away from an allow, and wins over it. | |
spec.grants[].all |
boolean | no | true covers every subject on the surface, now and later. A grant is all or a list of ids, not both. | |||
spec.grants[].ids |
array of string | no | 1 to 100 items; each item: format uuid |
The subjects covered, as UUIDs. 1 to 100 ids; duplicates are dropped and the list is kept sorted. | ||
spec.expiresAt |
string | no | format date-time, or format date |
When the value stops being handed out. Left out, it never expires. A new expiry must be at least a minute and at most 10 years away; an expired entry is refused with vault_key_expired rather than decrypted. |
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 type is env: name must be pattern ^[A-Z_][A-Z0-9_]{0,63}$. Otherwise: provider is required and name must be pattern ^[A-Za-z0-9][A-Za-z0-9 ._:/@+-]{0,63}$. |
spec |
When type is management: grants must be 0 to 0 items. |
spec.grants[] |
Exactly one of these holds: (1) all is required and all must be true; (2) ids is required. |
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, except for the second phase below. - 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. - Second phase. This kind is not written inside the apply transaction. The other kinds apply and commit first, then each VaultEntry is written on its own, in the tenant vault. One that fails reports
status.phase: Failedand amessage, and nothing is recorded for it. The others stay applied and the apply answers 207. Plan again to see what is left. A singlePUTfrom the Terraform provider answers 502external_failedinstead. - Status. A failed second-phase write reports phase
Failedand the entry owns nothing. The phases the engine reports are explained under status phases.
How apply treats it
- The vault lives wholly outside Postgres, so this kind is written in the second phase. After the Postgres transaction commits, each entry is a compare-and-swap of the record inside a small transaction that records its ownership. Its undo puts the entry back (also a compare-and-swap, refused if someone changed it since) when that cannot be recorded.
- A failed entry is reported with phase
Failedand owns nothing. - The value is a
valueFromsecret, required to create, never read back, exported or shown in a plan. The same value again is no change. - The vault service checks the document, so it is refused for the reasons the vault page refuses it.
- The entry keeps the
metadata.nameit was applied under. An undeclared entry is listed under a name derived from its id, type and provider or variable. The type cannot change. - Delete revokes the entry, keeping the stub and history. AI firewall and sandbox derives run after the write, in the background, one at a time per tenant.
- Owner only: a token for this kind needs the owner role.
Terraform and OpenTofu
The resource is vulnetix_vault_entry 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_vault_entry.
Import an object that already exists by its name:
# Import an existing VaultEntry by its name (metadata.name).
terraform import vulnetix_vault_entry.npm_token npm-token
A secret is two arguments, never stored in the plan or the state: value_wo (write-only) and value_wo_version. Change the version number to send a new value. See Secrets.
Install, authentication and how state is read: Terraform and OpenTofu.
Check a file with vulnetix config validate -f <file>. See Validate documents. In an editor, start the file with the schema comment.