Background
stackctl must support the existing local-stack SOPS + age workflow, but sops and age must remain optional runtime dependencies. The base CLI must work without them. They become mandatory only when a stackctl secrets ... command is used or when stackctl doctor --check-secrets is requested.
Goals
Implement:
stackctl secrets encrypt
stackctl secrets decrypt
stackctl secrets deploy
stackctl secrets clean
stackctl secrets doctor
- optional
stackctl secrets rotate-keys
Dependency behavior
Before any secrets operation mutates files or deploys stacks, check for required commands:
If either is missing, exit gracefully with an actionable message:
ERROR: stackctl secrets decrypt requires sops and age.
Missing:
- sops
- age
Install:
macOS: brew install sops age
Linux: install sops and age from your package manager or upstream releases
Then verify:
stackctl doctor --check-secrets
No partial secret workflow should continue when required tools are missing.
Required behavior
- Discover services from configured
.env.example locations.
- Resolve default encrypted file names from config, e.g.
.env.enc.
- Resolve profile encrypted file names, e.g.
.env.dev.enc or .env.prod.enc.
- Support explicit service target by basename or repo-relative path.
- Encrypt
.env or profile-selected plaintext input to encrypted dotenv output.
- Decrypt encrypted dotenv output into active
.env using restrictive file permissions.
- Use temp file + atomic move for decrypted outputs.
- For
secrets deploy:
- decrypt target env files
- determine affected stacks
- generate/render/deploy affected stacks
- remove plaintext
.env files created by this run
- For
secrets clean:
- remove plaintext
.env files only when corresponding encrypted files exist
- use
shred -u when available
- fall back to
rm -f with a warning when shred is unavailable
CLI shape
stackctl secrets encrypt
stackctl secrets encrypt postgres
stackctl secrets encrypt --profile dev
stackctl secrets decrypt
stackctl secrets decrypt postgres
stackctl secrets decrypt --profile prod
stackctl secrets deploy
stackctl secrets deploy postgres
stackctl secrets deploy --profile prod
stackctl secrets clean
stackctl secrets doctor
Plaintext safety
.env is always treated as plaintext and should be gitignored.
.env.<profile> is allowed only when env.allowPlaintextProfiles is true.
- Warn when plaintext profile files appear to contain sensitive keys such as
SECRET, TOKEN, PASSWORD, PRIVATE, KEY, CLIENT_SECRET, DATABASE_URL, MONGO_URI, or REDIS_URL.
Acceptance criteria
- Secrets commands fail cleanly when
sops or age is missing.
- Base non-secret commands work when
sops and age are missing.
- Encrypt/decrypt command construction is unit tested through a fake process runner.
- Temp-file and atomic-move behavior is tested.
- Cleanup behavior is tested with both
shred available and unavailable.
- Profile-specific encrypted file resolution is tested.
secrets deploy --dry-run prints decrypt/render/deploy/cleanup plan without mutation.
Non-goals
- Do not implement secret editing UI in this issue.
- Do not commit plaintext env files.
- Do not make
sops or age a hard install dependency for the entire CLI.
Background
stackctlmust support the existing local-stack SOPS + age workflow, butsopsandagemust remain optional runtime dependencies. The base CLI must work without them. They become mandatory only when astackctl secrets ...command is used or whenstackctl doctor --check-secretsis requested.Goals
Implement:
stackctl secrets encryptstackctl secrets decryptstackctl secrets deploystackctl secrets cleanstackctl secrets doctorstackctl secrets rotate-keysDependency behavior
Before any secrets operation mutates files or deploys stacks, check for required commands:
sopsageIf either is missing, exit gracefully with an actionable message:
No partial secret workflow should continue when required tools are missing.
Required behavior
.env.examplelocations..env.enc..env.dev.encor.env.prod.enc..envor profile-selected plaintext input to encrypted dotenv output..envusing restrictive file permissions.secrets deploy:.envfiles created by this runsecrets clean:.envfiles only when corresponding encrypted files existshred -uwhen availablerm -fwith a warning whenshredis unavailableCLI shape
Plaintext safety
.envis always treated as plaintext and should be gitignored..env.<profile>is allowed only whenenv.allowPlaintextProfilesis true.SECRET,TOKEN,PASSWORD,PRIVATE,KEY,CLIENT_SECRET,DATABASE_URL,MONGO_URI, orREDIS_URL.Acceptance criteria
sopsorageis missing.sopsandageare missing.shredavailable and unavailable.secrets deploy --dry-runprints decrypt/render/deploy/cleanup plan without mutation.Non-goals
sopsoragea hard install dependency for the entire CLI.