Credentials
Do not put secrets in the workspace, in this repository, or in a commit. Crystal Code never writes secrets into the project tree.
Store a persistent API key in ~/.crystal/credentials.json, keyed by provider name. The value is plain text. Where the operating system allows it, the file is owner-only. Child processes do not inherit it. Use it for an interactive install.
A process environment variable overrides the file. Use one for a single launch or a CI job. A key in a shell startup file is loaded by login shells, including the login shell started for model-invoked commands, and those files are often readable by other users.
Resolution order for the active provider:
- Process environment (below).
apiKeyon the effective provider definition.~/.crystal/credentials.json, keyed by provider name.
The effective definition is the entry in providers.json. If that file is absent, it is the legacy config.json.providers entry. When providers.json exists, the legacy field is not used. See Configuration.
Environment names, in order:
apiKeyEnvironmenton that definition, when set.<PROVIDER>_API_KEY, derived from the provider name. Hyphens become underscores. Examples:DEEPSEEK_API_KEY,OPENAI_API_KEY,OPENROUTER_API_KEY.CRYSTAL_API_KEY, the shared fallback.
A provider-specific variable wins over CRYSTAL_API_KEY.
apiKey on the definition may be:
| Form | Meaning |
|---|---|
{env:NAME} |
Read the named process environment variable |
{file:path} |
Read a file. Relative to ~/.crystal, or absolute. ~ is expanded |
| a literal string | Used as-is. Avoid this in shared files |
Keep the secret out of providers.json. A key that stays on this machine belongs in credentials.json. {env:NAME} fits a launch or CI job that already exports the variable. {file:path} fits a key that already lives in another owner-only file.
{ "deepseek": { "apiKey": "" }}Leave the value empty in examples and in any file that might be shared. Put the real secret in the local credentials.json.
If no key is found, the process prints an English error and exits with status 1. It does not print the secret.