Skip to content

Add Treasury run command - do not persist secrets on disk - #304

Merged
jadrol merged 2 commits into
masterfrom
feature/INFRA-1250/run-command
Aug 13, 2026
Merged

Add Treasury run command - do not persist secrets on disk#304
jadrol merged 2 commits into
masterfrom
feature/INFRA-1250/run-command

Conversation

@jadrol

@jadrol jadrol commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Requestor/Issue: @jadrol
Risk (low/med/high): low
Tested (yes/no): yes
Description/Why:

treasury run [flags] COMMAND [ARGS...] executes a command with secrets loaded into its
environment, in memory only, never written to disk. Same idea as op run from 1Password,
with our treasury paths and the template syntax we already use in ah-config templates:

{{ export "development/mobile-app-gateway/" }}   # whole path, named after the last key part
AUTH_API_PASSWORD={{ read "development/auth/PASSWORD" }}
RAILS_ENV=development                            # plain value, no secret store involved

The file is .env.treasury by default, --env-file points elsewhere, --profile picks an
AWS profile when AWS_PROFILE is not the one you want. Treasury stays out of the way of the
command: stdio is inherited, signals are forwarded, and the command's exit code becomes its own.

To keep it quick on a big path, secrets are read in as few calls as possible — one call per
exported path, all single keys batched into one. That needed GetParameters support in the SSM
backend and a Keys variant of GetObjects, so nothing outside the listed keys is decrypted.

@jadrol
jadrol requested a review from a team as a code owner August 12, 2026 11:50
@jadrol
jadrol requested review from krzwiatrzyk-lgd and lgd-michallasisz and a lite review from Copilot August 12, 2026 11:50
@jadrol
jadrol force-pushed the feature/INFRA-1250/run-command branch from 5c88142 to 95b43ed Compare August 12, 2026 11:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new treasury run CLI command that materializes secrets into a subprocess environment (in-memory) based on a .env.treasury template file, alongside backend support for efficiently fetching only specific keys (not whole prefixes) where possible.

Changes:

  • Introduce treasury run [flags] COMMAND [ARGS...] with env-file parsing, signal forwarding, and exit-code passthrough.
  • Add env-file resolution logic (read/export) plus unit and Bats coverage for parsing and runtime behavior.
  • Extend backend APIs to support “keys-only” retrieval (SSM batched GetParameters, S3 per-key reads) and bump version.

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
version/version.go Bumps Treasury version to v0.15.0.
types/types.go Extends GetObjectsInput with Keys for keys-only retrieval.
cmd/run.go Adds the new run command implementation (env loading, exec, signals, exit codes).
client/read.go Adds ReadKeys to fetch specific keys via GetObjects(Keys=...).
client/envfile.go Implements .env.treasury parsing and two-pass secret collection/rendering.
client/envfile_test.go Adds unit tests for env-file parsing and fetch behavior.
backend/ssm/aws.go Extends SSM client interface with GetParameters.
backend/ssm/ssm.go Implements keys-only retrieval via batched GetParameters; reuses limit constant.
backend/ssm/getparameters_test.go Adds tests for batching and missing-key reporting in SSM keys-only reads.
backend/s3/s3.go Implements keys-only reads by fetching specified objects individually.
test/backend/test.go Updates mock backend to support GetObjects(Keys=...) behavior.
test/ssm/test.go Updates mock SSM client to support GetParameters.
test/resources/bats.env.treasury Adds a sample env file for Bats run tests.
test/bats/tests.bats Adds Bats coverage for treasury run behavior (env, comments, exit codes, signals).
README.md Documents treasury run usage and env-file format.
.gitignore Ignores .env.treasury by default.
Suppressed comments (1)

README.md:246

  • Typo in the README heading: “Teamplate usage” should be “Template usage” so the generated anchor matches links.
### Teamplate usage

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread client/envfile.go Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Copilot AI review requested due to automatic review settings August 12, 2026 11:56
@jadrol
jadrol force-pushed the feature/INFRA-1250/run-command branch from 95b43ed to 08684ea Compare August 12, 2026 12:00

This comment was marked as outdated.

Copilot AI review requested due to automatic review settings August 12, 2026 12:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (1)

cmd/run.go:99

  • --profile is currently only used to configure Treasury's AWS client; the executed command still inherits the caller's AWS_PROFILE (because command.Env is built from os.Environ() plus the env-file entries). This contradicts the help/README text implying --profile selects the profile for the overall run. Consider appending AWS_PROFILE=<profile> to the environment passed to the subprocess (and note that when using the S3 backend, profile selection may still require additional work because backend/s3.New loads its own ambient config).
		return err
	}

	return execute(cmd.Context(), args, environment)

@lgd-michallasisz

Copy link
Copy Markdown
Contributor

GetObject never closes resp.Body, and this loop calls it once per key. treasury run then waits on the child, so those connections stay open for the whole command, not just the fetch. Is that a problem for this PR?
If not, approved.

@jadrol

jadrol commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

GetObject never closes resp.Body, and this loop calls it once per key. treasury run then waits on the child, so those connections stay open for the whole command, not just the fetch. Is that a problem for this PR? If not, approved.

I haven't added any GetObject method, coud you pinpoint me to line in my diff? I cannot find any applicable place in my code for this suggestion

@lgd-michallasisz

@lgd-michallasisz

Copy link
Copy Markdown
Contributor

@jadrol sorry for the confussion, the thing that I mentioned is not introduced by you, you are just using it.
You are using GetObject here , which is defined here and Resp.Body is never closed.

Copilot AI review requested due to automatic review settings August 13, 2026 08:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (3)

cmd/run.go:38

  • Forwarding SIGINT/SIGQUIT causes the child to receive terminal-generated signals twice (once from the shared process group, once from this forwarding loop). This can lead to double-interrupt behavior in wrapped commands. Consider forwarding only signals that are typically sent directly to the parent process (e.g. SIGTERM from a supervisor).
var forwardedSignals = []os.Signal{
	syscall.SIGINT,
	syscall.SIGTERM,
	syscall.SIGQUIT,
	syscall.SIGHUP,
	syscall.SIGUSR1,
	syscall.SIGUSR2,
}

cmd/run.go:136

  • This comment currently states Ctrl-C arrives twice, which will no longer be true once SIGINT/SIGQUIT are removed from forwardedSignals. Update the comment so it matches the actual forwarding behavior and rationale (forward only signals directed at treasury, avoid duplicating terminal-generated signals).
// forwardSignals relays the signals treasury receives to the command it runs,
// so that a SIGTERM from a supervisor reaches the process that does the work.
// The command shares the process group with treasury, so signals coming from
// the terminal reach it on their own, a Ctrl-C simply arrives twice.
// The returned function stops the forwarding.

client/envfile.go:197

  • export() currently sorts all fetched secret keys for every export directive, then filters by prefix. For large environments (many secrets across multiple paths), this adds unnecessary O(N log N) work per export. Sorting only the keys under the requested prefix keeps deterministic output while reducing work.
	prefix = withSlash(prefix)
	for _, key := range slices.Sorted(maps.Keys(e.secrets)) {
		if strings.HasPrefix(key, prefix) {
			e.add(path.Base(key), e.secrets[key])
		}
	}

@jadrol
jadrol merged commit f524030 into master Aug 13, 2026
7 checks passed
@jadrol
jadrol deleted the feature/INFRA-1250/run-command branch August 13, 2026 09:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants