- Subprocesses (
gpg,gh,ssh,msmtp,sendmail) now run withENVSTASH_PASSWORDandENVSTASH_KEY_FILEscrubbed from the child env - SSH destinations are validated (reject leading
-, control characters) and spawned with--argv separator to prevent option injection - Email recipients are validated (reject leading
-, CRLF, control characters, malformed addresses) and spawned with--argv separator to prevent argv/header injection send --to gistnow usestempfile::NamedTempFile(randomized name, auto-cleanup) instead of a predictable pathcheckout(apply) refuses to write through a symlink and writes with mode 0600 on unixsaverefuses to read through a symlinkdumprefuses to write through a pre-planted symlink at the output path- Enforce 0600 on existing files during
apply/dump(not just on new files) —OpenOptions::modeonly applies at creation, so a pre-existing world-readable target would otherwise retain its old mode execalso stripsENVSTASH_KEY_FILEfrom the child environment (not justENVSTASH_PASSWORD)receive --from <gist>now invokesgh gist viewwith--separator to block option injection on the gist id- Base64 auto-decode requires decoded bytes to start with
EVPW(transport v1) or-----BEGIN PGP; opaque bytes pass through unchanged - GPG/SSH/email child-stdin is now explicitly closed before
wait, eliminating a deadlock class and surfacing "stdin unavailable" errors
queries::get_configsfor batched config lookups (one SELECT instead of N)crypto::aes::build_cipher,encrypt_with_cipher,decrypt_with_cipherfor reusing an AES cipher across a batch of entries
- Breaking:
--password <literal>removed fromsend,receive,dump,load— use--password-file <path>(mode 0600 required on unix),ENVSTASH_PASSWORD, or the interactive prompt - Breaking:
[send.headers]is now a per-host map[send.headers."host"]— use"*"for a global fallback; auth headers are automatically stripped forhttp://targets unless the host islocalhost/127.0.0.1 initerrors instead of silently writing empty string when--key-filepath is not valid UTF-8- Bulk operations (
save,receive,load,rm --branch,rm --all) now run inside a single SQLite transaction — faster on large batches, and leaves the database consistent if a write is interrupted save/get_save_entriesbuild the AES cipher once per save and reuse it across every entry, trimming per-entry key-schedule overheadload_encryption_keyfetchesencryption_modeandkey_filein a single SELECTlog(history) no longer decrypts the same save twice in adjacent iterationslsmemoizes the on-disk.envhash per file path instead of re-reading the file per rowdisk_content_hashnow returnsResult<Option<String>>— IO and parse errors propagate onsave/send; onlsthey are logged as a single warning per path and treated as "unknown" so the listing stays non-fatal
- Release profile uses
lto = "thin",codegen-units = 1,strip = "symbols",panic = "abort"— binary drops from ~8.2 MB to ~6.3 MB
- Tab completion for file paths in
save,checkout --dest,receive,dump,load - Tab completion for file paths and version hashes in
diff
- Clippy fixes across the codebase
- Bump
- Encryption keys are zeroed from memory after use
- Env output validates variable names to prevent shell injection
- Graceful error handling when HOME is not set
mancommand with usage examples and detailed guide- Context-aware help: bare
envstashhides Setup when store is initialized, hides completion tip when already configured
- Bare
envstashnow shows dynamic help instead of clap's static help
- Network-dependent tests (paste, gist) skipped in CI
- Shell tab-completion for version hashes (bash, zsh, fish)
lsandlognow show content hashes as primary version identifiers- Numeric indices still work as a convenient shortcut
- CLI commands renamed to follow Unix conventions:
share→sendimport→receive
- Config section
[share]→[send]
- CLI commands renamed to follow Unix conventions:
list→lsdelete→rmhistory→logapply→checkout(co)
- Old command names still work as hidden aliases
initmoved to its own "Setup" section in--help
init --encrypt gpg: lists available keys, requires explicit--recipientselectionshare --encrypt: requires explicit--recipient(supports multiple recipients)dump --encrypt: defaults to the store's GPG recipient instead of git signing key
share --to: upload to 0x0.st, or--to <url>for a custom paste serviceshare --to gist: create a GitHub Gist via gh CLIshare --to email:<addr>: send via msmtp or sendmailshare --to ssh://user@host: pipe to remote envstash importimport --from <url>: fetch exports from URLs, gist URLs, or SSHshare --public: create public gists (default: secret)- Config file (
~/.config/envstash/config.toml) for setting the default--totarget