---
title: Password Managers
description: Read every secret you enter in Crumble, such as credentials, API keys, and tokens, from 1Password or Bitwarden instead of storing it in Crumble.
sidebar:
  order: 8
---

A password manager keeps your secrets where they already are, and Crumble
reads them when it needs them. Crumble supports 1Password and Bitwarden
Secrets Manager.

Every secret you enter in Crumble can come from one:

- A bot's [credential](/concepts/credentials).
- A field of a bot's [login](/concepts/logins).
- A [model provider's](/getting-started/providers) API key.
- An [MCP server's](/concepts/schedules-and-integrations#mcp-integrations)
  bearer token.
- A plugin key, such as the [Exa API key](/concepts/web-search) or the
  [Discord bot token](/concepts/discord).
- A [machine account's](/concepts/credentials#machine-accounts) GitHub token.
- A [GitHub App's](/concepts/credentials#acting-as-you) client secret.
- The client secret of an MCP server's OAuth app.

Crumble only reads. It never writes or changes a secret in your password
manager.

## Connecting a Password Manager

First, create a token that can read the secrets Crumble needs:

| Password manager          | Token                                                                  |
| ------------------------- | ---------------------------------------------------------------------- |
| 1Password                 | A service account token with read access to the vaults that hold them. |
| Bitwarden Secrets Manager | A machine account access token with read access to their projects.     |

Then, in the web app, open **Settings** > **Password managers**, select
**Connect** next to the password manager, paste the token, and select
**Connect**.

Crumble signs in once to check the token, then stores it encrypted with your
account's key. To change it, open the row's **More** menu and select
**Replace token**. Values read with the old token are dropped.

Over the API, send `{"token": "..."}` to `PUT /v1/password-managers/{id}`,
where `{id}` is `1password` or `bitwarden`.

## Pointing a Secret at It

In the secret's editor, select the password manager above the value field
instead of **Crumble**, then paste a reference:

| Password manager | Reference                                                   |
| ---------------- | ----------------------------------------------------------- |
| 1Password        | A secret reference, such as `op://Private/Acme/credential`. |
| Bitwarden        | The secret's ID.                                            |

Crumble reads the reference once when you save and rejects the save if it
can't read it. Nothing changes until it can.

### Over the API

Every endpoint that takes a secret takes the same object: a value to store, or
a reference to read.

```json
{ "value": "sk-..." }
```

```json
{
  "reference": {
    "passwordManager": "1password",
    "reference": "op://Private/Acme/credential"
  }
}
```

Responses never include a value. They describe each secret with
`configured`, plus the `passwordManager` and `reference` it is read from, both
`null` for a value stored in Crumble. A password manager's `usedBy` lists the
keys of the secrets that read from it, such as `mcp:<server ID>`.

## When Crumble Reads a Secret

Crumble reads a secret only when something uses it, such as a request with a
credential's placeholder or a model call. It keeps the value in memory, never
on disk, and decides on each use whether to ask the password manager again:

```mermaid
flowchart TD
  Use["Something uses the secret"] --> Age{"When was it<br/>last read?"}
  Age -- "Under 55 minutes ago" --> Cached["Use the cached value"]
  Age -- "55 to 60 minutes ago" --> Refresh["Use the cached value,<br/>refresh it in the background"]
  Age -- "Over an hour ago, or never" --> Read["Ask the password manager"]
  Read --> Answer{"What does it answer?"}
  Answer -- "The value" --> Fresh["Use it and cache it"]
  Answer -- "Can't be reached" --> Grace{"Read in the<br/>last two hours?"}
  Grace -- "Yes" --> Stale["Use the cached value"]
  Grace -- "No" --> Fail["The use fails"]
  Answer -- "Secret gone, or<br/>token can't read it" --> Drop["Drop the value now.<br/>The use fails."]
```

Changing or removing a secret's reference drops its value too.

## When a Secret Can't Be Read

What fails depends on the kind of secret:

| Secret                    | What happens                                                                                             | Where you see it                                       |
| ------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| **Credential**            | The server doesn't send the request and answers it with an error that names the credential.              | The credential's row in **Settings** > **Credentials** |
| **Login**                 | Nothing is typed, and the desktop job is told the value couldn't be read.                                | The login's row in **Settings** > **Logins**           |
| **Provider API key**      | The model call fails with an error that names the provider and the password manager's message.           | The failed model call's error                          |
| **MCP server token**      | The bot answers without that server's tools.                                                             | The server's entry in **Integrations**                 |
| **Machine account token** | The same as a credential: requests through the bot's `GH_TOKEN` that would use the machine account fail. | The `GH_TOKEN` row in **Settings** > **Credentials**   |

## Disconnecting

Open the row's **More** menu and select **Disconnect**.

If **Disconnect** is unavailable, some secrets still read from the password
manager, and the row says how many, such as **(used by 3)**. Switch those
secrets to **Crumble** or another reference first.
