Every time I needed to inspect a JWT, I ended up doing the same thing: paste it into a website I half-trust, squint at the payload, then close the tab feeling vaguely guilty. I wanted a local tool, scriptable, that could both decode tokens and mint them for testing. So I wrote jwt-cli.

What it does

jwt-cli is a small Go binary that encodes and decodes JWT tokens from the command line. Nothing leaves your machine, and it covers every algorithm I actually run into:

  • HMAC: HS256, HS384, HS512
  • RSA: RS256, RS384, RS512
  • ECDSA: ES256, ES384, ES512

That’s it. No web UI, no telemetry, no online service. Just a binary you can pipe into and out of.

Install

The easiest path on macOS or Linux is Homebrew:

brew tap sgaunet/homebrew-tools
brew install sgaunet/tools/jwt-cli

Or grab a binary from the releases page and drop it in /usr/local/bin. There’s also a Docker image (sgaunet/jwt-cli:latest) which is mostly useful as a COPY --from= source in your own Dockerfiles.

Encoding a token

The simplest case — sign a payload with an HMAC secret:

jwt-cli encode hs512 \
    --payload '{ "email": "myemail@me.com" }' \
    --secret  "myAwesomeSecret"

That spits out the familiar three-segment token. Change hs512 to rs256 and pass --private-key to sign with an RSA key instead. The subcommand selects the algorithm — there’s no clever auto-detection, which I consider a feature.

Decoding and verifying

Decoding is the inverse, and the CLI verifies the signature for you:

# Wrong secret? You get told.
jwt-cli decode hs512 --secret "wrong secret" --token "$TOKEN"
# signature is invalid

# Right secret? You get the JSON payload.
jwt-cli decode hs512 --secret "myAwesomeSecret" --token "$TOKEN"
# {
#   "email": "myemail@me.com"
# }

The exit code reflects the verification result, which is what you want when you’re shoving this into a CI step.

Generating keys

For RSA and ECDSA you’ll need a keypair. Rather than make you remember the openssl incantations, jwt-cli genkeys prints them for the algorithm you’re targeting. Here’s the ES256 example straight from the README:

openssl ecparam -genkey -name prime256v1 -noout -out ES256-private.pem
openssl ec -in ES256-private.pem -pubout -out ES256-public.pem

Run jwt-cli genkeys and it gives you the equivalent for every supported algorithm. Small thing, but I no longer have to dig through old notes.

Shell completion

The CLI ships completions for bash, zsh, fish and PowerShell:

jwt-cli completion zsh > ~/.zsh/completions/_jwt-cli

After that, jwt-cli encode <TAB> lists the algorithms, and jwt-cli decode rs256 --private-key <TAB> filters to .pem and .key files. Nice for the muscle memory.

Where to grab it

It’s open source, MIT-licensed, and small enough to read end to end if you’re curious how the signing pieces fit together. If it saves you one trip to a sketchy online JWT debugger, it’s earned its keep.