grithdocs

Encrypted key management

pro

Sharing provider API keys across a team, where they are encrypted, and exactly what that protects against.

Pro

A team can hold its Anthropic, OpenAI and OpenRouter keys in the grith.ai dashboard and have every signed-in machine pull them, instead of pasting them into chat or committing them. The dashboard also accepts an Ollama entry, but grith talks to Ollama on a local endpoint and never sends it a key.

The flow

  1. A team admin or the owner adds a key in the dashboard. The server encrypts it at rest with AES-256-GCM and stores only a short prefix for display. Members can see that a key exists and its prefix; they cannot read it back.
  2. grith pro sync pulls the current set. The daemon does the same on its own at most once every 24 hours.
  3. Each key is written to ~/.config/grith/provider-keys/ as an encrypted envelope, mode 0600. The first key for a provider is <provider>.json; extras are <provider>--<label>-<n>.json.
  4. A key that has been revoked in the dashboard is deleted from the machine in the same pass.

The local envelope

~/.config/grith/provider-keys/anthropic.json
{
"version": 1,
"nonce": "<base64, 12 bytes>",
"ciphertext": "<base64>",
"provider": "anthropic"
}

AES-256-GCM with a fresh nonce per file. The key is derived from your account API key with HKDF-SHA256, so there is no passphrase to remember and no keyring dependency.

⚠️At-rest protection, not end-to-end encryption

Keys arrive from grith.ai decrypted over TLS and are encrypted again locally. The service can read the plaintext - it has to, in order to serve it. Anyone holding your account API key can decrypt the local envelopes, and rotating that API key makes the existing envelopes undecryptable: run grith pro sync again after a rotation to rewrite them.

How a key gets used

At model-call time grith resolves a provider key in this order:

  1. api_key in the [llm.<provider>] section of your config,
  2. the provider's environment variable,
  3. the synced vault above.

So a local key or an environment variable always wins over the team's. The key files are read each time grith builds a model client, so a rotated key lands on the next session rather than needing a daemon restart.

A synced key never appears in grith config output or in the logs - only its file path does.

What this protects, and what it does not

  • Protected: the file on disk. A stray backup or a copied dotfile is useless without the account API key. And a key revoked centrally disappears from every machine that syncs.
  • Not protected: a compromised machine. Whoever can read credentials.json can decrypt the envelopes sitting next to it. This is trusted-device sharing, not hardware isolation.

See also

Last updated: 2026-08-24Edit this page on GitHub →