feat: read login password and key from env or stdin#3498
Conversation
Adds ATUIN_PASSWORD and ATUIN_KEY environment variable fallbacks and a new --password-stdin flag to `atuin login`, with the same env+stdin treatment for `atuin register`. Addresses the unattended-login gap from atuinsh#183: --password is visible in the host's process list, which is unsafe in shared environments and impractical for CI / config management (Ansible, etc.) workflows. The 2021 fix (atuinsh#185) only added an interactive prompt, which doesn't help non-interactive callers. Precedence: --password flag > --password-stdin > env var > interactive prompt. --password and --password-stdin are mutually exclusive (enforced by clap). Existing flag-only and interactive flows are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR adds non-interactive password and key sources for login and registration. The main changes are:
Confidence Score: 3/5These issues should be fixed before merging.
Focus on Important Files Changed
Reviews (1): Last reviewed commit: "feat: read login password and key from e..." | Re-trigger Greptile |
| let key = self | ||
| .key | ||
| .clone() | ||
| .or_else(|| env_secret(KEY_ENV)) | ||
| .unwrap_or_else(|| read_user_input("encryption key [blank to use existing key file]")); |
There was a problem hiding this comment.
ATUIN_KEY is already used by Atuin's config environment loader as the key_path override, and doctor.rs reports that same variable as the key path. This new fallback now treats the same value as raw encryption key material. When a user has ATUIN_KEY=/custom/path/to/key to point Atuin at a key file, atuin login will try to parse /custom/path/to/key as the encryption key and fail instead of using the existing key file.
| SyncAuth::NotLoggedIn { .. } => {} | ||
| } | ||
|
|
||
| let resolved_password = self.resolve_password()?; |
There was a problem hiding this comment.
resolve_password() reads all of stdin when --password-stdin is set, but this happens before Hub registration checks whether username, email, and password are all present for the headless path. For example, printf %s "$PASSWORD" | atuin register --password-stdin --email a@b.com consumes the piped secret and then falls back to browser registration because username is missing. With terminal stdin, this can also block before the fallback message is reached.
* Rename ATUIN_KEY -> ATUIN_ENCRYPTION_KEY. The `atuin doctor` output already labels the `key_path` setting with "ATUIN_KEY", so reusing that name for a different concept (raw key contents) would have been user-facing confusion even though figment's prefix wiring does not actually collide (the real path override would be ATUIN_KEY_PATH). * Defer register's stdin/env password read until the headless path is reachable, so `printf %s "$PASSWORD" | atuin register --password-stdin --email a@b.com` (no username) no longer consumes the piped secret before falling through to OAuth. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks for the review (and to Greptile for the catches). Pushed c40b229 addressing both P1s:
Tests, clippy, fmt all clean locally. |
Resolves the unattended-login concern from #183:
atuin login -p <password>exposes the password in the host's process list, which is unsafe in shared environments and impractical for non-interactive callers (CI, config management like Ansible, etc.). The 2021 fix (#185) added an interactive prompt, but that path is unavailable when there's no TTY.This PR adds two non-interactive sources:
Precedence:
--passwordflag →--password-stdin→ATUIN_PASSWORDenv → interactive prompt. Key resolution follows the same shape minus stdin (--key→ATUIN_ENCRYPTION_KEYenv → interactive prompt). Same env+stdin treatment applied toatuin register. Existing flag-only and interactive flows are unchanged.ATUIN_ENCRYPTION_KEYis named to avoid confusion with theATUIN_KEYlabel thatatuin doctoralready prints forkey_path(the key file location). The variable in this PR holds key contents; they're different concepts.For
atuin register, stdin/env resolution is deferred until the headless path is reachable (both--usernameand--emailprovided), so a piped secret isn't silently consumed before falling through to the OAuth flow.Local validation
cargo test -p atuin --bin atuin— 220 passing (214 existing + 6 new inaccount::login::tests)cargo clippy -p atuin -- -D warnings -D clippy::redundant_clone— cleancargo fmt --check— clean(Note:
cargo clippy -p atuin --testsreports 10 errors onmainbefore my changes — they're insearch/interactive.rsandkeybindings/key.rs, unrelated to this PR.)Notes
AI-assisted (Claude) and human-reviewed/tested before submission. Implementation reads the existing
or_user_input/read_user_passwordpattern and adds parallel helpers (env_secret,read_secret_from_stdin) in the samepub(super)style soregister.rscan reuse them.Checks