VendoVendo Docs
Build a toolvendo.yaml

Env and secrets

How env vars get into your tool at runtime — declared, injected, and generated.

By the time your container boots, Vendo has assembled a complete environment from four sources: integrations, wizard inputs, deploy-context built-ins, and any secrets the platform generates on your behalf. This page covers the part of that assembly you control from vendo.yaml — the env: block — and the contract for what your code can expect.

The env: block

env:
  required:
    - DATABASE_URL
    - JWT_SECRET
  optional:
    - LOG_LEVEL
    - SENTRY_DSN

env.required lists env vars your tool must have to start. The deploy worker checks each one resolves to a non-empty value before launching your container. If any are missing, the deploy fails fast with a clear error pointing at the missing key.

env.optional is informational — the dashboard surfaces these as configurable, but the deploy proceeds whether or not they have values.

The env: block is a contract, not a definition. You don't put values here. The values come from:

  • Integrations — declaring integrations: [openrouter] causes OPENROUTER_API_KEY and OPENROUTER_BASE_URL to be injected.
  • Wizard inputs — declaring a userInputs[] entry in your template manifest with key: GREETING causes GREETING to be injected with whatever the tenant typed.
  • Platform built-insDATABASE_URL, REDIS_URL, R2 keys, etc., when your template requests those resources.
  • Generated secrets — declared in the template manifest's secrets: block (see below).

Listing a var under env.required without one of those four supplying it will fail the deploy. The check is intentionally strict — silent missing env vars are the #1 cause of "deployed but not working" reports.

Var names must match ^[A-Z][A-Z0-9_]*$. Both required and optional reject duplicates within the array.

Generated secrets

Generated-secret declarations (secrets:, ${jwt_secret} placeholders, service env wiring) live in the template manifest (templates/<slug>/<version>.json in vendo-templates), not in vendo.yaml. The shape below is the template manifest's, validated by cloudflare/deploy-worker/src/schema/manifest.schema.json.

When your tool needs a stable random value — a JWT signing key, a session secret, an encryption key — declare it in the template manifest and reference it with a ${...} placeholder in the service env:

{
  "secrets": {
    "jwt_secret": { "type": "random_hex", "length": 32 }
  },
  "services": [
    {
      "name": "web",
      "role": "web",
      "env": { "JWT_SECRET": "${jwt_secret}" }
    }
  ]
}

Vendo generates the value once per deployment, persists it, and injects it on every restart. It never appears in logs, never leaves the platform, and rotates only if you explicitly rotate it from the deployment's settings page.

In vendo.yaml itself, you just list the env var name under env.required:

env:
  required:
    - JWT_SECRET

Your code reads it like any other env var:

JWT_SECRET = os.environ["JWT_SECRET"]

Platform built-ins

When your template requests a Postgres database, R2 bucket, or Redis instance, Vendo wires the connection details into env. The standard set:

VariableSource
DATABASE_URLThe pooled Postgres URL for your deployment's database
DATABASE_URL_UNPOOLEDThe direct (non-pooled) Postgres URL — use for migrations
REDIS_URLRedis connection string, if your template includes Redis
R2_BUCKET, R2_ENDPOINT, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEYR2 (S3-compatible) storage, if requested
VENDO_DEPLOYMENT_IDThe UUID of this deployment — the SDK reads this automatically
VENDO_DEPLOYMENT_SLUGThe deployment's slug (the user-facing piece of {slug}.vendo.run)
VENDO_TENANT_IDUUID of the tenant that owns this deployment
VENDO_API_URLBase URL for SDK API calls
VENDO_API_KEYThe vendo_sk_* proxy key for this deployment

The Vendo- and platform-prefixed vars are set on every deployment, regardless of vendo.yaml. You don't list them and you can't override them. They appear in templates as ${vendo_tenant_id}, ${vendo_deployment_id}, ${vendo_deployment_slug} placeholders, resolved by cloudflare/deploy-worker/src/secrets.ts.

What your tool should NOT do

  • Don't read provider keys from env that the proxy serves. Even when OPENROUTER_API_KEY is set, it's a vendo_sk_* proxy key, not a raw OpenRouter key. Don't try to call OpenRouter directly with it — point your client at OPENROUTER_BASE_URL and let Vendo's proxy handle metering and gating.
  • Don't bake secrets into your Docker image. Images are reused across tenants; embedded secrets are a guaranteed leak. Read everything from env at runtime.
  • Don't write secrets to disk outside a Railway volume. The container's writable layer is per-restart and not preserved across upgrades. Volumes (or Postgres / R2) are the only durable storage.

Inspecting env at runtime

Tenants can view the resolved env var set (with secret values masked) from the deployment's settings page. As a tool author, you can reproduce the same set locally with ./bin/repo dev --env for testing — that command pulls real per-tenant proxy URLs and a personal vendo_sk_* so your calls flow through prod metering against your own tenant.

On this page