In this episode we'll secure credentials: keeping passwords and tokens out of shell history, process lists, and logs, making use of interactive prompts, netrc, and environment variables, plus applying best practices for token rotation and request auditing.

In episode 12, you secured the path — TLS ensures requests reach the right server without being readable along the way. But there's a gap that encryption can't close: credentials leaking on your own side. Passwords written on the command line, tokens stored in shell history, or secrets printed in CI logs — all of them render perfect TLS useless. Like a safe with a steel door but the key hanging on the door handle.
Episode 13 is about secrets handling: where secrets must not go, where they must go, and what habits keep credentials alive in the right place.
Before learning the solutions, you must know your enemy. There are four classic places where credentials leak:
~/.bash_history or ~/.zsh_history records every typed command, including passwords stuck in arguments.ps while the process runs. Not just shell history..curlrc or other files that make it into git are a time bomb for the whole team.# DON'T do this — the password shows up in ps and shell history
curl -u arman:sandirahasia https://api.example.com/meThe problem isn't curl — curl only does what's asked. The problem is putting secrets in a place designed to be public. The solution is always the same: separate credentials from the command.
-u Without a PasswordThe simplest way to keep a password off the command line is to not write it. If you give -u a username without a colon and password, curl asks for it interactively with a prompt that doesn't show the keystrokes:
curl -u arman https://api.example.com/meEnter host password for user 'arman':Type the password, and the command will never be recorded in history nor appear in ps. This pattern is perfect for manual sessions on shared machines — but for automated scripts, there's a more appropriate way.
Tip
Make sure this prompt isn't triggered mid-way through an unattended script — if stdin isn't a terminal, curl will fail with an error rather than hang. For full automation, use netrc or the environment variables below, not an interactive prompt.
.netrc File and --netrcFor password-based automation, curl has a classic mechanism: the .netrc file — a file storing host-credential pairs, read by curl only when asked. Its structure is simple:
machine api.example.com
login arman
password sandirahasia
machine staging.example.com
login arman
password sandi-lainTo use it, call --netrc (reads the default ~/.netrc) or --netrc-file for a specific file:
curl --netrc-file ~/.api-netrc https://api.example.com/meIts advantages: no credentials on the command line, curl picks the right pair based on host, and the file can be rotated without touching scripts. Netrc is the answer to "cron scripts that need a password without human interaction".
Important
The .netrc file stores passwords in plain text. It must be locked down: chmod 600 ~/.api-netrc so only its owner can read it. Never commit this file to git — and if possible, build its contents from a secret manager at deploy time, rather than storing it as a static file.
For tokens — not passwords — the most common approach in the modern world is the environment variable. Variables never appear in ps (only arguments are visible), and values can be injected from outside without touching code:
curl --oauth2-bearer "$API_TOKEN" https://api.example.com/mecurl -u "$API_USER:$API_PASS" https://api.example.com/meIn CI pipelines, these values come from the platform's secret store — GitHub Actions puts them in Settings > Secrets, GitLab CI in Settings > CI/CD > Variables. Your scripts stay clean; secrets flow through the environment. For stricter environments, level up to a secret manager like HashiCorp Vault or AWS Secrets Manager, which can rotate keys and log access — called at runtime, not copied into files.
Warning
Watch out for one trap: writing curl -u user:pass inside a script still puts the password in the process arguments — safe from history, but still visible to ps. Always read from the environment: curl -u "$USER:$PASS" not curl -u user:rahasia. Secrets may only live in the environment and tightly-permissioned files.
Tokens are the new credential of the API world, and tokens most often leak through the same carelessness: pasting them directly into a command. A command like curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." will be recorded in full in shell history — and anyone reading your history (or a leaked .bash_history file) gets full access.
The right habits:
export API_TOKEN=eyJhbGciOiJIUzI1NiJ9... then use $API_TOKEN in the command.--oauth2-bearer (from episode 10) so the header is assembled automatically from the variable.# Token from the environment, not typed directly
curl --oauth2-bearer "$API_TOKEN" https://api.example.com/me--trace Only for DebuggingIn episodes 9 and 18 you'll meet --trace and --trace-ascii — byte-by-byte recordings that are very useful when debugging. But remember: a trace records everything, including the Authorization header, cookies, and body. A trace file is a bundle of secrets ready for anyone to read.
# The trace captures the Authorization header and cookies — don't commit it
curl --trace-ascii trace.txt --oauth2-bearer "$API_TOKEN" \
https://api.example.com/meA healthy policy: enable --trace only when a problem really needs dissecting, delete the file immediately when done, and never put traces in git or logs readable by others. If you must share a trace for a bug report, redact the tokens and cookies first — or just use the more concise -v for light cases.
All the techniques above are tools; here are the policies that bind them into one way of working:
Warning
A secret that has ever leaked is considered leaked forever. Once a token has been written to history, a log, or a PR, don't try to delete it quietly — rotate it immediately. Rotation is a normal part of operations, not a sign of failure.
Episode 13 equips you with secrets management discipline: recognizing the four leak locations (shell history, process list, CI logs, committed files), using interactive prompts with -u without a password, automating authentication with .netrc and --netrc-file, separating tokens into environment variables and secret managers, limiting --trace to debugging only, and applying token rotation and least-privilege practices.
The core thing to remember: secrets aren't about being invisible, but about never being placed in the wrong place. Secure transport is useless if the key is hanging on the front door. curl's defaults are already safe; your job is not to break them for typing convenience.
In the next episode 14, we'll move from security to speed: modern protocols — HTTP/2 and HTTP/3 with their multiplexing, plus WebSocket for real-time communication. See you!