Enterprise security model¶
Hub: documentation home. Hardening checklist: security-hardening.md.
VectorSmith is a compiler, not an identity provider or access plane. You own auth, host choice, and the store. This page is the security model the compiler and serve / connect enforce when you turn the knobs on.
0.2.0 applies every layer below at process start. validate --enterprise is the CI gate; a non-compliant YAML now also refuses serve and connect.
Layers¶
| Layer | Where | What it does |
|---|---|---|
| Hidden filters | static_filters |
Always AND. The model never sees them |
| Request tenancy | security.tenancy |
Extra payload filter from JWT claim or header |
| Auth | --auth jwt / api_key / builtin |
Who is calling HTTP /mcp |
| RBAC | security.rbac |
Which compiled tools that principal may invoke |
| Audit | observability.audit |
One event per named tool call |
| Rate limits | security.rate_limit |
Off by default; HTTP 429 |
| Output policy | output.redact |
Applied before the result (and audit) leaves the process |
--auth none is loopback only (exit 3 off localhost). Builtin OAuth still requires https:// --public-url.
Tenancy¶
security.tenancy.mode:
none— no request filter (YAMLstatic_filtersstill apply)static— this process is one tenant; usestatic_filtersclaim— value from JWT (claim: tenant_id)header— value from an HTTP header
The engine ANDs tenant_filter with must / must_not. enforce: strict rejects a model argument that conflicts (VB4010).
Pick one isolation layer unless you intend both:
| Pattern | YAML | When |
|---|---|---|
| Claim (multi-tenant HTTP) | tenancy.mode: claim only |
One process, many tenants from JWT |
| Static (one-tenant process) | tenancy.mode: static + static_filters.must tenant |
Replica / namespace already isolated |
| Both (AND) | claim and a compile-time tenant filter | Rare: process is pinned to acme and JWT must match |
Copy-paste: examples/enterprise/tools.yaml (claim) and tools.static.yaml (static).
Trust profiles¶
- Stdio has no authenticated caller identity. Use static filters and one process/catalog per security boundary.
- HTTP derives request context from JWT, API-key, or builtin OAuth state and is the profile for shared claim/header tenancy.
- Python in-process calls may pass
CallContext, but the embedding application is responsible for authenticating the caller before constructing it.
These profiles are not interchangeable. Details and framework mappings: security hardening and Python API.
Auth and RBAC¶
HTTP --auth jwt validates RS256 via JWKS (vectorsmith[auth-jwt]). --auth api_key reads a keys file. --auth-store redis shares builtin OAuth tokens across replicas. GET /readyz fetches JWKS so a down IdP takes the replica out of rotation.
security.rbac.deny_tools wins over allow: ["*"]. Checks apply to the inner name of run_tool.
Audit¶
JSONL: request_id, principal, tool, connection, redacted args, result count, latency. Sinks: file (mode 0600), http (raw JSONL POST), otlp (OTLP HTTP JSON logs). Sink failures do not block the call. Default redact: password, token, secret. Multi-project serve uses each YAML’s audit block unless you pass --audit-*.
Credentials¶
connections.*.credentials.provider defaults to env. serve / connect dispatch on the provider:
| Provider | Runtime | Required |
|---|---|---|
env |
--env-file / env= |
${VAR} on connection fields |
vault |
HTTP KV read (VAULT_ADDR, VAULT_TOKEN, credentials.vault.path) |
Secret JSON keys = connection fields (url, api_key, …) |
aws_sm |
Secrets Manager (vectorsmith[creds-aws]) |
credentials.aws_sm.secret_id (VB4041) |
k8s |
In-cluster service account | credentials.k8s.secret (VB4042) |
Inject a fetch callable on build_credential_resolver for tests. Details: tools.yaml → credentials.
Rate limits¶
security.rate_limit is off by default. Per-principal / per-tool / embed windows. In-memory or store: redis (vectorsmith[auth-redis]; async client). HTTP 429 with retry_after_s.
Production checklist¶
- Run
validate --enterprise --strictin CI. It intentionally remains non-zero for experimental backend warningVB2024; stable deployment claims require the backend conformance gate to pass. serve --httpwith--auth jwtorapi_key(never--auth noneon a public bind)GET /readyzas the readiness probe;GET /healthzfor liveness--log-format jsonandobservability.audit.enabled- Helm / two replicas + Redis if you use builtin OAuth — Kubernetes
Directory parameter resolve caches in-process only (see tools.yaml → resolve).
See examples/enterprise/. Full inventory: library surface.