Decode JWTs
View headers, claims, signatures, and readable timestamps flagged when a token is expired or not yet valid.
A command-line tool for JWT, JWS, and JWE
Decode JWTs, verify signatures, and decrypt JWEs with clear, syntax-highlighted output. Everything runs locally, so your tokens never leave your machine.
brew install webcodr/tap/jwtd
$ jwtd eyJhbGciOiJSUzI1NiIs...
Header:
{
"alg": "RS256",
"typ": "JWT"
}
Payload:
{
"sub": "user_2048",
"admin": true,
"iat": "2026-07-22T03:00:00Z (1784689200)",
"exp": "2026-07-22T04:00:00Z (1784692800)",
"note": null
}
Signature: eyJfX2p3dGRfXyI...
01 / overview
Decode without a key. Add one when you need signature verification or decrypted content.
View headers, claims, signatures, and readable timestamps flagged when a token is expired or not yet valid.
Check JWS signatures independently from claim validation.
Inspect protected headers and decrypt compact JWEs with the appropriate key.
Follow JWT and JWE payloads through nested token structures.
Load PEM, DER, certificates, JWKs, encoded keys, or raw secrets.
Emit one machine-readable object with --json for
piping into tools like jq.
02 / installation
A suitable method is selected for your operating system. All options remain available.
Latest release: v5.1.0
macOS
Install the current release from the webcodr tap. Apple silicon and Intel are both covered.
brew install webcodr/tap/jwtd
Linux
Homebrew, native packages, and per-distro repositories are all available. Pick whichever fits your system.
Homebrew — also works on macOS
brew install webcodr/tap/jwtd
Arch Linux — AUR
yay -S jwtd-bin
Fedora / RHEL — COPR
sudo dnf copr enable webcodr/jwtd
sudo dnf install jwtd
Debian / Ubuntu - amd64 Download .deb
curl -fLO https://github.com/webcodr/jwtd/releases/latest/download/jwtd-linux-amd64.deb
sudo dpkg -i jwtd-linux-amd64.deb
Debian / Ubuntu - arm64 Download .deb
curl -fLO https://github.com/webcodr/jwtd/releases/latest/download/jwtd-linux-arm64.deb
sudo dpkg -i jwtd-linux-arm64.deb
Fedora / RHEL / openSUSE - amd64 Download .rpm
curl -fLO https://github.com/webcodr/jwtd/releases/latest/download/jwtd-linux-amd64.rpm
sudo rpm -i jwtd-linux-amd64.rpm
Fedora / RHEL / openSUSE - arm64 Download .rpm
curl -fLO https://github.com/webcodr/jwtd/releases/latest/download/jwtd-linux-arm64.rpm
sudo rpm -i jwtd-linux-arm64.rpm
Windows
Add the webcodr bucket once, then install jwtd.
scoop bucket add webcodr https://github.com/webcodr/scoop-bucket
scoop install jwtd
macOS / Linux
Install from the flake into your profile, or run it ad hoc with nix run.
nix profile install github:webcodr/jwtd
Go 1.26+
Build the latest tagged release with your Go toolchain.
go install github.com/webcodr/jwtd@latest
The Homebrew formula and the .deb / .rpm
packages install shell completions for bash, zsh, and fish.
03 / usage
Pass a token as an argument, pipe it through stdin, or use the interactive prompt.
View the header, claims, and signature without a key.
jwtd eyJhbGciOiJIUzI1NiIs...
Verify the cryptographic signature without evaluating claims such as expiry. Invalid signatures exit nonzero.
jwtd --key /path/to/public-key.pem eyJhbGciOiJSUzI1NiIs...
Compact JWEs are detected automatically. Provide a private key to decrypt the payload. Nested JWT and JWE payloads are decoded in place.
jwtd --key /path/to/private-key.pem eyJhbGciOiJSU0EtT0FF...
Read the token from a pipe, so output from other tools flows straight in. Wrapped or spaced tokens are accepted.
pbpaste | jwtd
Set JWTD_KEY instead of passing --key.
It accepts the same formats and is overridden by an explicit
flag.
JWTD_KEY=/path/to/public-key.pem jwtd eyJhbGciOiJSUzI1NiIs...
Run jwtd with no token and no pipe to enter one at an editable prompt.
jwtd
Prefix the key with raw: to pass a literal
symmetric secret instead of a file, so an HS256 token verifies
without touching disk.
jwtd --key raw:my-hmac-secret eyJhbGciOiJIUzI1NiIs...
Add --json for a single object per token, with exact
numbers and raw timestamps, ready to pipe into
jq. Invalid signatures still print and exit nonzero.
jwtd --json eyJhbGciOiJIUzI1NiIs... | jq .payload
Color auto-disables off a terminal. Use
--color to force it, for example through a pager:
auto, always, or never.
jwtd --color=always eyJhbGciOiJIUzI1NiIs... | less -R
04 / key formats
The --key flag and JWTD_KEY environment
variable use the same format detection.
Explicit HMAC secret
jwtd --key raw:my-hmac-secret eyJhbGciOiJIUzI1NiIs...
05 / algorithms
Signature verification and JWE decryption cover the algorithms defined by the JOSE standards. Accepted signing algorithms are restricted to those matching the key type to rule out algorithm confusion.
HMAC, RSA (PKCS#1 v1.5 and PSS), ECDSA, and EdDSA.
RSA, ECDH-ES, AES key wrap, AES-GCM key wrap, PBES2, and direct.
AES-CBC with HMAC and AES-GCM.
06 / release security
Release archives and Linux packages are listed in
checksums.txt, which is signed with a keyless Cosign
bundle. Each archive also includes a Syft SPDX SBOM.
Verify checksums.txt
cosign verify-blob \
--bundle checksums.txt.sigstore.json \
--certificate-identity-regexp '^https://github.com/webcodr/jwtd/\.github/workflows/release\.yml@' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
checksums.txt
07 / faq
What jwtd does, and just as importantly, what it deliberately does not.
No. jwtd runs entirely on your machine and makes no network requests. Decoding, verification, and decryption all happen locally, so the secrets inside a token never leave your terminal.
No. Signature verification reports only whether the cryptographic signature is valid. Claim checks such as expiry are kept separate, so an expired but authentic token still verifies. An invalid signature exits nonzero.
No. Decoding a JWT and inspecting a JWE protected header need no key. A key is only required to verify a JWS signature or decrypt a JWE payload.
Pass the secret explicitly: hmac:<file> for a
file of secret bytes, or raw:<secret> for an
inline one. jwtd never treats an unrecognized key file as a
symmetric secret on its own — a public key is a published value,
so guessing would let anyone who knows it forge a token that
verified.
Not directly. OpenSSH keys,
authorized_keys entries, and RFC 4716 armor are
detected and reported with a conversion hint. Convert RSA and
ECDSA keys with
ssh-keygen -e -m PKCS8 -f <key>, then pass the
result.
Yes. --json prints one JSON object per token instead
of the colored sections: header, payload, and signature, plus
signatureValid when a key is given. Numbers are exact
and timestamps stay raw, so it pipes cleanly into
jq. An invalid signature still prints and exits
nonzero.