Documentation
¶
Overview ¶
Package config owns everything PayCLI knows before it talks to a server: where its files live (§4.1), what is in them (§4.2, §4.3), how the layers combine (§4.5) and what can be learned about the surrounding Payload project from the local filesystem alone (§7.10, §7.11).
Two rules shape this package:
- Resolve is a pure function. It performs no I/O whatsoever, so §4.5's precedence matrix is a table test (§3.1).
- Nothing here reads the process environment directly. It enters as an Env snapshot built once by internal/cli/app.go, which is the only file allowed to touch it (§3.1).
Index ¶
- Constants
- Variables
- func BlockSlugsFromSource(src string) []string
- func Interpolate(s string, env Env) (string, []string)
- func InterpolateMap(in map[string]string, env Env) (map[string]string, []string)
- func ProfileNames(files ...*File) []string
- type AuthMode
- type CacheSection
- type Defaults
- type Env
- type File
- type Flags
- type Input
- type KeyringMode
- type LoggingSection
- type Paths
- func (p Paths) AuditFile() string
- func (p Paths) CacheRoot() string
- func (p Paths) CredentialsFile() string
- func (p Paths) Map() map[string]string
- func (p Paths) ScopeDir(scope string) string
- func (p Paths) UpdateStateFile() string
- func (p Paths) WithCacheDir(dir string) Paths
- func (p Paths) WithConfigFile(path string) Paths
- type Profile
- type Project
- type Resolved
- type ScanResult
- type Setting
- type UpdateSection
Constants ¶
const ( KindUser = "user" KindProject = "project" )
Layer kinds for the source strings of §4.5.
const ( ConfigFileName = "config.toml" CredentialsFileName = "credentials.json" AuditFileName = "audit.log" UpdateStateFileName = "update-state.json" ProjectFileName = "pay.toml" // CacheGeneration is the versioned sub-directory of the cache dir; the // whole tree below it is disposable (§4.1, §8). CacheGeneration = "v1" )
File names inside the resolved directories (§4.1).
const ( DBPostgres = "postgres" DBMongoDB = "mongodb" DBSQLite = "sqlite" DBUnknown = "unknown" )
DBAdapter values (§7.11).
const ( SourceConfigured = "configured" SourceProjectPkg = "project-package-json" SourceProjectSource = "project-source" SourceInferred = "inferred" SourceObserved = "observed" SourceUnknown = "unknown" )
Provenance strings shared by §7.10/§7.11 and `pay config explain`.
const ( MaxScanFiles = 200 MaxScanBytes = 2 << 20 // 2 MB total )
Scan caps (§7.10). They exist so that pointing PayCLI at a monorepo cannot turn a cache miss into a filesystem crawl.
const ( DefaultProfileName = "default" DefaultAPIPath = "/api" DefaultGraphQLRoute = "/graphql" DefaultAuthCollection = "auto" DefaultAuthHeaderScheme = "JWT" DefaultOutput = "json" DefaultErrorsTo = "stdout" DefaultDepth = 0 DefaultLimit = 20 DefaultMaxRetries = 3 DefaultConcurrency = 8 DefaultMaxBulk = 100 DefaultAcceptLanguage = "en" DefaultLogLevel = "info" DefaultLogFormat = "text" DefaultUpdateChannel = "stable" // MaxConcurrency is §9.1's hard cap. PayCLI is the only backpressure a // Payload 3.x server has. MaxConcurrency = 32 )
Built-in defaults (§4.2). These are the last link of §4.5's chain.
const ( DefaultTimeout = 30 * time.Second DefaultDeadline = 120 * time.Second DefaultDiscoveryTTL = 24 * time.Hour DefaultAccessTTL = 10 * time.Minute DefaultSchemaTTL = 24 * time.Hour DefaultIdentityTTL = 60 * time.Second DefaultSkillsTTL = 6 * time.Hour )
Default durations (§4.2).
const ( SourceFlag = "flag" SourceDefault = "default" // SourceDerivedGraphQL is §4.2's visible coupling between api_path and // graphql_route. SourceDerivedGraphQL = "derived:api_path+graphql_route" )
Source strings that are not a layer name.
const FilePerm fs.FileMode = 0o600
FilePerm is the mode of config.toml (§4.1). It is 0600 like everything else PayCLI writes: the file holds header values that §5.3 treats as secret.
Variables ¶
var AuthModes = []AuthMode{AuthModeAuto, AuthModeAPIKey, AuthModeJWT, AuthModeAnonymous}
AuthModes is the accepted set, in help order.
Functions ¶
func BlockSlugsFromSource ¶
BlockSlugsFromSource extracts `slug: '…'` literals that sit in an object literal which also has a `fields:` key (§7.10).
This is a byte scanner, not a TypeScript parser: PayCLI must never import or evaluate project code. It understands strings, template literals and both comment forms well enough that a slug inside one of them cannot be mistaken for a declaration, and it pairs slug/fields by brace nesting, so a nested block definition is picked up while an unrelated `slug:` on a collection export without `fields:` is not.
Order is source order; duplicates are removed.
func Interpolate ¶
Interpolate expands ${NAME} references in a config value against env (§4.2's `"X-Vercel-Protection-Bypass" = "${VERCEL_BYPASS}"`).
Deliberately minimal, because this string can end up in an HTTP header and in §8.1's headersFP:
- ${NAME} only. A bare $NAME is left alone, so a literal dollar in a token survives untouched.
- $${NAME} escapes to the literal ${NAME}.
- An unset (or set-but-empty, §4.5) variable expands to "" and its name is returned in missing, so the caller can warn instead of silently sending an empty header.
The returned missing slice is sorted and de-duplicated.
func InterpolateMap ¶
InterpolateMap expands every value of a header map. Keys are never interpolated: a header *name* built from the environment would make §8.1's headersFP and the redaction allow-list unpredictable.
func ProfileNames ¶
ProfileNames returns the union of profile names across the layers, sorted, for did-you-mean and `pay auth list`.
Types ¶
type AuthMode ¶
type AuthMode string
AuthMode is §5.0's three-way auth selector plus "auto".
const ( AuthModeAuto AuthMode = "auto" AuthModeAPIKey AuthMode = "api-key" AuthModeJWT AuthMode = "jwt" AuthModeAnonymous AuthMode = "anonymous" )
Auth modes. "auto" is a *configuration* value only: it never survives credential resolution, which always lands on one of the other three (§5.1).
func ParseAuthMode ¶
ParseAuthMode validates --auth-mode / PAY_AUTH_MODE / the profile key.
type CacheSection ¶
type CacheSection struct {
Dir string `toml:"dir,omitempty"`
DiscoveryTTL string `toml:"discovery_ttl,omitempty"`
AccessTTL string `toml:"access_ttl,omitempty"`
SchemaTTL string `toml:"schema_ttl,omitempty"`
IdentityTTL string `toml:"identity_ttl,omitempty"`
SkillsTTL string `toml:"skills_ttl,omitempty"`
}
CacheSection is the [cache] table (§4.2).
type Defaults ¶
type Defaults struct {
Output string `toml:"output,omitempty"`
Depth *int `toml:"depth,omitempty"`
Limit *int `toml:"limit,omitempty"`
Timeout string `toml:"timeout,omitempty"`
Deadline string `toml:"deadline,omitempty"`
MaxRetries *int `toml:"max_retries,omitempty"`
Concurrency *int `toml:"concurrency,omitempty"`
Redact *bool `toml:"redact,omitempty"`
AcceptLanguage string `toml:"accept_language,omitempty"`
MaxBulk *int `toml:"max_bulk,omitempty"`
ConfirmWrites *bool `toml:"confirm_writes,omitempty"`
Locale string `toml:"locale,omitempty"`
FallbackLocale string `toml:"fallback_locale,omitempty"`
ErrorsTo string `toml:"errors_to,omitempty"`
}
Defaults is the [defaults] table (§4.2). Pointers everywhere: `depth = 0` and `redact = false` are meaningful settings, not absence.
type Env ¶
Env is a snapshot of the process environment.
Lookup implements §4.5's "set but empty counts as unset" rule, so `PAY_API_KEY= pay …` cannot silently blank a credential.
type File ¶
type File struct {
Version int `toml:"version,omitempty"`
DefaultProfile string `toml:"default_profile,omitempty"`
Defaults Defaults `toml:"defaults,omitempty"`
Cache CacheSection `toml:"cache,omitempty"`
Logging LoggingSection `toml:"logging,omitempty"`
Update UpdateSection `toml:"update,omitempty"`
Profiles map[string]Profile `toml:"profiles,omitempty"`
// APIKey is not a legal key anywhere (§4.2); it is decoded only so the
// hard error can name it.
APIKey string `toml:"api_key,omitempty"`
// Path is where the file came from; it is what `pay config explain`
// prints as "user:/abs/config.toml" or "project:/abs/pay.toml" (§4.5).
Path string `toml:"-"`
// Exists is false for a file that was absent, which is not an error.
Exists bool `toml:"-"`
// Kind is "user" or "project"; it prefixes the source string.
Kind string `toml:"-"`
// UnknownKeys are decoded-but-unrecognised keys, surfaced as warnings so a
// typo like `base_ur1` is visible without being fatal.
UnknownKeys []string `toml:"-"`
}
File is a decoded config.toml (§4.2) or pay.toml (§4.3). The same schema serves both; a project file simply must not carry secrets.
func Load ¶
Load reads and validates a config file. A missing file yields a usable empty File with Exists == false and no error: PayCLI must work with no config at all, driven entirely by flags and environment (§4.5, §21).
func Parse ¶
Parse decodes TOML bytes. It is the I/O-free half of Load, which is what the tests use.
func (*File) Save ¶
Save writes the file atomically at 0600 (§4.1). Version is forced to 1 so a hand-written file that omitted it does not round-trip as 0.
func (*File) SetProfile ¶
SetProfile inserts or replaces a profile.
func (*File) Source ¶
Source renders this layer's §4.5 source string, e.g. "user:/home/u/.config/pay/config.toml".
type Flags ¶
type Flags struct {
Profile string
BaseURL string
APIPath string
GraphQLPath string
GraphQLRoute string
AuthCollection string
AuthMode string
AuthHeaderScheme string
Output string
ErrorsTo string
Path string
Locale string
FallbackLocale string
AcceptLanguage string
Timeout string
Deadline string
CacheTTL string
Depth *int
Limit *int
MaxDocs *int
MaxRetries *int
Concurrency *int
Redact *bool
InsecureSkipVerify *bool
NoCache *bool
Refresh *bool
Yes *bool
DryRun *bool
NoAudit *bool
Quiet *bool
Verbose *bool
LogLevel string
LogFormat string
}
Flags carries §9.1's root persistent flags. Every field is empty/nil unless the flag was actually changed, which is how "flag" wins §4.5's first slot without a spurious zero value beating a config file.
type Input ¶
Input is everything Resolve is allowed to see. No file handles, no clock, no environment access: the caller loads the layers, Resolve combines them (§3.1).
type KeyringMode ¶
type KeyringMode string
KeyringMode is PAY_KEYRING (§5.2).
const ( KeyringAuto KeyringMode = "auto" KeyringOff KeyringMode = "off" KeyringForce KeyringMode = "force" )
Keyring modes.
func ParseKeyringMode ¶
func ParseKeyringMode(s string) (KeyringMode, error)
ParseKeyringMode validates PAY_KEYRING. The default is "off": §5.1 step 8 reads the keychain only when the profile opted in or PAY_KEYRING=force, so an unset variable must not make the chain touch dbus/wincred. "auto" only affects *writes* (§5.2).
type LoggingSection ¶
type LoggingSection struct {
Level string `toml:"level,omitempty"`
Format string `toml:"format,omitempty"`
}
LoggingSection is the [logging] table (§4.2).
type Paths ¶
type Paths struct {
ConfigDir string
CacheDir string
StateDir string
ConfigFile string
Sources map[string]string
}
Paths is the one source of truth for where PayCLI keeps its files (§4.1). Sources explains, per directory, which rule produced it, so `pay config paths --output json` never has to guess.
func DefaultPaths ¶
DefaultPaths is PathsFor bound to this host. It is the only function in the package that touches the OS, and it never fails: an unknown home directory degrades to the process working directory rather than aborting the CLI.
func PathsFor ¶
PathsFor is the pure form of path resolution: everything it needs is an argument, so the Windows and XDG branches are both testable on any host.
goos is a runtime.GOOS value; home is the user's home directory (may be empty, in which case relative fallbacks are used rather than failing — a missing $HOME must never make `pay version` impossible).
Precedence per directory: the specific PAY_*_DIR variable, then $PAY_HOME, then the platform rule. PAY_HOME "overrides all three" defaults (§4.1) but the more specific variable still wins over it — that is the conventional reading, and it lets a test pin PAY_HOME while redirecting just the cache.
func (Paths) CacheRoot ¶
CacheRoot is the generation directory; `rm -rf` on it must only ever cost time (§4.1).
func (Paths) CredentialsFile ¶
CredentialsFile is §4.4's 0600 credential store.
func (Paths) UpdateStateFile ¶
UpdateStateFile is §15's self-update bookkeeping.
func (Paths) WithCacheDir ¶
WithCacheDir applies config.toml's [cache] dir override (§4.2).
func (Paths) WithConfigFile ¶
WithConfigFile applies the --config flag, which points at the config file itself and therefore also moves nothing else (§9.1).
type Profile ¶
type Profile struct {
BaseURL string `toml:"base_url,omitempty"`
APIPath string `toml:"api_path,omitempty"`
GraphQLRoute string `toml:"graphql_route,omitempty"`
GraphQLPath string `toml:"graphql_path,omitempty"`
AuthCollection string `toml:"auth_collection,omitempty"`
AuthMode string `toml:"auth_mode,omitempty"`
AuthHeaderScheme string `toml:"auth_header_scheme,omitempty"`
Label string `toml:"label,omitempty"`
APIKeyEnv string `toml:"api_key_env,omitempty"`
CredentialHelper string `toml:"credential_helper,omitempty"`
InsecureSkipVerify *bool `toml:"insecure_skip_verify,omitempty"`
Headers map[string]string `toml:"headers,omitempty"`
Blocks map[string][]string `toml:"blocks,omitempty"`
// Optional pins. Each one turns a discovered-or-unknown value into a
// configured one and flips the matching *_source to "configured" (§4.2).
IDType string `toml:"id_type,omitempty"`
Locales []string `toml:"locales,omitempty"`
PayloadVersion string `toml:"payload_version,omitempty"`
DBAdapter string `toml:"db_adapter,omitempty"`
EchoCheckIgnore []string `toml:"echo_check_ignore,omitempty"`
CustomEndpoints []string `toml:"custom_endpoints,omitempty"`
// APIKey is never a legal key (§4.2); see File.Validate.
APIKey string `toml:"api_key,omitempty"`
}
Profile is one [profiles.X] table (§4.2).
Every optional scalar is a pointer or an empty-able string so that "unset" and "set to the zero value" stay distinguishable through §4.5's chain. The APIKey field exists only so that a config.toml carrying the forbidden key decodes instead of landing in the undecoded set: its presence is a hard error (config_secret_in_plaintext).
type Project ¶
type Project struct {
// Dir is the directory that anchored the discovery — the first one going
// up that contained any marker at all.
Dir string
// ConfigPath is ./pay.toml, the committed project config (§4.3).
ConfigPath string
// PackageJSON is the nearest package.json (§7.11 source 2).
PackageJSON string
// PayloadConfig is the nearest payload.config.ts / .js / .mjs, at the root
// or under src/ (§7.10).
PayloadConfig string
// SrcDir is the payload config's directory when it lives in src/.
SrcDir string
// GitRoot is the directory holding .git; the walk stops after it.
GitRoot string
// Start is the directory the walk began in.
Start string
// contains filtered or unexported fields
}
Project is what the upward walk of §4.3 found around the working directory. Every field is optional: PayCLI is usable from any directory, including one that has nothing to do with a Payload project.
func FindProject ¶
FindProject walks up from start looking for a project (§4.3). It stops at the git root, at the filesystem root, or after maxWalkDepth directories.
The walk never leaves the filesystem and never executes anything it finds.
func (*Project) GitignoreCovers ¶
GitignoreCovers reports whether the project's .gitignore mentions pattern. `pay auth login` uses it to warn when a skill directory or a credential file would be committed (§5.3). A missing .gitignore reports false.
func (*Project) IsPayload ¶
IsPayload reports whether this looks like a Payload project specifically — the precondition for §7.10's block-slug scan and §7.11's version read.
func (*Project) LoadConfig ¶
LoadConfig loads the project pay.toml, if the walk found one. A project without pay.toml is normal and yields an empty, non-existent File.
type Resolved ¶
type Resolved struct {
Profile string
ProfileDefined bool
Label string
BaseURL string
APIPath string
GraphQLRoute string
GraphQLPath string
AuthCollection string
AuthMode AuthMode
AuthHeaderScheme string
APIKeyEnv string
CredentialHelper string
Headers map[string]string
KeyringMode KeyringMode
Output string
ErrorsTo string
Path string
Locale string
FallbackLocale string
AcceptLanguage string
Timeout time.Duration
Deadline time.Duration
Depth int
Limit int
MaxDocs int
MaxRetries int
Concurrency int
MaxBulk int
Redact bool
ConfirmWrites bool
InsecureSkipVerify bool
NoCache bool
Refresh bool
CacheTTL time.Duration
Yes bool
DryRun bool
NoAudit bool
Quiet bool
Verbose bool
CacheDir string
DiscoveryTTL time.Duration
AccessTTL time.Duration
SchemaTTL time.Duration
IdentityTTL time.Duration
SkillsTTL time.Duration
LogLevel string
LogFormat string
UpdateAuto bool
UpdateChannel string
// Optional pins (§4.2). An empty value means "not configured"; the
// matching *Source entry says so explicitly.
IDType string
Locales []string
PayloadVersion string
DBAdapter string
EchoCheckIgnore []string
CustomEndpoints []string
Blocks map[string][]string
// ConcurrencyClamped is true when the requested concurrency exceeded
// MaxConcurrency and was lowered.
ConcurrencyClamped bool
// Warnings are non-fatal configuration observations for the caller to put
// in the envelope (§10): a moved base_url credential, an unset ${ENV} in a
// header, an unknown key in a config file.
Warnings []string
// Sources maps a setting name to its §4.5 provenance string.
Sources map[string]string
// contains filtered or unexported fields
}
Resolved is the effective configuration for one command invocation.
basicAuth is deliberately unexported and has no JSON/TOML tag anywhere: it holds a credential lifted out of base_url (§4.2) and must never be serialised with the rest of the struct.
func Resolve ¶
Resolve combines the layers of §4.5. It is pure: given the same Input it returns the same Resolved, and it never touches the filesystem, the clock or the environment beyond the supplied snapshot.
func (*Resolved) BasicAuth ¶
BasicAuth returns the credential that was carried in base_url, if any (§4.2). The transport applies it as an Authorization: Basic header, where it is redacted like every other credential.
func (*Resolved) Explain ¶
Explain renders `pay config explain --output json` (§4.5). Header values are masked: the names are the only part that may be shown (§5.3).
func (*Resolved) HeaderNames ¶
HeaderNames returns the configured extra header names, sorted. Only names ever leave this process; values are secret (§5.3, §8.1).
func (*Resolved) NormURL ¶
NormURL is §8.1's normalised URL: lowercase scheme and host, the port only when it is non-default, plus api_path. Userinfo is already gone by construction. It lives here because it is a pure function of resolved config, and internal/cache must not re-derive it differently.
func (*Resolved) RequireBaseURL ¶
RequireBaseURL is the check every command that talks to a server performs. Resolve itself tolerates an absent base_url so that `pay version`, `pay config paths` and `pay explain` still work on a bare machine.
type ScanResult ¶
type ScanResult struct {
Root string `json:"root,omitempty"`
PayloadVersion string `json:"payload_version,omitempty"`
PayloadVersionSource string `json:"payload_version_source"`
PackageJSON string `json:"package_json,omitempty"`
// DBAdapter is inferred from the project's @payloadcms/db-* dependency.
// §7.11 only defines "configured" and "inferred" for this field, so a
// package.json match reports "inferred" — which deliberately does NOT
// unlock §9.3's client-side operator block, since that requires
// "configured".
DBAdapter string `json:"db_adapter"`
DBAdapterSource string `json:"db_adapter_source"`
BlockSlugs []string `json:"blocks,omitempty"`
BlocksSource string `json:"blocks_source"`
BlockSlugFiles []string `json:"block_slug_files,omitempty"`
SlugFile map[string]string `json:"-"`
FilesScanned int `json:"files_scanned"`
BytesScanned int64 `json:"bytes_scanned"`
Truncated bool `json:"truncated,omitempty"`
}
ScanResult is everything §7.10 and §7.11 can learn from the project on disk. Every fact carries its provenance; nothing here is ever guessed.
func Scan ¶
func Scan(p *Project) *ScanResult
Scan reads what the local filesystem knows about the project: the Payload version from package.json (§7.11) and the block slugs from project source (§7.10).
It is local-filesystem only. It never makes a network call, never imports or evaluates project code, and never writes anything.
type UpdateSection ¶
type UpdateSection struct {
Auto *bool `toml:"auto,omitempty"`
Channel string `toml:"channel,omitempty"`
}
UpdateSection is the [update] table (§4.2).