Variables and Secrets

Environment variable interpolation, config variables, and the open-source secrets plane with provider-URI references

Overview

SBproxy has three systems for keeping values out of your config file: environment variable interpolation (${VAR} anywhere in the YAML), config variables (static values under a variables block, available to templates), and the secrets plane (values resolved from a secrets backend at config load, referenced by logical name or provider URI). All three ship in the open-source binary.

Environment Variables in Config

Reference environment variables anywhere in the config with ${VAR_NAME}. They resolve at config load time, which is the simplest way to keep an API key out of the file:

origins:
  "ai.example.com":
    action:
      type: ai_proxy
      providers:
        - name: openai
          api_key: ${OPENAI_API_KEY}

An unset variable leaves the literal ${VAR_NAME} string in place rather than failing the load, so validate configs with sbproxy validate before shipping them.

A separate set of process-level variables controls the binary itself:

VariableDescription
SB_CONFIG_FILEPath to the sb.yml config file (the path must be supplied; the binary does not auto-discover it)
SB_LOG_LEVELLog level (debug, info, warn, error)
SB_REQUEST_LOG_LEVELRequest log level
SB_LOG_FORMATLog output format
SB_GRACE_TIMEGraceful shutdown timeout
SB_WORKER_THREADSWorker thread count

Config Variables

The variables block on an origin defines static values available to templates under the {{ variables.name }} scope. Any JSON type works, including nested objects:

origins:
  "api.example.com":
    variables:
      api_version: v2
      base_url: https://api.example.com
      feature_flags:
        new_ui: true
    action:
      type: proxy
      url: "{{ variables.base_url }}/{{ variables.api_version }}"

Secrets

The secrets plane is open source. The top-level proxy.secrets block picks a backend, maps logical names to backend paths, and controls rotation. Resolved secrets are available in templates as {{ secrets.name }}:

proxy:
  secrets:
    backend: hashicorp
    hashicorp:
      addr: https://vault.example.com:8200
      mount: secret
    map:
      openai_key: secret/data/prod/openai_key
    rotation:
      grace_period_secs: 300
      re_resolve_interval_secs: 60
    fallback: cache

Backends: env (the default), local, and hashicorp. The fallback field says what happens when the backend is unreachable: serve the cached value, reject, or fall back to the environment.

Secret Reference URIs

Secret-bearing fields (provider API keys, auth tokens, signing keys) also accept provider-specific reference URIs directly. The scheme names the provider type, the authority names a configured backend instance, and the path is the backend's own:

SchemeProviderExample
vault://HashiCorp Vault KVvault://primary/secret/data/openai-prod?key=api_key
awssm://AWS Secrets Managerawssm://primary/openai-keys?version=3&key=api_key
gcpsm://GCP Secret Managergcpsm://primary/openai-api-key?version=latest
k8ssecret://Kubernetes Secretk8ssecret://primary/sbproxy-secrets/openai-key
secretfile://Local YAML or JSON secret filesecretfile://local/openai-prod?key=api_key
secret://Local static secret mapsecret://local/openai-prod

version= pins a secret version on backends that support versioning; key= extracts one field from a JSON payload. References resolve at boot, and a reference that cannot be resolved fails the load loudly instead of starting with a missing credential. All of these backends ship in the open-source binary.

Template Scopes

Templates use {{ scope.path }} syntax. Available scopes:

ScopeDescriptionExample
requestCurrent HTTP request{{ request.headers.x_api_key }}
variablesUser-defined variables{{ variables.api_version }}
secretsLoaded secrets{{ secrets.api_token }}
configConfig metadata{{ config.hostname }}
sessionSession data{{ session.auth.email }}
envConfig identity fields{{ env.workspace_id }}
serverServer-level vars{{ server.var_name }}

See also