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.
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.
terminalnpx zavudev fn secrets set SENDER_ID jn76vnxet8g5nq661by3v06y1581bmmn npx zavudev fn secrets set DATABASE_URL postgresql://... npx zavudev fn secrets set OPENAI_API_KEY sk-...
TypeScriptdefineAgent({ senderId: process.env.SENDER_ID!, ... }) defineTool({ ..., handler: async () => { const db = postgres(process.env.DATABASE_URL!) // ... } })
Setting secrets
Inline
terminalnpx 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
terminalnpx 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)
terminalpbpaste | 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
terminalnpx zavudev fn secrets list
Codekey 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
terminalnpx 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
| Constraint | Limit |
|---|---|
| Key format | [A-Z_][A-Z0-9_]* (uppercase env-var style) |
| Key length | ≤ 64 chars |
| Reserved prefixes | AWS_, LAMBDA_, _HANDLER, _X_AMZN (system-reserved) |
| Value size | ≤ 4096 chars |
| Total secrets per function | 50 |
| Total env size | 4 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:
| Key | Value | Purpose |
|---|---|---|
ZAVU_API_KEY | A unique live API key scoped to this function's project | Lets the function call Zavu's REST API. |
ZAVU_API_BASE_URL | The dashboard's Convex .site URL | So local-dev functions hit the right backend. |
ZAVU_PROJECT_ID | The function's project ID | For logging / multi-tenant code. |
ZAVU_FUNCTION_ID | This function's ID | For logging. |
ZAVU_FUNCTION_SLUG | This function's slug | For logging / URL construction. |
Use them directly:
TypeScriptimport 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
terminalnpx 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.
terminalnpx 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.