config

package
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Jul 28, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package config owns credential and endpoint resolution for the CLI.

There are two credentials in play and they are deliberately never conflated:

  • the session (an OAuth access token) is user scoped and only ever sent to the auth host (console.tabstack.ai);
  • an API key is organisation scoped and only ever sent to the product host (api.tabstack.ai).

Because API keys are org scoped, the on-disk shape keys them by organisation id: one user can belong to several orgs and hold a different key in each.

Index

Constants

View Source
const (

	// EnvAPIKey is exported so `auth status` can name the variable it is
	// reporting on without re-declaring the string.
	EnvAPIKey = envAPIKey

	// DefaultBaseURL is the production product API root. Every extract,
	// generate, automate, and research endpoint hangs off this.
	DefaultBaseURL = "https://api.tabstack.ai/v1"

	// DefaultAuthURL is the auth and management host. OAuth and every /cli/*
	// management endpoint hangs off this. The session token goes here and
	// nowhere else.
	DefaultAuthURL = "https://console.tabstack.ai"

	// CurrentVersion is the schema version written into the config file. A file
	// with no version predates org-scoped keys and gets migrated on load.
	CurrentVersion = 1
)

Variables

View Source
var ErrNoAPIKey = errors.New("no API key found")

ErrNoAPIKey is returned when no credential can be resolved at all.

Functions

func ConfigPath

func ConfigPath() (string, error)

ConfigPath returns the path the config file is read from and written to.

func PermissionsOK added in v1.0.2

func PermissionsOK(path string) (os.FileMode, bool)

PermissionsOK reports a config file's permission bits and whether they are owner-only. A missing file counts as fine: there is nothing exposed yet.

func Redact added in v1.0.2

func Redact(s string) string

Redact shortens a secret for display: first four and last four characters, elided in the middle. Anything too short to redact meaningfully collapses entirely, so a short token cannot be reconstructed from its own preview.

func SchemasDir added in v1.0.1

func SchemasDir() (string, error)

SchemasDir returns the default directory pre-defined schemas are pulled into (`schema pull`). It sits alongside the config file under the tabstack config home. The `--storage` flag overrides it per invocation.

Types

type Config

type Config struct {
	AuthURL      string `toml:"auth_url,omitempty"`
	BaseURL      string `toml:"base_url,omitempty"`
	ActiveOrg    string `toml:"active_org,omitempty"`
	LegacyAPIKey string `toml:"legacy_api_key,omitempty"`
	Version      int    `toml:"version"`

	Session *Session             `toml:"session,omitempty"`
	Orgs    map[string]*OrgCreds `toml:"orgs,omitempty"`
}

Config is the on-disk configuration, decoded as TOML.

Field order matters for encoding: TOML tables must follow the scalars of the table they sit in, so Session and Orgs are declared last. Reordering them above a scalar would emit a file that no longer parses.

func (*Config) HasKey added in v1.0.2

func (c *Config) HasKey(id string) bool

HasKey reports whether an organisation has a stored API key.

func (*Config) Org added in v1.0.2

func (c *Config) Org(id string) *OrgCreds

Org returns the stored credentials for an organisation id.

func (*Config) OrgName added in v1.0.2

func (c *Config) OrgName(id string) string

OrgName returns an organisation's display name, falling back to the id when we have never seen a name for it.

func (*Config) ResolveAPIKey added in v1.0.2

func (c *Config) ResolveAPIKey(req KeyRequest) (KeyResolution, error)

ResolveAPIKey picks the product credential for this invocation. It is the one place precedence is decided, so every command resolves identically:

  1. --key/--api-key flag
  2. TABSTACK_API_KEY
  3. the stored key for the --org override, when given
  4. the stored key for the active org
  5. LegacyAPIKey, only while no active org is set

A --org override that has no stored key is an error, never a fallback. Using org A's credential while the user believes they are acting as org B is the worst failure available here, so it is made impossible rather than unlikely.

func (*Config) ResolveAuthURL added in v1.0.2

func (c *Config) ResolveAuthURL(flag string) string

ResolveAuthURL returns the auth and management host, with the same precedence as ResolveBaseURL.

func (*Config) ResolveBaseURL added in v1.0.2

func (c *Config) ResolveBaseURL(flag string) string

ResolveBaseURL returns the product API root: config file, then environment, then flag, each overriding the last.

func (*Config) UpsertOrg added in v1.0.2

func (c *Config) UpsertOrg(id, name string) *OrgCreds

UpsertOrg records or updates an organisation's display name without touching its stored key.

type CredentialStore added in v1.0.2

type CredentialStore interface {
	Load() (*Config, error)
	Save(*Config) error
	Path() string
}

CredentialStore is the whole surface commands use to read and write credentials. Everything goes through it so an OS keychain implementation can be added later without touching command code.

type FileStore added in v1.0.2

type FileStore struct {

	// Warn is where permission warnings go. Defaults to os.Stderr; tests
	// substitute a buffer.
	Warn io.Writer
	// contains filtered or unexported fields
}

FileStore is the shipped CredentialStore: a single TOML file, 0600, inside a 0700 directory.

func NewFileStore added in v1.0.2

func NewFileStore() (*FileStore, error)

NewFileStore builds a store over the default config path.

func NewFileStoreAt added in v1.0.2

func NewFileStoreAt(path string) *FileStore

NewFileStoreAt builds a store over an explicit path. Used by tests and by anything that needs to point at a throwaway config.

func (*FileStore) Load added in v1.0.2

func (f *FileStore) Load() (*Config, error)

Load reads and decodes the config. A missing file is not an error: it yields an empty config at the current version, which is what a fresh install looks like.

Migration is applied in memory only. Nothing is written until the next successful Save, so simply running a read-only command never rewrites a user's file.

func (*FileStore) Path added in v1.0.2

func (f *FileStore) Path() string

Path returns the file the store reads and writes.

func (*FileStore) Save added in v1.0.2

func (f *FileStore) Save(cfg *Config) error

Save writes the config atomically: a temp file in the same directory, chmod 0600 before any content is visible under the real name, then a rename. A half-written credential file is never observable, and the rename cannot land on a different filesystem.

type KeyRequest added in v1.0.2

type KeyRequest struct {
	// Flag is the value of --api-key/--key ("" when unset).
	Flag string
	// OrgOverride is an already-resolved organisation id from --org ("" when
	// unset). It selects which stored key to use for this invocation only and
	// never mutates config.
	OrgOverride string
}

KeyRequest is the per-invocation input to API key resolution.

type KeyResolution added in v1.0.2

type KeyResolution struct {
	APIKey string
	Source KeySource
	// OrgID is the organisation the key belongs to, when it came from stored
	// per-org credentials. Empty for flag, env, and legacy keys.
	OrgID   string
	OrgName string
	// EnvOverriding is true when TABSTACK_API_KEY won, which means the active
	// org is not authoritative for product calls this invocation.
	EnvOverriding bool
}

KeyResolution is the outcome of resolving a product credential.

type KeySource added in v1.0.2

type KeySource string

KeySource describes where a resolved API key came from, so `auth status` can explain the resolution without ever printing the key.

const (
	SourceFlag        KeySource = "flag"
	SourceEnv         KeySource = "environment"
	SourceOrgOverride KeySource = "--org override"
	SourceActiveOrg   KeySource = "active org"
	SourceLegacy      KeySource = "legacy config key"
	SourceNone        KeySource = "unset"
)

type OrgCreds added in v1.0.2

type OrgCreds struct {
	Name       string `toml:"name"`
	APIKey     string `toml:"api_key,omitempty"`
	APIKeyID   string `toml:"api_key_id,omitempty"`
	APIKeyName string `toml:"api_key_name,omitempty"`
}

OrgCreds is one organisation's product credential. Name is display only and can change server-side, which is why Orgs is keyed by organisation id.

type Session added in v1.0.2

type Session struct {
	AccessToken  string    `toml:"access_token"`
	RefreshToken string    `toml:"refresh_token"`
	ExpiresAt    time.Time `toml:"expires_at"`
	Scope        string    `toml:"scope,omitempty"`
	UserEmail    string    `toml:"user_email,omitempty"`
}

Session is the OAuth session: user scoped, auth host only.

func (*Session) Expired added in v1.0.2

func (s *Session) Expired(now time.Time, skew time.Duration) bool

Expired reports whether the access token is past its expiry, allowing for a skew window so we refresh slightly early rather than racing the server.

Jump to

Keyboard shortcuts

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