Skip to content
Beta. VectorStep is in beta.Let us know if something breaks.

Configuration

The installer writes ~/.vectorstep/config/gateway.yaml for you; edit it in place. Values support ${VAR_NAME} environment variable substitution throughout, resolved from whatever’s in ~/.vectorstep/.env.

Field Default Description
host 0.0.0.0 Bind address
port 18780 Bind port. Use a different port from OpenClaw (18789) if running both.
metrics_auth false GET /metrics is open by default (Prometheus scrapers connect with no credential); set true to require the invoke/admin bearer token. See Gateway authentication.

No TLS by default — the Gateway serves plain HTTP/WS until you set this. Same shape as VectorStep’s own server.tls block (see Deployment → TLS), deliberately: an operator running both services shouldn’t have to learn two schemas.

server:
host: 0.0.0.0
port: 18780
tls:
cert: /etc/vectorstep-gateway/tls/server.crt
key: /etc/vectorstep-gateway/tls/server.key
key_password: ${VS_TLS_KEY_PASSWORD} # optional, if the key is encrypted
client_ca: /etc/vectorstep-gateway/tls/ca.crt # optional; presence enables mTLS

As with VectorStep, startup fails fast — naming the path — if cert is set without key, or if a configured file doesn’t exist. No certificate generation or ACME support is built in.

On the VectorStep side, once the Gateway terminates TLS, point executors.gateway at wss:// and give it what it needs to verify the Gateway’s certificate:

executors:
gateway:
url: wss://gateway.internal:18780/rpc
token: ${VECTORSTEP_GATEWAY_TOKEN}
rest_url: https://gateway.internal:18780
tls:
verify: true # default
ca: /etc/vectorstep/tls/ca.crt # optional, for a private CA
client_cert: /etc/vectorstep/tls/client.crt # optional, for mTLS
client_key: /etc/vectorstep/tls/client.key

verify defaults to true. If the Gateway’s certificate is signed by a private CA (the common case for an internal deployment), set ca to that CA’s certificate rather than turning verification off. verify: false disables both hostname and certificate checking and logs a warning on every connection — it should only ever appear as a deliberate, visible line in your own config, never a default. The same tls block also governs the REST calls VectorStep’s UI makes to the Gateway (/agents, /mcp/tools, etc.) — one block, one behaviour, for both transports.

Field Default Description
path ~/.vectorstep-gateway/identity (native) — the installer’s Docker config sets it to /data/identity instead Where identity files are stored. Auto-generated on first run.

Two tokens — admin and invoke — are minted into <path>/device-auth.json on first run; see Gateway authentication for what each grants and who holds it. An identity directory from before this split (a single operator token) is migrated automatically on load: the existing token becomes admin and keeps working unchanged, and a new invoke token is minted alongside it — logged once at startup, since VectorStep’s own config then needs updating to the new invoke token.

/data/identity sits on the container’s named Docker volume, not its ephemeral filesystem — the compose file mounts /data there — so a recreated container keeps its identity, including both tokens VectorStep and any Gateway MCP client were configured with. See Docker for the full container config.

Field Default Description
max_agent_iterations 20 Max LLM ↔ tool call loops before aborting a run
request_timeout_seconds 180 Timeout for a single LLM API call
max_agent_iterations_ceiling 100 Highest max_iterations an agent or a pipeline step may raise its own loop limit to. A larger value is capped at this. Never lowers max_agent_iterations itself.
request_timeout_ceiling_seconds 1800 Highest per-LLM-call timeout an agent or step may raise request_timeout_seconds to. Also sets the HTTP-client timeout of the providers, so an allowed override is never cut short by the client.
mcp_tool_timeout_seconds 30 Timeout for a single MCP tool call
llm_retry_attempts 2 Retries on the same model after a retryable error (429/5xx/529/timeout/connection error) before falling over to the next entry in model_fallbacks
llm_retry_base_delay_seconds 1.0 Base delay for exponential backoff between retries (doubles each attempt: 1s, 2s, 4s, …)
max_concurrent_runs 10 Gateway-wide cap on simultaneously executing agent runs. Once at capacity, new requests are still accepted (Frame 1) but queue until a slot frees up.
trace_tool_result_max_chars 3000 Gateway-wide default cap on a tool_result trace event’s content, truncated with a trailing "… [truncated]" marker. This only affects the trace copy — the streamed/persisted record a caller (or a downstream grounding judge) inspects. The LLM conversation itself always receives the tool’s full, untruncated output regardless of this setting; nothing about the agent’s own reasoning is affected by it. A caller can override this per-request via the agent request’s traceToolResultMax (see the WebSocket protocol) without changing the gateway-wide default.

Each key is a server name agents can reference in their tools: list. The gateway spawns each as a subprocess on startup using stdio JSON-RPC transport. For the step-by-step on Docker, native Linux and Kubernetes — secrets, restart, verification and pre-installing servers in your own image — see Adding MCP tool servers.

mcp_servers:
grafana:
command: npx
args: ["-y", "@grafana/mcp-grafana"]
env:
GRAFANA_URL: ${GRAFANA_URL}
GRAFANA_TOKEN: ${GRAFANA_TOKEN}
Field Required Description
command Yes Executable to run (npx, python3, etc.)
args No Arguments list
env No Environment variables for the subprocess. Supports ${VAR_NAME} substitution.

Each key becomes a model prefix for routing. Configure only the providers you need. See Providers for how the prefix determines routing and for Azure’s deployment-name specifics.

providers:
anthropic:
api_key: ${ANTHROPIC_API_KEY}
openrouter:
api_key: ${OPENROUTER_API_KEY}
base_url: https://openrouter.ai/api/v1
ollama-local:
base_url: http://localhost:11434/v1
ollama-cloud:
api_key: ${OLLAMA_API_KEY}
base_url: https://ollama.com/api
google:
api_key: ${GOOGLE_API_KEY}
base_url: https://generativelanguage.googleapis.com/v1beta/openai
azure:
api_key: ${AZURE_OPENAI_API_KEY}
resource_name: ${AZURE_OPENAI_RESOURCE} # e.g. "my-company-openai"
api_version: "2025-01-01-preview" # optional, this is the default
openai:
api_key: ${OPENAI_API_KEY}
base_url: https://api.openai.com/v1 # optional, this is the default
yolo:
api_key: ${YOLO_API_KEY}
base_url: https://your-provider.example.com/v1

Most providers support api_key and base_url. Azure uses different fields:

Field Required Description
api_key No API key. Empty string = no auth header sent.
base_url No Override the provider’s default endpoint (not used for azure).

Azure-specific fields (under providers.azure):

Field Required Description
api_key Yes Azure OpenAI API key from Azure AI Foundry.
resource_name Yes Azure resource name — the subdomain part of {resource_name}.openai.azure.com.
api_version No Azure API version. Default: 2025-01-01-preview.

Default endpoints:

Provider key Default endpoint Notes
anthropic SDK default Uses Anthropic Python SDK natively
openrouter https://openrouter.ai/api/v1 OpenAI-compat
ollama-local http://localhost:11434/v1 Local Ollama OpenAI-compat endpoint
ollama-cloud https://ollama.com/api Native Ollama /api/chat endpoint
google https://generativelanguage.googleapis.com/v1beta/openai OpenAI-compat
azure https://{resource_name}.openai.azure.com/openai/deployments/{deployment}/chat/completions OpenAI-compat, auth via api-key header
openai https://api.openai.com/v1 Native OpenAI API
yolo None — base_url required Generic OpenAI-compat custom endpoint, e.g. a self-hosted or third-party API
Field Default Description
level INFO Log level: DEBUG, INFO, WARNING, or ERROR
dir "" (disabled) Directory for rotating log files. Omit to log to stdout only. When set, creates gateway.log (all application logs, 10 MB x 5 rotating) and access.log (uvicorn HTTP access logs, kept separate so request noise doesn’t pollute gateway.log or stdout) — same format and rotation policy as VectorStep’s service.log/access.log split, so both services’ logs can be correlated directly.
format text text is one readable line per record; json is one JSON object per line for log shippers. Both carry the trace, run and step fields described in Observability and both scrub known credential shapes.

Controls OpenTelemetry tracing. Disabled by default — all tracer.start_as_current_span() calls are no-ops until enabled.

observability:
otel:
enabled: true
exporter: otlp # otlp | console
endpoint: https://otlp-gateway-prod-eu-west-0.grafana.net/otlp/v1/traces
service_name: vectorstep-gateway
Field Default Description
enabled false Enable OTel tracing
exporter otlp otlp sends to an OTLP HTTP endpoint; console prints spans to stdout
endpoint http://localhost:4318/v1/traces OTLP HTTP endpoint. For Grafana Cloud, use your region’s OTLP gateway URL with a Basic Auth header set via OTEL_EXPORTER_OTLP_HEADERS.
service_name vectorstep-gateway service.name resource attribute on all spans

When OTel is enabled, the gateway emits three span types per agent run:

Span Parent Key attributes
agent.run VectorStep gen_ai.gateway span (via W3C traceparent) agent.name, gen_ai.request.model, vectorstep.gateway.iterations, vectorstep.gateway.tool_calls, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens
llm_call agent.run llm_call.iteration, llm_call.attempt, gen_ai.request.model, gen_ai.response.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, llm_call.error/llm_call.retryable (failed attempts only)
tool_call <name> agent.run tool.name, tool.is_error

VectorStep injects a W3C traceparent header into the agent WebSocket request params, and the gateway extracts it to make agent.run a child of VectorStep’s pipeline span — giving you a single unified trace across both services in Grafana Tempo.

The Gateway can also send its logs to the same collector, in addition to stdout:

observability:
otel:
logs:
enabled: true # default false
endpoint: null # default: the traces endpoint with /v1/traces replaced by /v1/logs
level: INFO # may be stricter than logging.level

Stdout stays the record: if the collector is unreachable the exporter drops records, so keep logging to stdout as well. Records use the same service.name as traces and carry the trace and span IDs, plus vectorstep.run.id, vectorstep.step and vectorstep.pipeline.name when the service supplied them. Auth headers come from the standard OTEL_EXPORTER_OTLP_HEADERS (or OTEL_EXPORTER_OTLP_LOGS_HEADERS) environment variable, as for traces. If endpoint is unset and the traces endpoint does not end in /v1/traces, the Gateway refuses to start rather than guess a URL. There is no content capture on the Gateway; see Observability.

Grafana Cloud setup:

  1. Get your OTLP endpoint from Grafana Cloud portal → Connections → OpenTelemetry
  2. Set OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic <base64(instanceId:apiKey)> in your environment
  3. Enable observability.otel.enabled: true and set endpoint to your Grafana Cloud OTLP URL

Optional. Operator-owned allow/deny rules evaluated on every tool call, independent of any agent’s own tools: allowlist. Omit it entirely for the previous unconditional-execution behaviour. See Tool policy for the full schema, matching semantics, the audit trail, and default-deny visibility filtering.

tool_policy:
default: allow # allow | deny
rules:
- deny: {server: atlassian, tool: jira_delete_issue}
reason: "Destructive Jira operations are operator-only"
- allow: {server: grafana}
Field Default Description
default allow Decision when no rule matches
rules [] Ordered list — first match wins
rules[].allow / rules[].deny — Exactly one per rule; match block: server, tool (glob), agent (glob), input_regex
rules[].reason — Required on deny, optional on allow

Governs skill script execution — the one piece of the skills feature that is deliberately not reachable through any write API, even though a skill’s content (including bundled scripts, as inert files) is.

skills_dir: ./skills # directory containing skill subdirectories, mirrors agents_dir
security:
skills:
allow_script_execution: false
allowed:
- skill: incident-triage
path: scripts/classify_severity.py
sha256: 3f2a1c9e...
Field Default Description
skills_dir ./skills Directory containing skill subdirectories (skills/<name>/SKILL.md), same shape as agents_dir.
security.skills.allow_script_execution false Whether run_skill_script is ever offered to an agent, regardless of what’s in allowed.
security.skills.allowed [] List of {skill, path, sha256} — the only scripts that can actually run. path is relative to that skill’s own directory (skills/<skill>/), not a shared scripts directory. sha256 is required and is re-verified before every single execution, not just at startup.

config.yaml is loaded once at gateway startup and is not writable via any API — same reasoning as tool_policy above, applied to a narrower surface. POST /reload / SIGHUP reload agent and skill content from disk but never re-read config.yaml, so approving a new script for execution needs an actual restart. See Skills — Running a skill’s bundled scripts for the full threat model and process containment this ports from VectorStep’s own executor: shell pipeline steps.

writes_enabled: false # default: true

When false, every agent and skill write endpoint (POST/PUT/DELETE on /agents and /skills) refuses with 403 and {"type": "writes_disabled"}. Use it when agents are assembled from a ConfigMap rather than written through the API, so it fails clearly instead of with a bare 500 from a read-only filesystem. /agents/validate, /skills/validate, POST /reload and all reads keep working, and the check runs after the admin-role check. Like the rest of config.yaml it is read at startup — restart to change it. See Changing config on Kubernetes.