Documentation
¶
Overview ¶
Package credentials is a small local store for provider API keys, so Kram's accounts screen has somewhere to put a key the user pastes in instead of asking them to export an env var by hand every session (the friction that motivated this package in the first place — env vars only take effect in the exact shell they were exported in, which is an easy thing to get wrong).
This is OS-permission-based protection (like gh/aws CLI's own credential files), not encryption at rest — there's no key-management story for a local single-user CLI tool that would make "encrypted JSON readable only by code that also ships the decryption key" meaningfully safer than a 0600 file. Treat it the same way you'd treat ~/.aws/credentials.
Index ¶
- type OAuthToken
- type Store
- func (s *Store) All() map[string]string
- func (s *Store) Delete(envVar string) error
- func (s *Store) DeleteOAuth(envVar string) error
- func (s *Store) Get(envVar string) string
- func (s *Store) GetOAuth(envVar string) (OAuthToken, bool)
- func (s *Store) Resolve(ctx context.Context, envVar string, ...) (string, error)
- func (s *Store) Set(envVar, key string) error
- func (s *Store) SetOAuth(envVar string, tok OAuthToken) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type OAuthToken ¶
type OAuthToken struct {
Access string `json:"access"`
Refresh string `json:"refresh"`
ExpiresAt time.Time `json:"expires_at"`
}
OAuthToken is a refreshable credential — a short-lived access token plus the refresh token that can mint a new one, as returned by Anthropic's and OpenAI's browser-login flows (internal/oauthflow). Unlike the plain keys map, these expire and must be refreshed before use; see Store.Resolve.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a flat env-var-name -> API-key map, persisted as JSON, plus a separate map of refreshable OAuth tokens (see OAuthToken) for accounts connected via a browser-login subscription flow rather than a pasted developer API key.
func Load ¶
Load reads the credentials file (kramhome.Path("credentials.json")) and the OAuth token file (kramhome.Path("oauth_tokens.json")), or leaves either empty if it doesn't exist yet — a missing file is the normal first-run state, not an error. The two are kept in separate files rather than one so the well-established plain-key format and its existing callers are never touched by the OAuth addition.
func (*Store) All ¶
All returns every stored env-var -> key pair (plain keys only, not OAuth tokens).
func (*Store) DeleteOAuth ¶
DeleteOAuth removes a stored OAuth token and persists immediately.
func (*Store) Get ¶
Get returns the stored key for envVar, or "" if none is set. This never looks at OAuth tokens — a caller that needs a currently-valid credential regardless of which kind was stored should use Resolve instead.
func (*Store) GetOAuth ¶
func (s *Store) GetOAuth(envVar string) (OAuthToken, bool)
GetOAuth returns the stored OAuth token for envVar and whether one exists — note this can be expired; callers that need a currently-valid token should use Resolve, which refreshes automatically.
func (*Store) Resolve ¶
func (s *Store) Resolve(ctx context.Context, envVar string, refresh func(ctx context.Context, refreshToken string) (OAuthToken, error)) (string, error)
Resolve returns a currently-valid credential for envVar. If a stored OAuth token exists and isn't within refreshSkew of expiring, its access token is returned as-is; if it's expired or about to be, refresh is called with the stored refresh token, the result is persisted (via SetOAuth) before returning, and — since some providers don't rotate the refresh token on every refresh — an empty Refresh in the result is treated as "unchanged" rather than discarding the still-valid one. If no OAuth token is stored for envVar at all, this falls back to the plain Get(envVar) value, so callers that don't care which kind of credential is configured can always call Resolve.