Pipedash
Configuration

Token storage

Where Pipedash keeps provider tokens, how the vault locks and opens, and how the stores encrypt tokens.

Pipedash keeps provider tokens in a token store, separate from config.toml. Four store types exist, and the desktop app picks one at startup from the backend and the vault password.

The four stores

StoreUsed whenWhere tokens live
KeyringDesktop, SQLite backend, no vault password and no encrypted tokensOne JSON map in the system keyring, service pipedash, account tokens
Encrypted SQLiteSQLite backend with a vault passwordColumns providers.encrypted_token and providers.token_nonce
Encrypted PostgreSQLPostgreSQL backendTable encrypted_tokens with provider_id, nonce, ciphertext, created_at and updated_at
EnvironmentToken references in config.tomlEnvironment variables that ${VAR} or env:VAR reference

PIPEDASH_TOKEN_<provider_id> has no effect today, and token references cover that need. See Configuration.

Desktop selection

At startup the desktop app checks 3 facts: the backend, whether PIPEDASH_VAULT_PASSWORD has a value, and whether pipedash.db already holds encrypted tokens. It applies the first matching row.

OrderConditionResult
1SQLite, no password, no encrypted tokensKeyring store
2SQLite with encrypted tokens and no password, or PostgreSQL with no passwordLocked vault
3SQLite with a passwordEncrypted SQLite store with the keyring as fallback
4Anything else, which means PostgreSQL with a passwordEncrypted PostgreSQL store

The server never uses the keyring. For SQLite it requires the password, and for PostgreSQL it uses the encrypted store.

Keyring fallback migration

In row 3 the encrypted SQLite store is primary and the keyring is the fallback. Tokens move from the keyring to the database as Pipedash reads them.

  • Read: the store asks the encrypted store first. On a miss it reads the keyring, copies the token to the encrypted store and then deletes the keyring entry.
  • Write: the store writes to the encrypted store and removes any keyring copy.
  • Delete: the store deletes from both.

If the copy fails, Pipedash returns the token anyway and retries on the next read.

Vault locking and opening

A vault stays locked while Pipedash needs a password and has none. Locked behavior differs by platform.

  • Desktop: the app starts with a locked vault and no providers. It waits to load tokens and start the background tasks, including the refresh loop, until the vault opens.
  • Web server: setup completes in locked mode when the password is missing, and token loading waits for the vault to open.

The /unlock page is public. When the vault needs a password and is locked, the UI redirects every other page to /unlock. A browser session also needs the bearer token that a successful open stores. Settings, then Storage, shows a Lock Vault button while a password-protected vault is open.

ActionWeb route
StateGET /api/v1/vault/status
OpenPOST /api/v1/vault/unlock with { "password": "..." }
LockPOST /api/v1/vault/lock
Variable statusGET /api/v1/storage/vault-password-status

Opening the vault on desktop sets PIPEDASH_VAULT_PASSWORD for the process and checks the password by decrypting every token. A wrong password returns Invalid password. On success the app loads the providers, starts the background tasks, and the UI refetches providers and pipelines. With a SQLite backend that holds no encrypted tokens, it only stores the password for a later migration.

Locking removes the variable and answers Restart required to fully clear token cache. Tokens already in memory stay until the next restart.

The password has two jobs

PIPEDASH_VAULT_PASSWORD derives the encryption key, and the web server also uses it as the API bearer token.

  • Encryption: the encrypted stores derive their AES key from it. storage.vault_password in config.toml takes priority over the variable.
  • API access: when the variable has a value, protected /api/v1/ routes need the header Authorization: Bearer <password>. The server compares it in constant time and answers 401 on a mismatch.

The check skips /api/v1/health, /api/v1/setup, /api/v1/vault, /api/v1/ws and /api/v1/plugins. The WebSocket expects { "type": "auth", "token": "..." } as its first message when a password exists.

The server reads the variable on every request. POST /api/v1/vault/lock removes it, so protected routes accept requests without a bearer token afterward. POST /api/v1/vault/unlock sets it to the password in the request.

Encryption

Both encrypted stores use AES-256-GCM with a 32-byte key and a random 12-byte nonce per token. Each store derives its key with Argon2id version 0x13.

UseMemoryPassesParallelism
SQLite token key65536 KiB31
PostgreSQL token key4096 KiB11
SQLite export key65536 KiB31
Keyring export key4096 KiB11

The export keys protect the token blob that a storage migration moves between stores. See Setup wizard.

The PostgreSQL store with no password generates a random 64 character password and does not save it. Tokens it writes cannot be read after a restart. The SQLite store refuses to start without a password.