secrets

package
v1.222.0-rc.2 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// ScopeInstance stores a value per component instance (stack + component). Default.
	ScopeInstance = providers.ScopeInstance
	// ScopeStack stores a single value shared by every instance in a stack.
	ScopeStack = providers.ScopeStack
	// ScopeGlobal stores a single value shared by every stack and component using the backend.
	ScopeGlobal = providers.ScopeGlobal
)

Variables

View Source
var (
	ErrStoreNotFound      = providers.ErrStoreNotFound
	ErrStoreNotSecret     = providers.ErrStoreNotSecret
	ErrProviderNotFound   = providers.ErrProviderNotFound
	ErrDeleteNotSupported = providers.ErrDeleteNotSupported
)

Provider-construction errors are owned by the providers package; re-export the ones callers and tests assert on so they remain reachable from pkg/secrets.

View Source
var (
	// ErrSecretNotDeclared indicates a secret name was used but not declared in the
	// component's secrets.vars (or an inherited declaration).
	ErrSecretNotDeclared = errors.New("secret is not declared")

	// ErrSecretMissing indicates a declared secret has no value in its backend.
	ErrSecretMissing = errors.New("secret is not initialized in its backend")

	// ErrUndeclaredKey indicates an imported/pushed key has no matching declaration.
	ErrUndeclaredKey = errors.New("key is not declared as a secret")

	// ErrNoBackend indicates a declaration references no backend (neither store nor sops).
	ErrNoBackend = errors.New("secret declaration has no backend (set `store:` or `sops:`)")

	// ErrAmbiguousBackend indicates a declaration references more than one backend.
	ErrAmbiguousBackend = errors.New("secret declaration must set exactly one of `store:` or `sops:`")

	// ErrKeygenUnsupported indicates the referenced vault's backend cannot generate a key.
	ErrKeygenUnsupported = errors.New("vault backend does not support key generation")

	// ErrInvalidSecretArgs indicates the !secret function received invalid arguments.
	ErrInvalidSecretArgs = errors.New("invalid !secret arguments")

	// ErrEmptyName indicates an empty secret name.
	ErrEmptyName = errors.New("secret name cannot be empty")

	// ErrScopeConflict indicates a declaration carries an explicit `scope` that conflicts with the
	// scope implied by its position (the one-way rule: an instance-declared secret can never be
	// stack-scoped, and a stack-level declaration can't be instance-scoped).
	ErrScopeConflict = errors.New("secret scope conflicts with declaration position")

	// ErrSecretNotOverridable indicates an attempt to set an instance-level value for a secret that
	// is stack-scoped at the targeted component. Overriding requires the instance to opt in by
	// declaring the secret under the component; otherwise the write is rejected (no silent shadow).
	ErrSecretNotOverridable = errors.New("secret is stack-scoped and not overridable at this instance")

	// ErrSopsCollision indicates two scopes resolve to a colliding SOPS file: distinct instances
	// sharing a file (no isolation), or a stack-scoped secret resolving per-component (not shared).
	ErrSopsCollision = errors.New("SOPS secret files collide across scopes")

	// ErrImportSourceStore indicates the import source store could not be determined: the
	// declaration is not store-backed and no explicit source store was given.
	ErrImportSourceStore = errors.New("import source store is required (the declaration has no `store:` to default to)")

	// ErrImportSourceRead indicates the source value could not be read from the source store
	// coordinate during an import.
	ErrImportSourceRead = errors.New("failed to read import source value")
)

Sentinel errors for the secrets subsystem.

Functions

func DetectSopsCollisions

func DetectSopsCollisions(placements []SopsPlacement) error

DetectSopsCollisions returns an error if the placements violate the scope-vs-file invariants that keep stack and instance secrets safe:

  • instance-scoped: two distinct (stack, component) instances must not resolve to the same file (a shared file means an "instance override" would silently overwrite another instance);
  • stack-scoped: a (stack, secret) must resolve to a single file independent of component (a per-component file means the secret is not actually shared).

The derive-in-code default never trips these; it exists to catch a hand-written `spec.file` template that does not discriminate by component (or that discriminates a stack-scoped secret).

func ExtractDeclarations

func ExtractDeclarations(componentSection map[string]any) map[string]Declaration

ExtractDeclarations reads the `secrets.vars` map from a resolved component section and returns the declared secrets keyed by name. Inheritance is already applied by the standard Atmos stack-merge pipeline, so this simply reads the merged map. Unknown/zero values are tolerated; a declaration with neither `store` nor `sops` is returned with an empty BackendName so callers can surface ErrNoBackend at use time.

func ExtractProviders

func ExtractProviders(componentSection map[string]any) map[string]any

ExtractProviders reads the `secrets.providers` map from a resolved component section. This lets SOPS providers be declared in a stack/component rather than only in atmos.yaml.

func Resolve

func Resolve(atmosConfig *schema.AtmosConfiguration, input, currentStack string, stackInfo *schema.ConfigAndStacksInfo) (any, error)

Resolve resolves a `!secret NAME [| path ...] [| default ...]` expression to a value.

Behavior (in order):

  1. If the processing scope is an inspection command with masking enabled (stackInfo.SecretsMaskOnly), it returns the mask replacement WITHOUT retrieving the value from the backend — no provider call, no credentials required.
  2. Otherwise it looks up the declaration in the component section, resolves the backend provider, retrieves the value, applies the optional path/default modifiers, registers the value (recursively) with the I/O masker, and returns it.

func TagScope

func TagScope(section map[string]any, scope Scope) (map[string]any, error)

TagScope returns a copy of a `secrets:` section with the derived scope stamped onto every declaration under `secrets.vars`. Stamping happens by declaration position before the standard Atmos deep-merge, so "most-specific wins" resolves stack-vs-instance overrides for free (a component-level `instance` tag overrides an inherited stack-level `stack` tag) and ExtractDeclarations reads the resolved scope after the merge.

The input section is NOT mutated — the stack-level (global) section is shared across every component in a stack, so mutating it in place would race under concurrent stack processing.

A declaration carrying an explicit `scope` that conflicts with the positional scope returns ErrScopeConflict, enforcing the one-way rule: an instance-declared secret can never be stack-scoped, and a stack-level declaration can't be instance-scoped. An explicit `scope: global` is exempt — it is strictly more shared than either position implies, so it is honored wherever the declaration appears (typically a catalog fragment imported anywhere).

Types

type BackendType

type BackendType string

BackendType distinguishes a store-backed (track 1) declaration from a SOPS (track 2) one.

const (
	// BackendStore is a store-backed secret (a `secret: true` store). It mirrors the
	// providers registry track key so the two never drift.
	BackendStore BackendType = BackendType(providers.TrackStore)
	// BackendSops is a SOPS file-backed secret. It mirrors the providers registry track key.
	BackendSops BackendType = BackendType(providers.TrackSops)
)

type Declaration

type Declaration struct {
	// Name is the secret name (the key used by `!secret NAME`).
	Name string
	// Description is a human-readable description.
	Description string
	// BackendType selects the backend track (store vs sops).
	BackendType BackendType
	// BackendName is the referenced store name (track 1) or SOPS provider name (track 2).
	BackendName string
	// Reference is an optional backend-specific address for the secret (e.g. a 1Password
	// `op://vault/item/field` reference). It may contain Go-template vars ({{ .atmos_stack }},
	// {{ .atmos_component }}). When set it overrides Name as the backend key; backends that key
	// off the declaration name (most stores, SOPS) ignore it.
	Reference string
	// Required marks the secret as required for validation.
	Required bool
	// Scope is the addressing level (stack vs instance). It is derived from declaration position
	// during stack processing (top-level `secrets:` → stack; component `secrets:` → instance) and
	// stamped onto the declaration; an empty Scope is treated as ScopeInstance.
	Scope Scope
}

Declaration is a single declared secret resolved from a component's secrets.vars.

func LookupDeclaration

func LookupDeclaration(componentSection map[string]any, name string) (Declaration, bool)

LookupDeclaration returns the declaration for a secret name from a component section.

func (*Declaration) IsStackScoped

func (d *Declaration) IsStackScoped() bool

IsStackScoped reports whether the declaration is stored once per stack (shared by all instances).

type GenerableVault

type GenerableVault struct {
	Track string
	Name  string
}

GenerableVault identifies a vault whose backend supports key generation (the registry-pattern keygen capability). Track is the backend track (e.g. "sops"); Name is the vault name.

type ImportSource

type ImportSource struct {
	// Store is the source store name. Empty selects the declaration's own `store:`.
	Store string
	// Stack is the source stack path segment (raw, not validated against real stacks).
	Stack string
	// Component is the source component path segment (raw; empty for stack-scoped legacy paths).
	Component string
	// Key is the source key. Empty selects the declaration name.
	Key string
}

ImportSource identifies an existing store coordinate to adopt a value from. Stack and Component are raw source path segments (typically copied verbatim from a legacy `!store <store> <stack> <component> <key>` expression) — they need not name a real Atmos stack or component, and either may be empty to omit that segment from the source path.

type ResolveOptions

type ResolveOptions struct {
	// Path is an optional YQ-style path expression applied to a structured secret value.
	Path string
	// Default is an optional fallback value used when the secret is missing.
	Default *string
}

ResolveOptions carries optional modifiers parsed from the !secret function or CLI flags.

type Scope

type Scope = providers.Scope

Scope is the addressing level at which a secret value is stored (see providers.Scope). It is re-exported here so the secrets service and CLI can branch on a declaration's scope without importing the providers package directly.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the CRUD facade for the `atmos secret` CLI. It operates over declarations extracted from a component section and dispatches to the appropriate backend provider.

func NewService

func NewService(atmosConfig *schema.AtmosConfiguration, stack, component string, componentSection map[string]any) *Service

NewService creates a Service scoped to a (stack, component) and its resolved component section (which carries the secrets.vars declarations after inheritance/merge).

func (*Service) Declarations

func (s *Service) Declarations() []Declaration

Declarations returns the declared secrets for the service's scope, sorted by name.

func (*Service) Delete

func (s *Service) Delete(name string) error

Delete removes a declared secret's value from its backend.

func (*Service) DeleteAll

func (s *Service) DeleteAll() (int, error)

DeleteAll removes every declared secret's value from its backend, returning the number of declarations processed. Delete is idempotent (it no-ops on values that are not initialized), so this also serves as a clean "reset" of a SOPS file's declared keys.

func (*Service) FileDependencies

func (s *Service) FileDependencies() []string

FileDependencies returns the distinct backing files this scope's file-based declared secrets (currently SOPS) resolve to, for `describe affected` to treat as implicit dependencies: a changed secret file then marks every component that consumes it. Secrets whose backend is not file-based (store-backed) contribute nothing. It is best-effort — declarations whose provider or path cannot be resolved are skipped rather than failing the whole computation. Results are de-duplicated and sorted.

func (*Service) GenerateKeyForVault

func (s *Service) GenerateKeyForVault(v GenerableVault) (*providers.KeygenResult, error)

GenerateKeyForVault generates key material for a vault referenced by this scope and returns what the backend produced.

func (*Service) Get

func (s *Service) Get(name string, opts ResolveOptions) (any, error)

Get retrieves a value for a declared secret, registering it with the masker.

func (*Service) ImportFromStore

func (s *Service) ImportFromStore(name string, src ImportSource, dryRun bool) error

ImportFromStore copies an existing secret value from a source store coordinate into the declared secret's computed coordinate — terraform-import-style adoption that brings a value created outside the secrets subsystem (e.g. legacy `!store` usage) under management. The source value is read, registered with the masker, and written through the declaration's normal Set path (sensitivity flag, scope-derived coordinate); the source is never modified or deleted. With dryRun the source is read — proving it exists and is accessible — but nothing is written.

func (*Service) IsDeclared

func (s *Service) IsDeclared(name string) bool

IsDeclared reports whether a key is declared as a secret in this scope.

func (*Service) Reset

func (s *Service) Reset() (bool, error)

Reset overwrites a file-based provider's backing file (e.g. SOPS) with a clean, empty document for this scope, creating it if missing. It is a no-op for backends that are not file-based (they have no whole-file state to reset). Returns whether any provider was reset.

func (*Service) ScopeOf

func (s *Service) ScopeOf(name string) (Scope, bool)

ScopeOf returns the resolved scope of a declared secret and whether it is declared. An undeclared name returns ("", false). A declaration with no explicit scope defaults to ScopeInstance.

func (*Service) Set

func (s *Service) Set(name string, value any) error

Set stores a value for a declared secret.

func (*Service) SopsPlacements

func (s *Service) SopsPlacements() []SopsPlacement

SopsPlacements returns the resolved SOPS file placement for each SOPS-backed declared secret in this service's scope. Non-SOPS (store-backed) secrets and unresolvable providers are skipped.

func (*Service) Status

func (s *Service) Status() []Status

Status reports whether each declared secret is initialized in its backend. It never registers values with the masker (uses the backend status check, not Get).

func (*Service) Validate

func (s *Service) Validate() ValidationResult

Validate checks that every required declared secret is initialized in its backend.

func (*Service) VaultsMissingKeys

func (s *Service) VaultsMissingKeys() ([]GenerableVault, error)

VaultsMissingKeys returns the vaults referenced by this scope's declarations whose backend supports key generation but has no key material yet, so callers can offer to generate one. It is backend-agnostic: any provider implementing providers.KeyGenerator participates. Results are de-duplicated and sorted by name.

type SopsPlacement

type SopsPlacement struct {
	Stack     string
	Component string
	Secret    string //nolint:gosec // holds the secret's NAME (identifier), never its value.
	Scope     Scope
	File      string
}

SopsPlacement is a single SOPS-backed secret's resolved file location within a scope. It is the input to the collision lookup that guards the advanced `spec.file` template path (the derive-in- code default is collision-safe by construction, so it never produces violations here).

type Status

type Status struct {
	Declaration Declaration
	Coordinate  providers.Coordinate
	Initialized bool
	// Err holds any error encountered while checking status (e.g. access denied).
	Err error
}

Status describes whether a declared secret is initialized in its backend.

type ValidationResult

type ValidationResult struct {
	// MissingRequired lists required secrets that are not initialized in their backend.
	MissingRequired []Status
	// Errored lists secrets whose status could not be determined (e.g. access denied).
	Errored []Status
	// All holds the full per-secret status set.
	All []Status
}

ValidationResult summarizes the outcome of validating a scope's declared secrets.

func (ValidationResult) Valid

func (r ValidationResult) Valid() bool

Valid reports whether all required secrets are initialized and no status errors occurred.

Directories

Path Synopsis
Package providers implements the secret backend providers for the Atmos secrets subsystem.
Package providers implements the secret backend providers for the Atmos secrets subsystem.

Jump to

Keyboard shortcuts

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