cliconfig

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 10, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package cliconfig manages the user's CLI credentials in ~/.instant-config.

SECURITY MODEL (post-2026-05-20, T16 P1-1 fix)

The user's API key is stored in the OS keychain via the secretstore package — macOS Keychain, Linux Secret Service / libsecret, or Windows Credential Manager. When no keychain is available (headless Linux, CI, sandboxed environments) we fall back to writing the key into ~/.instant-config mode 0600 and print a one-time stderr warning so the user knows their bearer token is on disk.

~/.instant-config itself always stores the non-secret display fields (email, plan tier, team name, API base URL, last-saved timestamp) so `instant whoami` can still answer offline without prompting the keychain.

Index

Constants

View Source
const FallbackAPIKeyField = "api_key_fallback"

FallbackAPIKeyField is the JSON field name we use when the keychain is unavailable and we have to write the bearer token to ~/.instant-config. Tests assert that this field is ABSENT when the keychain backend is in use (T16 P1-1 regression).

Variables

View Source
var ErrNotLoggedIn = errors.New("not logged in — run `instant login` to authenticate")

ErrNotLoggedIn is returned when an action requires authentication but no API key is found.

Functions

func Clear

func Clear() error

Clear removes the config file AND clears the secretstore (logout).

Types

type Config

type Config struct {
	// APIKey is the bearer token sent with every authenticated API request.
	// In-memory only; persistence routes through secretstore.
	// Empty = anonymous mode.
	APIKey string `json:"-"`

	// LegacyAPIKey captures any value found in the old `api_key` field on
	// disk so we can migrate it to the keychain on Save().
	LegacyAPIKey string `json:"api_key,omitempty"`

	// FallbackAPIKey is written ONLY when the keychain is unavailable and
	// we must store the secret on disk. Tests assert this is empty when
	// the keychain is in use.
	FallbackAPIKey string `json:"api_key_fallback,omitempty"`

	// Email is the account email, stored for display in `instant whoami`.
	Email string `json:"email,omitempty"`

	// Tier is the current plan: "anonymous", "hobby", "pro", "team".
	Tier string `json:"tier,omitempty"`

	// TeamName is the team name, if the user belongs to a team.
	TeamName string `json:"team_name,omitempty"`

	// APIBaseURL overrides the default https://api.instanode.dev endpoint.
	// Populated when INSTANT_API_URL env var was set at login time.
	APIBaseURL string `json:"api_base_url,omitempty"`

	// SavedAt is when the config was last written (for staleness checks).
	SavedAt time.Time `json:"saved_at,omitempty"`
	// contains filtered or unexported fields
}

Config holds the authenticated user's non-secret display data plus an in-memory copy of the API key loaded from the secretstore (or, on the file-fallback path, from disk).

The on-disk JSON shape includes only:

  • non-secret fields (email, tier, team_name, api_base_url, saved_at)
  • api_key_fallback (set ONLY when the keychain is unavailable)

The legacy `api_key` JSON field is no longer written. We still READ it on Load() so existing installs are migrated transparently into the keychain on the next Save().

func Load

func Load() (*Config, error)

Load reads ~/.instant-config from disk and, where the keychain is available, also reads the API key out of the secretstore.

Migration: if the legacy `api_key` field is present on disk (a config written before this fix) we load it into APIKey and clear it on the next Save() — keychain takes over.

func (*Config) EffectiveTier

func (c *Config) EffectiveTier() string

EffectiveTier returns the tier string, defaulting to "anonymous".

func (*Config) IsAuthenticated

func (c *Config) IsAuthenticated() bool

IsAuthenticated reports whether the config holds valid credentials.

func (*Config) Save

func (c *Config) Save() error

Save writes the non-secret fields to disk and routes the API key into the secretstore. If the secretstore Set fails (no keychain), the key falls back to ~/.instant-config and a one-time stderr warning is emitted.

The on-disk file is always written mode 0600 via an atomic temp+rename.

func (*Config) SecretBackendName

func (c *Config) SecretBackendName() string

SecretBackendName surfaces which secret backend is in use (for `whoami` to truthfully report "Key stored in: macOS Keychain" vs "file").

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL