Encrypted key management
proSharing provider API keys across a team, where they are encrypted, and exactly what that protects against.
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
- 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.
grith pro syncpulls the current set. The daemon does the same on its own at most once every 24 hours.- Each key is written to
~/.config/grith/provider-keys/as an encrypted envelope, mode0600. The first key for a provider is<provider>.json; extras are<provider>--<label>-<n>.json. - A key that has been revoked in the dashboard is deleted from the machine in the same pass.
The local envelope
{
"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:
api_keyin the[llm.<provider>]section of your config,- the provider's environment variable,
- 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.jsoncan decrypt the envelopes sitting next to it. This is trusted-device sharing, not hardware isolation.