earl

Secrets

Secure secret storage, the safety model, and how secrets flow through templates

Earl stores secret values in the OS keychain (macOS Keychain, Linux Secret Service, Windows Credential Manager). Values are never written to disk in plaintext.

Safety Model

The secret management system is designed around a core principle: AI agents can use secrets but never read them.

Secrets live in the OS keychain only

When you run earl secrets set, the value is passed directly to the OS credential store via the keyring crate. Earl uses the service name earl for all keychain entries. The plaintext value never touches the filesystem.

The CLI never exposes secret values

earl secrets get returns metadata only -- the key name, created timestamp, and updated timestamp. The value is always shown as [REDACTED]. There is no CLI flag or command to print the raw value.

Templates can reference secrets at render time

Operation templates (URL, headers, auth blocks, body) can reference secrets.<key> during request construction. The secret is fetched from the keychain, injected into the request, and then discarded from memory.

Output templates cannot access secrets

The result.output template has no access to the secrets.* namespace. Even if a template author accidentally writes {{ secrets.api_key }} in the output block, it will not resolve.

Redaction catches accidental leaks

If a secret value appears in API response data, earl's redactor replaces it with [REDACTED] before the output reaches the user or agent. The redactor checks the raw value plus its base64, hex, and URL-encoded variants.

Linux: A Secret Service daemon is required (gnome-keyring, kwallet, or keepassxc with Secret Service enabled). If no daemon is running, earl secrets set will fail with a keyring error. See Troubleshooting for setup guidance.

Commands

Store a secret

# Interactive prompt (value is not echoed to the terminal)
earl secrets set github.token

# Read from stdin (useful in CI or scripting)
printf '%s' "$GITHUB_TOKEN" | earl secrets set github.token --stdin

The --stdin flag reads the value from standard input and trims trailing newlines.

Show metadata

earl secrets get github.token

Output:

key: github.token
created_at: 2025-06-15T10:32:00Z
updated_at: 2025-06-15T10:32:00Z
value: [REDACTED]

If the key does not exist, earl exits with an error.

List all secrets

earl secrets list

Displays a table of all known secret keys with their created and updated timestamps. No values are shown. If no secrets have been stored, earl prints "No secrets found."

Delete a secret

earl secrets delete github.token

Removes the secret from both the OS keychain and the metadata index. If the secret did not exist, earl prints a message and exits normally.

Metadata Index

Earl maintains a JSON metadata index at ~/.local/state/earl/secrets-index.json to track which keys exist and when they were created or updated. This file:

  • Contains only key names and timestamps -- never secret values
  • Is written with 0600 permissions on Unix (owner read-write only)
  • Is automatically created and updated when you use earl secrets set or earl secrets delete

The index enables earl secrets list and earl secrets get to show metadata without querying the keychain for every possible key.

Referencing Secrets in Templates

Secrets are referenced by key name in template auth blocks and expressions:

auth {
  kind   = "bearer"
  secret = "github.token"
}
auth {
  kind     = "api_key"
  location = "header"
  name     = "X-API-Key"
  secret   = "stripe.api_key"
}

API keys can be placed in a header, query parameter, or cookie.

auth {
  kind            = "basic"
  username        = "deploy"
  password_secret = "registry.password"
}
auth {
  kind    = "oauth2_profile"
  profile = "my_provider"
}

The OAuth2 token is fetched automatically. See Auth for profile configuration.

For SQL templates, the database connection URL is referenced as a secret:

operation {
  sql {
    connection_secret = "mydb.connection_url"
    query = "SELECT * FROM users LIMIT 10"
  }
}

Secret key names use dot notation by convention (e.g., github.token, stripe.api_key, mydb.connection_url). The key in earl secrets set must exactly match the key referenced in the template.

Never put secret values directly in template files. Always store them with earl secrets set and reference by key name.

On this page