jwtd is a command-line tool that decodes and pretty-prints JSON Web Tokens (JWT) and JSON Web Encryption (JWE) tokens with syntax-highlighted JSON output. It can verify JWS signatures and decrypt JWEs when given a key via the --key flag or the JWTD_KEY environment variable.
What's new in 5.7.0
A hardening release
Two code-base reviews since 5.6.0 turned up nineteen findings, and this release closes all of them. Nothing changes for well-formed tokens and keys; every fix tightens an edge where jwtd accepted something it should have refused, printed something it should not have, or reported a verdict it could not stand behind.
Three of them mattered for verification
An empty oct JWK was an empty HMAC secret. jwtd rejects an empty symmetric secret because everyone knows it, but a JWK or JWK Set entry with "k":"" reached the verifier without passing that gate, so an HS256 token signed with the empty key reported Signature: VALID. Every JWK path now goes through the same check as raw: and hmac:, and the rejection names the key instead of falling through to a hint that would have suggested the JWK's own JSON as a secret.
A non-string kid selected the first key in a JWK Set. RFC 7515 requires kid to be a string. A header carrying "kid":123 or "kid": null was treated as if no kid were present, and a JWK Set then handed back its first entry without a word. It is now an error, on the human and --json paths and for JWEs alike.
The signing-method allowlist fails closed. An X25519 key parses as a valid JWE key but can verify no JWS. It used to skip the allowlist and rely on each Verify implementation's type assertion to refuse it; jwtd now refuses such a key itself before any verification runs.
Claim validation no longer trusts a number it cannot represent
--verify-claims handed exp, nbf, and iat to the JWT library as raw numbers, and the library converts them through a float whose conversion error it discards. An exp of 1e400 therefore reported token is expired, and an nbf of 1e400 reported valid. Both now yield Claims:INVALID with a reason that names the claim and says the value is not a representable timestamp — the same rule the display already applied when it declined to annotate the value.
--key reaches nested tokens
A decrypted JWE payload that is itself a JWE or JWT is decoded recursively, but the recursion dropped the key:the inner JWE printed its encrypted part sizes and asked for a key you had already passed, and the inner JWT printed without a signature verdict. The outer key is now applied at every level, and an invalid nested signature sets the exit code. When the key does not fit the inner token, jwtd falls back to the keyless rendering it produced before. …
This release is internal work again. Nothing about jwtd's output changes, and on an ordinary token nothing about the speed is perceptible:a jwtd <token> run takes about 0.88 ms, and roughly three quarters of that is process startup and Go runtime initialization that happens before jwtd's own code runs at all.
What changed is the printer. jwtd rendered its colored JSON with go-prettyjson, which marshalled the value back to JSON and re-decoded it before formatting, then assembled the result one string per token — each through fmt.Sprintf, each re-deriving its ANSI escape sequences. jwtd already holds the decoded token, so the round trip was pure overhead. The replacement walks the value straight into a single buffer, resolves each color's escapes once per section, and quotes strings through encoding/json only when quoting would actually change them.
Where it is measurable — a 725 KB token, far larger than anything you would paste by hand:
| | 5.5.0 | 5.6.0 | |---|---|---| | rendering the payload | 7.32 ms | 0.33 ms | | allocations while rendering | 19.6 MB, 136k | 579 B, ~0 | | decode and print, end to end | 11.4 ms | 4.3 ms | | decode and print, ordinary token | 14.6 µs | 4.0 µs |
The last row is the work jwtd does for a normal token, and it is 1 % of the run. Startup dominates, as it did before.
The output is byte-for-byte identical, and that is tested rather than asserted …
5.3.0Jul 25, 2026
Highlights WinGet (Windows) jwtd is now installable with the built-in Windows Package Manager: winget install WebCodr.jwtd The manifest installs the same signed release binary as every other channel, packaged as a portable zip whose hashes are taken from the release's signed checksums.txt. Windows releases now ship an additional .zip archive alongside the existing .tar.gz; its jwtd.exe is byte-for-byte the same binary. Scoop, Homebrew, AUR, Fedora COPR, and Nix are unchanged. What's Changed • feat:publish to WinGet by @bsg62 in #19 Full Changelog:v5.1.0...v5.2.0
5.2.0Jul 25, 2026
Highlights WinGet (Windows) jwtd is now installable with the built-in Windows Package Manager: winget install WebCodr.jwtd The manifest installs the same signed release binary as every other channel, packaged as a portable zip whose hashes are taken from the release's signed checksums.txt. Windows releases now ship an additional .zip archive alongside the existing .tar.gz; its jwtd.exe is byte-for-byte the same binary. Scoop, Homebrew, AUR, Fedora COPR, and Nix are unchanged. What's Changed - feat: publish to WinGet by @bsg62 in #19 Full Changelog: v5.1.0...v5.2.0
Frequently asked questions
Is jwtd free?
Yes. jwtd is free and open-source software released under the MIT license.
What is the latest version of jwtd?
The latest version is 5.7.0, released on Sep 9, 2026.
Is it safe to download jwtd?
The download button links to the official installer published on github.com, the same file Windows Package Manager installs. You can verify the downloaded file by comparing its SHA-256 checksum with 4c8fd855a613e90cc601d904b30a9ef8ca75130a620d86855cef4595eebcaf4b.
Which Windows versions does jwtd support?
jwtd runs on Windows and is available for 64-bit (x64), ARM64 systems.
How do I uninstall jwtd?
Open Settings › Apps › Installed apps, find jwtd and select Uninstall. From the command line, run winget uninstall --id=WebCodr.jwtd -e.
Is there a portable version of jwtd?
Yes. jwtd is available as a portable ZIP archive (jwtd-windows-amd64.zip, 64-bit (x64)) that runs without installation. Copy it to any folder or USB drive and run it from there.
Alternatives to jwtd
Other Command Line Tools programs for Windows from different developers.