Commit 7fc9c3e
feat(secrets): declarative secrets management with !secret, CRUD CLI, and masking (#1911)
* docs: Add secrets management PRD
Adds comprehensive PRD for GitOps-friendly, multi-cloud secrets management in Atmos with Vercel-like developer experience. Covers declarative secret declarations, CRUD CLI commands (init, add, get, rm, list, pull, push, validate), integration with existing store and auth infrastructure, and support for AWS SSM, ASM, SOPS, Vault, Azure Key Vault, and GCP Secret Manager backends.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
* docs: Use set/get/delete as primary commands with add/rm aliases
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: Fix wording - secrets use dedicated CRUD commands, not standalone CLI
* docs: Clarify store lifecycle - populated by Terraform outputs
* docs: Add atmos secret import command for env file import
Adds import command that creates declarations and sets values from .env/JSON/YAML files. Includes prior art research from Doppler, Chamber, and Vercel.
Key differences:
- push: requires pre-declared secrets (fails on undeclared)
- import: creates declarations as needed (bootstrap workflow)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: Fix import command - warns on undeclared, doesn't create declarations
* docs: Use providers pattern consistent with auth in secrets PRD
Rename all occurrences of "backend/backends" to "provider/providers"
and restructure defaults to match auth patterns from auth-default-settings.md.
Changes:
- `secrets.default_backend` → `secrets.defaults.provider`
- `secrets.backends` → `secrets.providers`
- `backend:` → `provider:` in secret declarations
- Backend → Provider in headings and descriptions
- Add references to I/O handling strategy and auth defaults PRDs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: Use spec instead of options for provider config consistency
Rename provider configuration field from `options` to `spec` for
consistency with the logs PRD (atmos-logs.md) pattern.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs(prd): require io masking for list/describe output
* docs(prd): Add sensitive terraform output handling PRD
Addresses gap in secrets management PRD for machine-generated sensitive
outputs flowing between components via !terraform.output and
atmos.Component(). Terraform already provides sensitive metadata;
Atmos I/O masking layer is ready — this PRD wires them together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(prd): Move sensitive terraform outputs PRD to secrets-masking/
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(prd): Fix broken relative links after move to secrets-masking/
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(prd): Promote store sensitivity awareness from future to in-scope
Store sensitivity (SSM SecureString, retrieval-side masking) is part of
the same pipeline as sensitive terraform outputs, not a separate concern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(prd): Extract store sensitivity into separate PRD
Store sensitivity awareness (SecureString, retrieval-side masking) is a
distinct concern from the terraform output pipeline. Split into its own
PRD at secrets-masking/store-sensitivity.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(prd): Unify secrets backend on stores, collapse masking flag, address review
Refine the secrets-management PRD set following PR #1911 review:
- Secret stores via a top-level `secret: true` flag on existing store types
(not a `type: secrets` wrapper); `!store` is refused on them, `!secret` is the
only accessor. Two-track backend model: store-backed (SSM/ASM/Vault/KV/GSM)
reuses the store registry; SOPS stays a native non-store provider.
- Secrets declared in stack config only ("global" = shared import), so every
secret keeps a real (stack, component, key) coordinate.
- Collapse the proposed `--secrets` flag into the existing `--mask`: inspection
commands skip retrieval (no credentials) when `--mask=true`; value-producing
commands always retrieve.
- Auth identity integration: top-level `identity:` on stores resolved via
pkg/auth (supersedes read/write_role_arn); precedence with inheritance of the
component instance's effective identity; SOPS-KMS-only identity.
Address CodeRabbit review comments:
- Register sensitive structured (map/list) outputs via a recursive helper, not
just strings (sensitive-terraform-outputs, store-sensitivity).
- Gate raw secret exposure on `--mask=false` with a mandatory warning.
- Define per-provider sensitivity-metadata source (tags/labels) so
GetWithSensitivity is deterministic, with a fail-safe fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(prd): Fix broken Deployments PRD link in secrets-management
The link checker failed because the relative link
[Deployments PRD](docs/prd/deployments/problem-statement.md) resolved to
the doubled path docs/prd/docs/prd/deployments/problem-statement.md, and
that PRD only lives on the origin/deployments-prd branch (not in this
branch). Reference it as an inline-code path with a branch annotation,
matching the existing citation in the References section.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(prd): Address CodeRabbit review on secrets PRDs
Round-2 review feedback on PR #1911:
- secrets-management.md: document identity-resolution observability (INFO log
of the resolved identity + its pinning source, --dry-run surfacing, and an
--explain flag for the full 4-level precedence chain) in the identity notes
and Phase 2 deliverables, so the precedence cascade is debuggable and
auditable.
- secrets-management.md: add a language ("text") to unlanguaged fenced blocks
(MD040) flagged by markdownlint, including the precedence diagram.
- store-sensitivity.md: explain why the reserved sensitivity key separator
differs (colon for AWS Secrets Manager tags vs hyphen for Azure/GCP, since
GCP label keys disallow colons).
- store-sensitivity.md: clarify that the SSM String fallback to sensitive=false
is intentional — the Type field is authoritative and SecureString has always
been the mechanism for sensitive data.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(secrets): declarative secrets management with !secret, CRUD CLI, and masking
Implements the Secrets Management PRD end to end: a GitOps-friendly, multi-cloud
secrets workflow built on the existing store registry.
Stores (pkg/store):
- StoreConfig gains `secret: true` (subsystem membership) and `kind` (cloud/thing)
with legacy `type` mapping; `!store` against a secret store is an error.
- New DeletableStore/StatusStore/SecretAwareStore interfaces; SSM writes SecureString
when secret + adds Delete/Has.
- New backends: AWS Secrets Manager and HashiCorp Vault (KV v2).
- Registry refactored to a table-driven builder map.
Core (pkg/secrets): service, declaration registry, resolver, validator, kinds, and a
leaf providers subpackage (pkg/secrets/providers) with a store-adapter (track 1) and a
native SOPS provider (track 2). SOPS providers may be defined in atmos.yaml, globally
in a stack, or under a component.
!secret + masking (pkg/io, internal/exec):
- !secret wired into live YAML dispatch with path/default modifiers and auto-masking.
- Inspection commands (describe/list) resolve !secret to <MASKED> WITHOUT retrieval
(no credentials); value-producing paths always retrieve. Adds io.MaskingEnabled()
and recursive io.RegisterSecretValue().
- Sensitive Terraform outputs (sensitive=true) auto-register with the masker.
CLI (cmd/secret): init, set (add), get, delete (rm), list, pull, push, import, validate.
Stack processing: `secrets` is now a first-class inheritable component section that also
merges a global stack-level `secrets:` block into every component.
Docs + example: full Docusaurus docs (overview, 9 subcommands, config, !secret),
blog post (with embedded example), roadmap milestone, and examples/sops-secrets with a
runnable `atmos test` custom command proving the full lifecycle (age-encrypted, no
cloud creds).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(secrets): make `atmos test` custom command re-invoke the same atmos binary
Custom command steps called a bare `atmos`, which resolves via PATH to a possibly
stale or absent binary — so `./build/atmos test` ran the steps against a different
(old) atmos that lacked the `secret` command and `!secret` function.
- Inject ATMOS_CLI_PATH (os.Executable()) into custom command step environments so
steps can re-invoke the SAME running atmos binary.
- examples/sops-secrets `atmos test` now uses "${ATMOS_CLI_PATH:-atmos}" for all
atmos calls, so `./build/atmos test` passes end-to-end without atmos on PATH.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(secrets): render `secret list` via pkg/list; add --help usage examples
- `atmos secret list` now renders through the pkg/list pipeline (column/sort/
renderer) instead of a hand-rolled table: theme-aware on TTY, delimited when
piped, with a `--format` flag (table/json/yaml/csv/tsv).
- Add embedded `cmd/markdown/atmos_secret*_usage.md` usage examples so
`atmos secret [sub] --help` shows worked examples (parent + all 9 subcommands).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(io): make --mask=false reliably disable masking (init-ordering bug)
The global masker is created the first time any output is produced, which can
happen before CLI flags are parsed — at which point viper.GetBool("mask")
returns the flag's default (true), not --mask=false. Because Initialize() is
guarded by sync.Once, the later PersistentPreRun call was a no-op and the masker
kept masking enabled, so --mask=false had no effect (only ATMOS_MASK=false,
readable at early-init time, worked).
- Add Masker.SetEnabled and io.ReconcileMasking() which re-reads the resolved
masking config (flag -> env -> atmos.yaml -> default) and updates the global
masker after flags are parsed.
- Call io.ReconcileMasking() in root PersistentPreRun after Initialize().
- Read masker enabled state under the lock in Mask() (race-safe with SetEnabled).
- Regression tests for the reconcile behavior and SetEnabled.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(secrets): clarify import routing; silence experimental notice in example test; structured test steps
- `atmos secret import`: clarify (help + import.mdx + usage example) that each key is
written to its OWN declared backend (the store:/sops: of its secrets.vars entry); there
is no single destination and mixed backends are routed automatically.
- examples/sops-secrets `atmos test`: set ATMOS_EXPERIMENTAL=silence in the command env so
the experimental notice doesn't clutter the proof output; adopt structured custom-command
step types (style/toast) and re-invoke the same binary via $ATMOS_CLI_PATH.
- README: note that masking toggles via the --mask flag or ATMOS_MASK env var.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(secrets): add 1Password store backend with full CRUD
1Password is now a first-class Track-1 secret store (pkg/store/onepassword_*),
flowing through the existing secrets subsystem (store: declarations, !secret,
the atmos secret CLI) with no dispatch changes.
- Dual backend, auto-selected by 1Password's own env convention: native SDK
(OP_SERVICE_ACCOUNT_TOKEN, local dev) and Connect REST (OP_CONNECT_HOST/TOKEN,
CI/cloud). No `op` CLI required.
- Reference-based addressing: each declared secret carries a Go-templated
op://vault/item/field reference (atmos_stack/atmos_component + sprig), via a new
generic `reference` field on secret declarations (pkg/schema, pkg/secrets).
- Full CRUD: get/set/delete/list/validate. set upserts the field (creating the
item as an API Credential if missing); delete removes the field and the item
once empty. Idempotent deletes.
- `secret: true` is implied for type: onepassword (ApplySecretDefaults).
- Pinned onepassword-sdk-go to v0.3.1: v0.4.0+ fails to compile under
CGO_ENABLED=0 (desktop-integration guard); fix is on the SDK's main but untagged.
Includes ongoing secrets-management work on this branch (SOPS age_key_file,
secret exec/shell/env, auth, pkg/shell, docs, snapshots).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(auth): pin keyring storage contract before pkg/keyring extraction
Add characterization tests that lock the exact on-disk/in-keychain layout
(realm-scoped key strings, zalando service/account args, credential-envelope
JSON shape, realm isolation) so the upcoming extraction of the keyring
backends into a generic pkg/keyring package is provably non-breaking for
existing users' stored credentials.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor(keyring): extract generic pkg/keyring; refactor auth onto it
Add pkg/keyring: a credential-agnostic key->string-value secret store with
system (OS keychain), file (encrypted), memory, and noop backends. Backend
selection lives in keyring.New; fallback policy is left to callers so a
durable-write store can fail loudly instead of silently using noop.
Refactor pkg/auth/credentials to a single keyringCredentialStore over
pkg/keyring, keeping the credential-specific layer (realm-scoped keys, typed
credentialEnvelope, expiry) and identical on-disk/keychain layout. The
storage-contract characterization tests pass unchanged, proving no breakage
for existing stored credentials.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(secrets): add GitHub Actions secrets store, `secret get --raw`, and OIDC alignment diagnostics
GitHub Actions secrets store (kind: github/actions): a "native CI" backend that
writes/lists/deletes secrets via the GitHub API (sealed-box encryption with
golang.org/x/crypto/nacl/box) and reads values from the runner environment, gated
by CI detection (options.ci.enabled). Flows through the existing store-backed
secrets provider; registered as secret-by-default.
- `secret get --raw`/`-r`: print the raw value with no trailing newline (text only)
for piping (e.g. `| pbcopy`); mutually exclusive with `--format=json|env`.
- Enriched github/actions read errors naming the configured environment and the
`secrets.NAME` -> `env:` mapping requirement.
- New stdlib-only pkg/github/oidc leaf: decode GitHub Actions OIDC token claims
(repository/environment) to warn (never fail) on repo/environment mismatch
between the runner and the store config.
- Docs: stores.mdx (github/actions backend + OIDC alignment), secret get.mdx (--raw).
This checkpoint also includes coherent secrets-management work from parallel
sessions on the same branch: keychain store backend (pkg/store/keychain_store.go),
completion of the pkg/keyring extraction (drop perf.Track to avoid the
perf->store->keyring import cycle, with a matching lintroller exclusion), and SOPS
keyring-sourced age identities.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(secrets): SOPS in-process key generation + age key from a store
Close the SOPS onboarding cliff (Atmos could encrypt/decrypt but never
generate an age key) and let a vault source its key from a store.
Key generation (generic, registry-pattern):
- `atmos secret keygen [VAULT]` dispatches to any provider implementing the new
backend-agnostic `KeyGenerator` capability; backends that don't support it
report a friendly "not implemented" message. The SOPS (age) provider
generates an identity in-process (filippo.io/age) and records each half where
SOPS already looks — private to the vault's key source (its key file, the
sops-standard keys file, or a configured store), public recipient to a
`.sops.yaml` creation rule (yaml.Node merge preserving other rules). Sinks
mirror the vault's configured source; KMS/GPG kinds report not-supported.
- `atmos secret init` auto-offers keygen for any key-generating vault missing a key.
Age key from a store:
- `spec.age_key: { store, path, value }` (back-compat with bare-string `age_key`
and `age_key_file`). The provider reads — and keygen writes — the key via a
provider-owned store triple, so a `keychain` (or other) store can hold the
private key. Precedence: value > store > file > SOPS_AGE_KEY_FILE/SOPS_AGE_KEY.
Also: net-new branch lint cleanup (godot, err113, unparam, add-constant,
function-result-limit, hugeParam, file-length) across the secrets/keygen,
keychain store, GitHub Actions store, and terraform CI-hook code. Docs:
secret/keygen.mdx; secrets.mdx.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [autocommit] formatting fixes
* ci: raise build job timeout to 30m to avoid Windows cache-save cancellation
The `Build (windows)` job was being cancelled at exactly 15 minutes. The
build, version check, and artifact upload all succeed, but the post-job Go
cache save (tar + zstd of GOMODCACHE/GOCACHE via setup-go) takes ~6 minutes
on the Windows runner. With the larger dependency set, total job time crossed
the 15-minute limit and the run was cancelled (not a build/test failure).
Raise `timeout-minutes` for the build job from 15 to 30 to give headroom for
the build plus the slow Windows cache save.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(secrets): user-voice blog rewrite, PRD reconciliation, and roadmap/homepage features
Blog (2026-06-05-secrets-management.mdx):
- Rewrite in a user-facing voice (Vercel/Heroku framing, declared registry,
per-environment provisioning), no internal jargon or hyperbole.
- Add the identity+secrets differentiator (SSO/OIDC/roles), the
components-auto-inject vs `secret exec`/`shell` distinction, and a note that
this was a most-requested feature we deliberately took time to get right.
Example README (examples/sops-secrets): trim to an embed-friendly length and
reframe "End-to-end proof" as "Give it a spin".
PRD reconciliation (docs/prd/secrets-management.md): document features that
shipped after the original PRD — `atmos secret keygen` (in-process SOPS key
generation; age key to a file or a store), `atmos secret exec`/`shell`,
`secret get --raw`, the secret-by-default kinds concept, and the 1Password /
1Password Connect, keychain, and GitHub Actions backends.
Roadmap + homepage: feature Secrets Management. Replace the "Server-Side
Commits" featured card in roadmap.js and the "Smart Scaffolding" card on the
homepage with Secrets Management.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(utils): add !secret tag to TestAtmosYamlTagsMap_ContainsAllTags
The !secret YAML function (AtmosYamlFuncSecret) was registered in both
AtmosYamlTags and atmosYamlTagsMap (27 tags) but the test's expectedTags
list still enumerated only 26, failing the len() equality assertion and
breaking the Acceptance Tests (linux + macos) CI jobs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(secrets): cover stack-scoped vs component-scoped SOPS storage
Add two white-box tests for the sopsProvider that lock in how the
spec.file template determines secret scope:
- StackScopedSharing: a template without {{ .atmos_component }} makes all
components in a stack share one encrypted file, so a same-named key
written by a second component overwrites the first (stack-scoped).
- ComponentScopedIsolation: a template with {{ .atmos_component }} gives
each component its own file with independent values, and a sibling
component cannot read another's key (component-scoped, both directions).
Adds a newAgeProviderWithFile helper mirroring newAgeProvider; runs fully
in-process (no sops binary, no fixtures, no cloud creds).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(secrets): add secrets to stack JSON schema + section-coverage drift guard
The secrets feature shipped without updating the stack JSON Schema, so the
component-level `secrets:` block (additionalProperties:false) was rejected by
editors and not validated by `atmos validate stacks`. Model `secrets` in all
three manifest schema copies (website, tests/fixtures, embedded), mirroring how
`auth` is wired, and fix the pre-existing `auth` drift in the embedded schema
(was only on the terraform component manifest).
Add a drift guard so this can't silently recur:
- schema_section_coverage_test.go: AST-parses pkg/config/const.go, requires every
*SectionName constant to be classified as a manifest section (must be in the
schema) or non-manifest. Unclassified constants fail the build; pre-existing
gaps are tracked in an explicit knownSchemaGaps allowlist.
- schema_secrets_validation_test.go: validates real component/stack-level secrets
configs against the embedded schema and rejects malformed ones.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(secrets): enforce XOR backend (store|sops) in secret_declaration schema
The secret_declaration schema documented "exactly one backend reference
(store or sops) is set" but accepted both or neither, leaking invalid
configs past schema validation into runtime ambiguity (both silently
resolves to sops; neither only surfaces ErrNoBackend at use time).
Add a nested oneOf enforcing the XOR across all three synchronized
schema copies (embedded, website, test fixture), plus negative-path
test cases for the both-backends and no-backend rejections.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(secrets): skip Unix perm assertion on Windows in keys-file test
TestAppendIdentityToKeysFile asserted the keys file is mode 0o600, which fails
on Windows: Go does not honor Unix permission bits there and reports 0o666 for
any writable file. Guard the perm assertion with runtime.GOOS != "windows",
matching the existing convention in pkg/auth/cloud/gcp/files_test.go. The
implementation already creates the file 0o600; only the assertion was
platform-specific.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* ci(codecov): use PyPI CLI to bypass broken Keybase GPG key import
Codecov uploads started failing CI with "Could not verify signature.
Please contact Codecov if problem continues". The action wrapper fetches
Codecov's GPG public key from https://keybase.io/codecovsecurity/pgp_keys.asc
to verify the downloaded CLI binary, but that URL is currently returning
404 ("SELF-SIGNED PUBLIC KEY NOT FOUND"). The empty import yields
"gpg: no valid OpenPGP data found", so the signature check has no key and
the step exits 1 (amplified by our fail_ci_if_error: true). This is a live
Codecov/Keybase-side outage hitting many repos, not a change in ours.
Add use_pypi: true so the Codecov CLI is installed from PyPI instead of
cli.codecov.io, avoiding the broken keyserver fetch and restoring uploads.
Repros on both v5.5.4 and v6.0.1 (verification path unchanged), so a major
bump does not help. Safe to remove once Codecov restores the Keybase key.
Ref: codecov/codecov-action#1955
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(secrets): raise coverage to ~80%+ across secrets packages
Patch coverage on the secrets-management branch was failing the Codecov
gate. This brings the new secrets code over the 80% line:
cmd/secret 18.3% -> 80.4%
pkg/secrets 64.5% -> 90.8%
pkg/secrets/providers 79.1% -> 81.6%
pkg/store 55.7% -> 80.7%
cmd/secret: add a minimal, behavior-preserving DI seam (deps.go) so the
command handlers are unit-testable. A `secretService` interface (which
*secrets.Service satisfies structurally) plus overridable package-level
seam vars (loadServiceFn, loadServiceAndConfigFn, promptForValueFn,
confirmActionFn, runCommandFn, startShellFn) let tests inject a fake
without constructing real config/auth. No exported API or runtime
behavior changed; the handlers now call the seam vars. Added a
fakeSecretService and per-handler/helper tests.
pkg/secrets, pkg/secrets/providers, pkg/store: tests only. Added
validator_test, uncovered service methods (VaultsMissingKeys,
GenerateKeyForVault, DeleteAll, Reset, IsDeclared), resolver helpers,
and store/provider branches. Covered the SDK-wrapper clients via
same-package httptest/fake injection below the wrapper (real go-github
client against httptest with NaCl-box round-trip; fake connect.Client
for 1Password Connect).
Verified: build, vet, custom golangci-lint (--new-from-rev, 0 issues),
full affected-package suite with -shuffle=on, and the examples/sops-secrets
end-to-end `atmos test` proof.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(secrets): address CodeRabbit review nits on PR #1911
- import_test: use t.TempDir() path instead of hardcoded Unix path
- push_test: assert imported value (not just key) in JSON format test
- exec_handler_test: drop Unix binary tokens (env/false) for portable placeholder
- set_test: close pipe reader in cleanup to avoid FD leak
- resolver_helpers_test: add compile-time sentinel for schema.ConfigAndStacksInfo fields
- onepassword_client_test: split fake vault maps so title-fallback branch is actually exercised
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(secrets): raise patch coverage to ~89% across secrets/store/keyring
- Refactor 1Password sdkClient behind an opSDKAPI interface with a production
adapter; add an in-memory fake (sdkClient body 0 -> ~95%).
- Introduce accessible-mode huh testing for masked prompts: confirm prompts via
a plain reader, masked/password inputs via a creack/pty pair (Windows-skipped).
Covers cmd/secret/prompt.go and pkg/keyring newPasswordPrompt for the first time.
- Cover sops.go / sops_keygen.go error paths, oidc.go via httptest, and the AWS
Secrets Manager lazy-init/identity paths via the existing fake.
- Cover shared.go loaders (loadService/loadServiceAndConfig/buildAuthManager) with
a minimal in-temp-dir atmos fixture, plus runSecretKeygen via a config seam and
the missing parseScope component branch.
- Add package-level test seams (restored via t.Cleanup): runForm (cmd/secret),
stdinIsTerminal/runPasswordForm (keyring), loadKeygenConfig (keygen).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(secrets): add scope resolution, SOPS collision detection, and enumerate command
Adds scope tagging/derivation for `secrets:` declarations (scope.go),
SOPS-backed collision detection guarding the advanced spec.file template
path (collision.go), and a `secret enumerate` command (enumerate.go).
Includes supporting updates across cmd/secret, pkg/secrets, stack
processing, schemas, PRD, and website docs, plus expanded test coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(ui): left-align Yes/No buttons in confirmation prompts
huh's Confirm field defaults buttonAlignment to lipgloss.Center, which
is a per-field option rather than a theme property. Add a shared
uiutils.NewAtmosConfirm() constructor that bakes in left alignment and
route all confirmation prompt call sites through it so the Yes/No
buttons line up flush-left under the title and footer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(secrets): keep resolved secrets out of Terraform varfiles on disk
Resolved !secret values were serialized verbatim into the generated
*.terraform.tfvars.json varfile and left orphaned on disk in plaintext.
Secret-bearing variables are now detected via the masker (independent of
the --mask display flag) and routed to TF_VAR_<name> environment variables
at runtime instead of the varfile. Detection is value-based, so secrets
composed into larger strings or nested in maps/lists are caught too.
- pkg/terraform/tfvars: Partition(vars, isSecret) + SecretEnv(secret)
- pkg/io: Masker.ContainsSecret + global io.ContainsSecret
- exec: strip secrets from varfile, inject TF_VAR_* into ComponentEnvList
- terraform shell / generate varfile: opt-in --with-secrets (ATMOS_WITH_SECRETS)
- generate varfiles (batch): always strips secrets
- generate varfile: emit UI success with relative path; "(with secrets)" suffix
- tests, PRD, blog, and CLI docs
Bundles in-progress secrets coverage tests and stores/secrets doc edits.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(secrets): cover enumerate/list/scope paths to lift patch coverage
Raise patch coverage on the changed secrets files above the 80% Codecov
target by exercising the remaining 0%/low-coverage functions:
- enumerateSecretScopes: drive the real config + describe-stacks pipeline
in-process (0% -> 90.9%); covers the file Codecov flagged at 6.49%.
- runSecretList: single-scope, load-error, and enumerated paths (70% -> 85%).
- sopsProvider.SupportsScope: scope acceptance/rejection (0% -> 100%).
- checkScopeSupported: rejection branch via a fake provider, since no real
backend rejects a valid scope (66.7% -> 100%).
Tests only; no production changes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(secrets): cover store-hook writes to an encrypted keychain store
Add a credential-free, server-free end-to-end test proving an
after-terraform-apply store hook writes a Terraform output into an
encrypted-at-rest secrets store (the keychain `file` backend). The test
deploys a component, then asserts the hook's write round-trips through
the store API and that the on-disk keyring file is encrypted (the
plaintext never appears). Isolation is env-driven: XDG_DATA_HOME points
the keyring file at a temp dir and ATMOS_KEYRING_PASSWORD drives the
file backend non-interactively. Skips gracefully when no terraform
binary is available.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(secrets): guard non-encrypting secret stores, propagate custom-component env, add helmfile SOPS fixture
- store: reject `secret: true` on backends that cannot encrypt at rest
(Redis, Artifactory) via ErrSecretBackendNotEncrypted, so secrets are
never persisted in plaintext.
- cmd: export a custom component's resolved `env` section (including
resolved `!secret` values) into the step subprocess environment,
mirroring the built-in terraform/helmfile/packer providers.
- tests: add a self-contained SOPS-encrypted helmfile secrets scenario
fixture and helmfile_secrets integration test (fixture age key).
- docs/examples: document the secret-store encryption requirement,
custom-command env propagation, and component secrets usage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(ci): exclude flaky tldp.org link and use t.Setenv in keychain test
- link-check: exclude tldp.org from lychee — its exit-codes reference
returns intermittent 502 Bad Gateway in CI, matching the existing
exclusions for the gnu.org/openbsd/LSB exit-code references.
- test: replace os.Unsetenv("GITHUB_ACTIONS") with t.Setenv(..., "") in
the keychain integration test for test-scoped, auto-restored isolation
(CodeRabbit review on PR #1911).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(cmd): capture custom-component step env via Go-native helper, not platform binaries
Addresses CodeRabbit review on PR #1911 for the component-env-export test:
- Replace the platform-specific `env` / `cmd /c set` step command (and its
runtime.GOOS branch) with the test binary itself in env-dump helper mode:
TestMain writes the step subprocess environment to the file named by
_ATMOS_TEST_DUMP_ENV and exits, mirroring the existing _ATMOS_TEST_EXIT_ONE
helper. This satisfies the repo rule against platform-specific test binaries.
- Stop logging the full captured environment (which could leak CI tokens);
log only the asserted DEPLOY_REGION/APP_VERSION keys.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* [autocommit] formatting fixes
* feat(secrets): add scope: global and terraform-import-style secret adoption
One secret value shared by many consumers is a scope problem, not an
addressing problem. Complete the scope ladder (instance -> stack -> global):
a global declaration's coordinate omits both the stack and component
segments ({prefix}/{NAME}), so every importer of a shared catalog fragment
converges on the same backend path by construction. An explicit
`scope: global` survives the positional stamp at either declaration
position; SOPS declares no global support yet (file placement is
scope-keyed), gated by SupportsScope.
Migration from legacy `!store <store> <stack> <component> <key>` paths is a
one-shot CLI adoption instead of permanent config vocabulary:
`atmos secret import NAME --from-stack=... --from-component=...` copies the
value from its legacy coordinate into the declaration's computed coordinate
(like `terraform import`, the source is never modified or deleted).
--from-store defaults to the declaration's own store, --from-key to the
secret name, and the segments are raw path strings transcribed from the old
expression. `import` keeps its FILE mode unchanged and becomes the general
surface for bringing existing secrets under management.
Store key builders (SSM, ASM, GSM, keychain) now omit empty stack/component
segments instead of rejecting them, so scoped coordinates compose cleanly;
key-only validation remains. list/enumerate dedupe global rows to one `*/*`
row; set's success message names the shared blast radius for stack/global
scopes. Both manifest JSON schemas gain the scope enum (the website copy was
missing `scope` entirely).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(secrets): warn against committing SOPS age private keys in demo/fixture dirs
Add README.md to both the sops-secrets example and the secrets-helmfile test
fixture explaining that the committed age private key is a throwaway for
self-contained demos only, and that real projects must keep the key out of the
repo (SOPS_AGE_KEY_FILE / OS keychain) and commit only the encrypted file. Add a
matching "DEMO KEY ONLY" warning header to each keys.txt, and trim the unused
DATADOG_API_KEY/REDIS_URL entries from the example's dev.enc.yaml.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: atmos-pro[bot] <173522224+atmos-pro[bot]@users.noreply.github.com>
Co-authored-by: Andriy Knysh <aknysh@users.noreply.github.com>1 parent 6a90ec1 commit 7fc9c3e
308 files changed
Lines changed: 28199 additions & 1889 deletions
File tree
- .github/workflows
- cmd
- auth
- devcontainer
- markdown
- secret
- terraform
- generate
- docs/prd
- secrets-masking
- errors
- examples
- custom-components
- components/script/deploy-app
- stacks/catalog/script
- onepassword-secrets
- components/terraform/api
- stacks
- catalog
- deploy
- sops-secrets
- .atmos.d
- components/terraform/api
- secrets
- stacks
- catalog
- deploy
- internal
- exec
- tui/utils
- pkg
- auth
- credentials
- config
- datafetcher
- schema
- atmos/manifest
- stacks/stack-config
- flags
- function
- github/oidc
- io
- keyring
- runner/step
- schema
- secrets
- providers
- shell
- store
- terraform
- clean
- output
- tfvars
- utils
- test1
- tests
- fixtures
- scenarios
- hooks-keychain-test
- components/terraform/hook-and-store
- stacks
- secrets-helmfile
- components/helmfile/redis
- secrets
- stacks/deploy
- schemas/atmos/atmos-manifest/1.0
- snapshots
- tools/lintroller
- website
- blog
- docs
- cli
- commands
- secret
- terraform
- generate
- configuration
- functions
- template
- yaml
- stacks/components
- tutorials/sharing-state
- plugins/file-browser
- src
- data
- pages
- static/schemas/atmos/atmos-manifest/1.0
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
48 | 51 | | |
49 | 52 | | |
50 | 53 | | |
| |||
284 | 287 | | |
285 | 288 | | |
286 | 289 | | |
287 | | - | |
288 | | - | |
289 | | - | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
290 | 297 | | |
291 | 298 | | |
292 | 299 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
67 | 71 | | |
68 | 72 | | |
69 | 73 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
44 | 52 | | |
45 | 53 | | |
46 | 54 | | |
| |||
169 | 177 | | |
170 | 178 | | |
171 | 179 | | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
172 | 184 | | |
173 | 185 | | |
174 | 186 | | |
| |||
273 | 285 | | |
274 | 286 | | |
275 | 287 | | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
276 | 292 | | |
277 | 293 | | |
278 | 294 | | |
| |||
281 | 297 | | |
282 | 298 | | |
283 | 299 | | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
284 | 304 | | |
285 | 305 | | |
286 | 306 | | |
| |||
321 | 341 | | |
322 | 342 | | |
323 | 343 | | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
324 | 348 | | |
325 | 349 | | |
326 | 350 | | |
| |||
409 | 433 | | |
410 | 434 | | |
411 | 435 | | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
412 | 440 | | |
413 | 441 | | |
414 | 442 | | |
| |||
461 | 489 | | |
462 | 490 | | |
463 | 491 | | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
464 | 504 | | |
465 | 505 | | |
466 | 506 | | |
| |||
473 | 513 | | |
474 | 514 | | |
475 | 515 | | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
476 | 524 | | |
477 | 525 | | |
478 | 526 | | |
| |||
541 | 589 | | |
542 | 590 | | |
543 | 591 | | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
544 | 596 | | |
545 | 597 | | |
546 | 598 | | |
| |||
634 | 686 | | |
635 | 687 | | |
636 | 688 | | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
637 | 701 | | |
638 | 702 | | |
639 | 703 | | |
| |||
694 | 758 | | |
695 | 759 | | |
696 | 760 | | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
697 | 765 | | |
698 | 766 | | |
699 | 767 | | |
| |||
762 | 830 | | |
763 | 831 | | |
764 | 832 | | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
765 | 841 | | |
766 | 842 | | |
767 | 843 | | |
| |||
818 | 894 | | |
819 | 895 | | |
820 | 896 | | |
| 897 | + | |
| 898 | + | |
| 899 | + | |
| 900 | + | |
821 | 901 | | |
822 | 902 | | |
823 | 903 | | |
| |||
947 | 1027 | | |
948 | 1028 | | |
949 | 1029 | | |
| 1030 | + | |
| 1031 | + | |
| 1032 | + | |
| 1033 | + | |
| 1034 | + | |
| 1035 | + | |
| 1036 | + | |
| 1037 | + | |
950 | 1038 | | |
951 | 1039 | | |
952 | 1040 | | |
| |||
1072 | 1160 | | |
1073 | 1161 | | |
1074 | 1162 | | |
| 1163 | + | |
| 1164 | + | |
| 1165 | + | |
| 1166 | + | |
| 1167 | + | |
| 1168 | + | |
| 1169 | + | |
| 1170 | + | |
1075 | 1171 | | |
1076 | 1172 | | |
1077 | 1173 | | |
| |||
1100 | 1196 | | |
1101 | 1197 | | |
1102 | 1198 | | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | + | |
1103 | 1203 | | |
1104 | 1204 | | |
1105 | 1205 | | |
| |||
1200 | 1300 | | |
1201 | 1301 | | |
1202 | 1302 | | |
| 1303 | + | |
| 1304 | + | |
| 1305 | + | |
| 1306 | + | |
1203 | 1307 | | |
1204 | 1308 | | |
1205 | 1309 | | |
| |||
1296 | 1400 | | |
1297 | 1401 | | |
1298 | 1402 | | |
| 1403 | + | |
| 1404 | + | |
| 1405 | + | |
| 1406 | + | |
1299 | 1407 | | |
1300 | 1408 | | |
1301 | 1409 | | |
| |||
1408 | 1516 | | |
1409 | 1517 | | |
1410 | 1518 | | |
| 1519 | + | |
| 1520 | + | |
| 1521 | + | |
| 1522 | + | |
1411 | 1523 | | |
1412 | 1524 | | |
1413 | 1525 | | |
| |||
1494 | 1606 | | |
1495 | 1607 | | |
1496 | 1608 | | |
1497 | | - | |
| 1609 | + | |
1498 | 1610 | | |
1499 | 1611 | | |
1500 | 1612 | | |
| |||
1556 | 1668 | | |
1557 | 1669 | | |
1558 | 1670 | | |
| 1671 | + | |
| 1672 | + | |
| 1673 | + | |
| 1674 | + | |
1559 | 1675 | | |
1560 | 1676 | | |
1561 | 1677 | | |
| |||
1752 | 1868 | | |
1753 | 1869 | | |
1754 | 1870 | | |
| 1871 | + | |
| 1872 | + | |
| 1873 | + | |
| 1874 | + | |
1755 | 1875 | | |
1756 | 1876 | | |
1757 | 1877 | | |
| |||
0 commit comments