VendoVendo Docs
Infrastructure

Secrets and env vars

What gets injected at boot, naming conventions, and where the values live.

When your container boots on Vendo, its environment is already populated with everything it needs: a Postgres URL, a Vendo API key, integration tokens, your declared user-facing config. This page documents what's injected, who set it, and what you can change after the fact.

The four kinds of env vars

Every row in your deployment's env is tagged with a kind:

KindSet byWhenUser-editable
systemDeploy workerFirst deploy, generated onceNo
integrationDeploy worker / bindings APIFirst deploy, plus when connections changeNo
userWizard inputs / Settings tabFirst deploy, edited any timeYes
wizard (legacy)Wizard inputsFirst deployYes

The Settings tab in the dashboard shows all four. You can edit user rows; system and integration are read-only badges (reveal still works, you can copy values, but you can't change them — they'd be overwritten on the next event that owns them).

system — Vendo-controlled

Set once at deploy time by the deploy workflow. Common keys:

VariableWhat
DATABASE_URLPooled Postgres URL (Neon). Connect with this.
DATABASE_URL_UNPOOLEDDirect Postgres URL. Use for migrations and long transactions.
DATABASE_HOST, DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAMEComponents, for tools that need them split out.
REDIS_URLRedis URL, if your manifest requested one.
R2_BUCKET, R2_ENDPOINT, R2_ACCESS_KEY, R2_SECRET_KEYR2 bucket, if your manifest declared object storage.
VENDO_API_KEYApp-scoped proxy key. Same value ${vendo_api_key} resolves to in the manifest.
VENDO_TENANT_ID, VENDO_DEPLOYMENT_ID, VENDO_DEPLOYMENT_SLUGIdentifiers for runtime use.
DOMAIN, SUBDOMAINYour public hostname.
ADMIN_EMAIL, ADMIN_PASSWORDBootstrap admin creds, if your manifest declared bootstrap_admin.

Generated secrets (any value your manifest declared as random_hex / random_base64 / uuid) are produced once during collect_secrets and stick for the life of the deployment.

integration — connection-derived

When a tenant binds an OAuth connection to your deployment, Vendo writes the relevant tokens as integration-kind env vars. The deploy workflow does this on first deploy; the POST /api/apps/[id]/bindings API does it on post-deploy bind.

What gets written depends on the provider, declared centrally in each integration's connectionEnvVars:

ProviderVariables typically injected
OpenAIOPENAI_API_KEY
AnthropicANTHROPIC_API_KEY
OpenRouterOPENROUTER_API_KEY, OPENROUTER_BASE_URL
TelegramTELEGRAM_BOT_TOKEN
NotionNOTION_TOKEN

For refresh-rotating profiles (most OAuth providers via Composio), Vendo deliberately does not inject the token as an env var — it would go stale within an hour. Your tool fetches via the SDK or credentials worker instead.

Tools that exclusively use refresh-rotating OAuth see no integration-kind rows, only system and user.

user — what your manifest's wizard asked for

Anything your template's wizardInputs[] collected from the user during the deploy wizard. Editable from the Settings tab post-deploy. After an edit, the dashboard surfaces a "Restart now" button — the new value isn't live in your container until the restart.

Reading env vars at runtime

Your code reads them with whatever your language's normal mechanism is:

  • Node: process.env.OPENAI_API_KEY
  • Python: os.environ["OPENAI_API_KEY"]
  • Go: os.Getenv("OPENAI_API_KEY")

No special SDK call required for env-injected values. Use the SDK when you need a refresh-rotating OAuth token (vendo.token("notion")) or when you want code that runs unchanged in OSS mode and Vendo mode.

Where the values actually live

You don't touch any of this; it's noted here so you can reason about behavior.

  • Encrypted at rest in Postgres (deployment_env_vars, AES-256-GCM).
  • Pushed to Railway as variables on the service at deploy + on every bind / restart.
  • The Railway runtime decrypts and presents them as ordinary OS env to your container.

Adding a new user env after deploy is a write to Postgres + a Railway upsert + a service restart. The "Restart to apply" banner is exactly this.

Gotchas

  • Restart, don't redeploy, for env changes. Editing a user row and restarting picks up the new value. Re-running the deploy wizard is unnecessary and slower.
  • system keys are reserved. Trying to PATCH /env with a key the deploy workflow owns returns 400.
  • Idempotency. All env writes are upsert-with-insert-if-absent. A deploy retry won't clobber user edits you've made post-deploy.
  • Renaming the deployment rewrites DOMAIN, SUBDOMAIN, and a few connection-derived URLs, then restarts. Don't cache these values in memory beyond a single request.

On this page