Skip to content

Function Secrets & Environment Variables

Inject env vars into your Zavu function — API keys, DB URLs, anything sensitive. Encrypted at rest, scoped per function, rotatable from the dashboard.

View .md

Secrets

Function secrets are encrypted at rest and injected as environment variables at runtime. The function reads them via process.env.<KEY> just like in any Node program.

terminal
npx zavudev fn secrets set SENDER_ID jn76vnxet8g5nq661by3v06y1581bmmn npx zavudev fn secrets set DATABASE_URL postgresql://... npx zavudev fn secrets set OPENAI_API_KEY sk-...
TypeScript
defineAgent({ senderId: process.env.SENDER_ID!, ... }) defineTool({ ..., handler: async () => { const db = postgres(process.env.DATABASE_URL!) // ... } })

Setting secrets

Inline

terminal
npx zavudev fn secrets set OPENAI_API_KEY sk-abc123

The value is in your shell history — fine for non-secret config, not great for real keys.

From a file

terminal
npx zavudev fn secrets set GOOGLE_SERVICE_ACCOUNT --from-file ./sa.json

Reads the file as UTF-8. Trailing whitespace is stripped. Useful for multi-line values (PEM keys, JSON service accounts).

From stdin (safest)

terminal
pbpaste | npx zavudev fn secrets set OPENAI_API_KEY - # or echo "$SECRET_FROM_VAULT" | npx zavudev fn secrets set KEY -

Nothing touches disk or shell history. The - placeholder tells the CLI to read from stdin until EOF.

Listing

terminal
npx zavudev fn secrets list
Code
key value synced ───────────── ──────── ────── SENDER_ID …1bmmn yes DATABASE_URL …5432 yes OPENAI_API_KEY …sk-x no 1 secret not yet deployed — run `npx zavudev deploy` to sync.

Only the last 4 characters of each value are shown. Plaintext is never returned by the API — even from the dashboard, only the same last-4 is visible.

Removing

terminal
npx zavudev fn secrets unset OLD_KEY # alias npx zavudev fn secrets rm OLD_KEY

When changes take effect

Setting / unsetting a secret marks the function as out of sync. The next npx zavudev deploy rebuilds the function with the new env vars. Until then, the running function still has the old environment.

Code
$ npx zavudev fn secrets set NEW_KEY value ✓ Created NEW_KEY (…alue) Environment updates on the next `npx zavudev deploy`. Run it now to apply. $ npx zavudev deploy ... function now sees process.env.NEW_KEY

Constraints

ConstraintLimit
Key format[A-Z_][A-Z0-9_]* (uppercase env-var style)
Key length≤ 64 chars
Reserved prefixesAWS_, LAMBDA_, _HANDLER, _X_AMZN (system-reserved)
Value size≤ 4096 chars
Total secrets per function50
Total env size4 KB (runtime hard limit)

For values larger than 4 KB (large JSON blobs, certificates), upload to S3 / Convex storage and store a URL + auth header pair instead.

Auto-provisioned secrets

Every function created by npx zavudev fn init gets these injected automatically — you don't set them yourself:

KeyValuePurpose
ZAVU_API_KEYA unique live API key scoped to this function's projectLets the function call Zavu's REST API.
ZAVU_API_BASE_URLThe dashboard's Convex .site URLSo local-dev functions hit the right backend.
ZAVU_PROJECT_IDThe function's project IDFor logging / multi-tenant code.
ZAVU_FUNCTION_IDThis function's IDFor logging.
ZAVU_FUNCTION_SLUGThis function's slugFor logging / URL construction.

Use them directly:

TypeScript
import Zavudev from "@zavudev/sdk" const zavu = new Zavudev({ apiKey: process.env.ZAVU_API_KEY!, baseURL: process.env.ZAVU_API_BASE_URL, })

Encryption

Values are encrypted with AES-256-GCM, key derived via PBKDF2 (100,000 iterations, SHA-256) from the platform encryption key. Encryption happens server-side before the value is persisted, so plaintext never lives in our database.

Your function receives the value at deploy time as a standard environment variable, encrypted at rest by managed encryption keys. Inside the function, process.env.X returns the value.

Common patterns

Rotating an API key
terminal
npx zavudev fn secrets set OPENAI_API_KEY sk-new-value npx zavudev deploy # The old key is now overwritten in storage; revoke it in OpenAI's dashboard.
Different secrets per environment

Functions are project-scoped — if you have separate Zavu projects for staging vs prod, each has its own secrets.

terminal
npx zavudev login # pick staging project npx zavudev fn secrets set DB_URL postgres://staging… npx zavudev deploy npx zavudev login # pick prod project npx zavudev fn secrets set DB_URL postgres://prod… npx zavudev deploy

npx zavudev whoami shows the current project before each operation.

Secrets in source control (don't)

Never commit secret values. Commit a .zavu/secrets.example.yml style file with key names + descriptions, and have a teammate's setup script prompt for actual values:

terminal
# setup.sh while read line; do [[ -z "$line" ]] && continue key=$(echo "$line" | cut -d= -f1) desc=$(echo "$line" | cut -d= -f2-) read -s -p "Enter $key ($desc): " val echo npx zavudev fn secrets set "$key" "$val" done < secrets.spec
CI / automation

Use ZAVUDEV_API_KEY env var so the CLI uses your CI's key:

YAML
# .github/workflows/deploy.yml env: ZAVUDEV_API_KEY: ${{ secrets.ZAVU_CI_KEY }} steps: - run: | npx zavudev fn secrets set DB_URL "${{ secrets.DB_URL }}" npx zavudev fn secrets set API_KEY "${{ secrets.API_KEY }}" npx zavudev deploy

API equivalence

For automation, the secret endpoints are part of the public API:

terminal
# Set curl -X PUT https://api.zavu.dev/v1/functions/$FN_ID/secrets/SENDER_ID \ -H "Authorization: Bearer $KEY" \ -d '{"value":"jn76…"}' # List (values never returned) curl https://api.zavu.dev/v1/functions/$FN_ID/secrets \ -H "Authorization: Bearer $KEY" # Unset curl -X DELETE https://api.zavu.dev/v1/functions/$FN_ID/secrets/OLD_KEY \ -H "Authorization: Bearer $KEY"

See the API reference for full schemas.