> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zavu.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# 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.

```sh theme={null}
zavu fn secrets set SENDER_ID jn76vnxet8g5nq661by3v06y1581bmmn
zavu fn secrets set DATABASE_URL postgresql://...
zavu fn secrets set OPENAI_API_KEY sk-...
```

```ts theme={null}
defineAgent({
  senderId: process.env.SENDER_ID!,
  ...
})

defineTool({
  ...,
  handler: async () => {
    const db = postgres(process.env.DATABASE_URL!)
    // ...
  }
})
```

## Setting secrets

### Inline

```sh theme={null}
zavu 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

```sh theme={null}
zavu 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)

```sh theme={null}
pbpaste | zavu fn secrets set OPENAI_API_KEY -
# or
echo "$SECRET_FROM_VAULT" | zavu fn secrets set KEY -
```

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

## Listing

```sh theme={null}
zavu fn secrets list
```

```
key              value    synced
─────────────  ────────  ──────
SENDER_ID       …1bmmn    yes
DATABASE_URL    …5432     yes
OPENAI_API_KEY  …sk-x     no
  1 secret not yet deployed — run `zavu 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

```sh theme={null}
zavu fn secrets unset OLD_KEY
# alias
zavu fn secrets rm OLD_KEY
```

## When changes take effect

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

```
$ zavu fn secrets set NEW_KEY value
✓ Created NEW_KEY (…alue)
  Environment updates on the next `zavu deploy`. Run it now to apply.

$ zavu deploy
... function now sees process.env.NEW_KEY
```

<Tip>
  You can batch secret changes before a single deploy. Setting 5 secrets in a
  row results in 1 sync (the next deploy), not 5.
</Tip>

## 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 `zavu 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:

```ts theme={null}
import Zavudev from "@zavudev/sdk"

const zavu = new Zavudev({
  apiKey: process.env.ZAVU_API_KEY!,
  baseURL: process.env.ZAVU_API_BASE_URL,
})
```

<Note>
  `ZAVU_API_KEY` is revoked automatically when you `zavu fn delete` the
  function. If you reset it manually from the dashboard's API Keys page, the
  function will start failing — redeploy to provision a new one.

  The auto-key has `messages:send`, `messages:read`, `contacts:read` scopes.
  For other operations create a separate scoped key and inject it as a secret.
</Note>

## 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

<AccordionGroup>
  <Accordion title="Rotating an API key">
    ```sh theme={null}
    zavu fn secrets set OPENAI_API_KEY sk-new-value
    zavu deploy
    # The old key is now overwritten in storage; revoke it in OpenAI's dashboard.
    ```
  </Accordion>

  <Accordion title="Different secrets per environment">
    Functions are project-scoped — if you have separate Zavu projects for
    staging vs prod, each has its own secrets.

    ```sh theme={null}
    zavu login                # pick staging project
    zavu fn secrets set DB_URL postgres://staging…
    zavu deploy

    zavu login                # pick prod project
    zavu fn secrets set DB_URL postgres://prod…
    zavu deploy
    ```

    `zavu whoami` shows the current project before each operation.
  </Accordion>

  <Accordion title="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:

    ```sh theme={null}
    # 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
      zavu fn secrets set "$key" "$val"
    done < secrets.spec
    ```
  </Accordion>

  <Accordion title="CI / automation">
    Use `ZAVUDEV_API_KEY` env var so the CLI uses your CI's key:

    ```yaml theme={null}
    # .github/workflows/deploy.yml
    env:
      ZAVUDEV_API_KEY: ${{ secrets.ZAVU_CI_KEY }}
    steps:
      - run: |
          zavu fn secrets set DB_URL "${{ secrets.DB_URL }}"
          zavu fn secrets set API_KEY "${{ secrets.API_KEY }}"
          zavu deploy
    ```
  </Accordion>
</AccordionGroup>

## API equivalence

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

```sh theme={null}
# 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](/api-reference) for full schemas.
