BETA In open beta. Install live. Lock $5/mo for your first 12 months. See pricing →
Docs / Setup

Bring your own keys (BYOK)

Hydrate is zero-config by default - if claude is on your $PATH, everything works. Adding your own provider API keys is optional; it upgrades retrieval quality and gives you a faster extraction path than the Claude Code subprocess. This page explains what those keys do, how to store them (industry-standard OS keychain, not .zshrc), and the precedence rules.

Should you add keys?

The honest tier ladder from /docs/install:

  • Just installed, didn't do anything else? You're on Tier 1 - Hydrate uses claude --print as a subprocess for extraction, FTS5 keyword search for retrieval. Works. Most users stop here.
  • Want semantic retrieval (fuzzy fact-matches, not just keyword)? Run Ollama locally + set HYDRATE_EMBED_ENDPOINT. Still no cloud keys. Tier 2.
  • Want the benchmark-quality setup? Add OPENAI_API_KEY - gpt-4o-mini for extraction, text-embedding-3-small for embeddings. Tier 3.
  • Want to route extraction through Anthropic directly (bypass the claude --print subprocess for a faster path)? Add ANTHROPIC_API_KEY.
  • Want code-specialised embeddings? Add VOYAGE_API_KEY - uses voyage-3-lite, better recall on API-surface / architectural vocabulary than OpenAI's general embedding.

How to add a key - the right way

Hydrate ships OS-native secret storage - macOS Keychain on Mac (via Apple's security tool, no CGO dependencies), a 0600-permission file under ~/.hydrate/ on Linux / Windows (v1). This is the same pattern gh auth login, stripe login, and 1password signin use.

# Interactive - prompts for the key, input hidden:
hydrate login openai
hydrate login anthropic
hydrate login voyage

# Non-interactive (CI, scripts):
echo "$OPENAI_API_KEY" | hydrate login openai --stdin

# See what's configured (values masked):
hydrate providers

# Remove a stored key:
hydrate logout openai

macOS users: the key goes in the login keychain under service com.gethydrate.cli. Inspect or revoke any time from Keychain Access.app → search "gethydrate". macOS will prompt for your user password the first time Hydrate reads the entry after a reboot - same behaviour as every other Keychain-using CLI.

Linux / Windows users: keys live in ~/.hydrate/secrets.yaml with mode 0600. Not encrypted at rest - the goal is "fewer footguns than exporting in your shell profile," not "defeat a local attacker with filesystem access." Secret-Service / Credential-Manager backends are v2.

What not to do

  • Don't paste keys into ~/.zshrc / ~/.bashrc unless you actively want them available to every program you run. That's a larger blast radius than strictly needed, and shell history / process-list leaks are real.
  • Don't commit ~/.hydrate/secrets.yaml to a dotfiles repo. The file is outside any git repo by default; adding it would defeat the point.
  • Don't put keys in ~/.hydrate/config.yaml either. Hydrate's own config reader reads them but the file isn't designed as a secret store. hydrate login writes to the right place.

Precedence - env vars always win

When Hydrate resolves which provider to use, the order is:

  1. Explicit HYDRATE_LLM_PROVIDER / HYDRATE_EMBED_PROVIDER
  2. Environment variables (OPENAI_API_KEY etc.)
  3. HYDRATE_LLM_ENDPOINT / HYDRATE_EMBED_ENDPOINT
  4. Keyring lookup (what hydrate login writes to)
  5. Claude Code subprocess (if claude is on PATH)
  6. Heuristic fallback (pure Go, no network)

Why env > keyring: CI / Docker / ephemeral shells set env vars on purpose and shouldn't be silently overridden by whatever the user's keychain had. Interactive shells, which rarely export API keys, pick up the keyring automatically.

Disabling / opting out

# Don't use any LLM at all (heuristic extractor, FTS5 only):
export HYDRATE_LLM_PROVIDER=none

# Don't use any embedder (FTS5 keyword retrieval only):
export HYDRATE_EMBED_PROVIDER=none

# Don't spawn hydrate-server if hooks find it down:
export HYDRATE_AUTOSTART=0

# Force the file backend everywhere (CI / test harnesses):
export HYDRATE_SECRETS_BACKEND=file

Key-safety posture

  • Hydrate reads the secret only in-process, passes it to the configured provider, discards. Nothing persists in memory.
  • Obfuscated-at-rest AES-GCM wrap applies to the local fact store (user facts, captured transcripts). Keys themselves live in the OS keychain / 0600 file, not the wrapped columns.
  • Hydrate Free never makes an outbound network call except to the provider you configured. Nothing phones home to gethydrate.dev. Add your own firewall rule to verify.

Rotation

hydrate login <provider> overwrites the existing entry. For rotation on a regular cadence: revoke the key at the provider, run hydrate login again with the new one - no restart of hydrate-server needed; the next request picks up the fresh value.