Package Exports
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (jwt-lab) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
██╗██╗ ██╗████████╗ ██╗ █████╗ ██████╗
██║██║ ██║╚══██╔══╝ ██║ ██╔══██╗██╔══██╗
██║██║ █╗ ██║ ██║█████╗██║ ███████║██████╔╝
██ ██║██║███╗██║ ██║╚════╝██║ ██╔══██║██╔══██╗
╚█████╔╝╚███╔███╔╝ ██║ ███████╗██║ ██║██████╔╝
╚════╝ ╚══╝╚══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═════╝
────────────────────────────────────────────────────────
v0.1.0 · JWT toolkit for developers & AI agentsjwt-lab
The JWT Swiss-Army Knife for Developers & AI Agents
Encode · Decode · Verify · Inspect · Explain · Keygen · MCP Server
A fast, secure, beautiful, and AI-agent-ready command-line tool for working with JSON Web Tokens (JWTs), plus a full Model Context Protocol (MCP) HTTP/JSON server.
Installation · Quick Start · Commands · MCP Server · Configuration · API Reference
Why jwt-lab?
| Feature | jwt-lab | jwt.io | Other CLIs |
|---|---|---|---|
| 🔐 Security linting & audit | ✅ 6 built-in rules | ❌ | ❌ |
| 🤖 AI-native MCP server | ✅ Full HTTP/JSON API | ❌ | ❌ |
| 🗣️ Natural language encoding | ✅ "admin token expires in 1h" |
❌ | ❌ |
⏰ Time travel (--fake-time) |
✅ Deterministic testing | ❌ | ❌ |
📋 Config as code (.jwt-cli.toml) |
✅ Profiles, defaults, keys | ❌ | ❌ |
| 🎨 Premium terminal UX | ✅ Colors, boxes, tables | ❌ | Partial |
| 🔑 Key generation (RSA/EC/Ed25519) | ✅ JWK + PEM output | ❌ | Partial |
| 📦 Dual ESM + CJS output | ✅ | N/A | ❌ |
🧪 Strict TypeScript, zero any |
✅ | N/A | ❌ |
Installation
# Global install (recommended)
npm install -g jwt-lab
# Or use with npx
npx jwt-lab --help
# Or add to a project
npm install jwt-lab --save-devQuick Start
# Encode a JWT with HMAC secret
jwt encode '{"sub":"user1","role":"admin"}' --secret my-secret --exp 1h
# Decode without verification
jwt decode eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMSJ9.xxx
# Verify signature + claims
jwt verify <token> --secret my-secret
# Security audit (no keys needed)
jwt explain <token>
# Inspect with full breakdown
jwt inspect <token> --secret my-secret
# Generate key pairs
jwt keygen ec --pem --out-dir ./keys
# Natural language encoding
jwt encode "admin token for user ali@example.com expires in 12h" --secret s
# Start MCP server for AI agents
jwt mcp serve --port 3000Commands
jwt encode
Encode a JWT from JSON or natural language.
# JSON payload
jwt encode '{"sub":"user1","role":"admin","email":"user@example.com"}' \
--secret my-secret \
--exp 1h \
--iss https://auth.myapp.com
# Natural language (no LLM — deterministic regex parser)
jwt encode "admin user user@example.com expires in 30m" --secret s
# With asymmetric key
jwt encode '{"sub":"svc"}' --key ./private.pem --alg ES256
# With profile from config
jwt encode '{"sub":"user1"}' --secret s --profile access_token
# Copy to clipboard
jwt encode '{"sub":"user1"}' --secret s --exp 1h --copy
# JSON output
jwt encode '{"sub":"user1"}' --secret s --jsonOptions:
| Flag | Description |
|---|---|
--secret <string> |
HMAC secret (HS256/384/512) |
--key <path> |
PEM or JWK private key file |
--alg <algorithm> |
Signing algorithm |
--exp <duration> |
Expiration (e.g., 1h, 30m, 7d) |
--iss <string> |
Issuer claim |
--sub <string> |
Subject claim |
--aud <string> |
Audience claim |
--kid <string> |
Key ID in header |
--jti |
Generate random UUID as JTI |
--header <json> |
Additional header fields |
--profile <name> |
Use named profile from config |
--copy |
Copy token to clipboard |
--json |
Output as JSON |
jwt decode
Decode a JWT without verification.
jwt decode <token>
# From stdin
echo "<token>" | jwt decode -
# Batch mode
cat tokens.txt | jwt decode - --batch
# JSON output
jwt decode <token> --jsonjwt verify
Full signature verification and claims validation.
# HMAC
jwt verify <token> --secret my-secret
# Asymmetric key
jwt verify <token> --key ./public.pem --alg ES256
# JWKS endpoint
jwt verify <token> --jwks https://auth.example.com/.well-known/jwks.json
# Required claims
jwt verify <token> --secret s --require sub,iss,exp
# Clock skew tolerance
jwt verify <token> --secret s --leeway 30
# Time travel for testing
jwt verify <token> --secret s --fake-time 2024-01-01T00:00:00ZOutput:
✅ Valid JWT
Algorithm: HS256
Subject: user1jwt inspect
High-level token breakdown with status, metadata, and security posture.
jwt inspect <token>
jwt inspect <token> --secret my-secret # with verification
jwt inspect <token> --json # machine-readable
jwt inspect <token> --table # table formatOutput:
╭───── Token Inspection ──────╮
│ │
│ Status: ✅ valid │
│ Algorithm: HS256 │
│ Subject: user1 │
│ Issuer: auth.example.com │
│ Expires in: 59m 30s │
│ │
│ Lint Findings: │
│ ⚠️ [pii-claims] Payload ... │
│ │
╰──────────────────────────────╯jwt explain
Static security audit — no keys required.
jwt explain <token>
jwt explain <token> --json # for CI pipelines
jwt explain <token> --table # table formatOutput:
🔍 JWT Security Audit
❌ [none-algorithm] Token uses the "none" algorithm
→ Replace "none" with a secure algorithm such as RS256 or ES256
⚠️ [pii-claims] Payload contains claims that may hold PII: email
→ Avoid embedding PII directly in JWT payloads
ℹ️ [hmac-preferred-asymmetric] Token uses HMAC algorithm (HS256)
→ Consider using an asymmetric algorithm such as RS256 or ES256Built-in security rules:
| Rule ID | Severity | What it checks |
|---|---|---|
none-algorithm |
🔴 error | Algorithm is "none" |
missing-exp |
🟡 warn | Token has no expiration |
long-lived-token |
🟡 warn | Lifetime > 24 hours |
pii-claims |
🟡 warn | Claims containing PII patterns |
missing-nbf-long-lived |
🔵 info | Long-lived token without nbf |
hmac-preferred-asymmetric |
🔵 info | HMAC where asymmetric is preferred |
jwt keygen
Generate cryptographic key pairs.
# EC key pair (default P-256)
jwt keygen ec
# RSA key pair
jwt keygen rsa --bits 4096
# Ed25519
jwt keygen ed25519
# PEM output to files
jwt keygen ec --pem --out-dir ./keys
# JWK with key ID
jwt keygen rsa --jwk --kid my-production-keyMCP Server
jwt-lab includes a full Model Context Protocol HTTP/JSON server for AI agents and programmatic access.
Start the server
jwt mcp serve --port 3000 --host 0.0.0.0Endpoints
| Method | Path | Description |
|---|---|---|
POST |
/encode |
Encode a JWT |
POST |
/decode |
Decode a JWT |
POST |
/verify |
Verify a JWT |
POST |
/inspect |
Inspect a JWT |
POST |
/keygen |
Generate key pair |
POST |
/explain |
Security audit |
GET |
/docs |
OpenAPI 3.1 spec |
GET |
/health |
Health check |
Examples with curl
# Encode
curl -X POST http://localhost:3000/encode \
-H "Content-Type: application/json" \
-d '{"payload":{"sub":"user1"},"secret":"my-secret","alg":"HS256","exp":"1h"}'
# Decode
curl -X POST http://localhost:3000/decode \
-H "Content-Type: application/json" \
-d '{"token":"eyJhbGciOiJIUzI1NiJ9..."}'
# Verify
curl -X POST http://localhost:3000/verify \
-H "Content-Type: application/json" \
-d '{"token":"eyJ...","secret":"my-secret"}'
# Explain (security audit)
curl -X POST http://localhost:3000/explain \
-H "Content-Type: application/json" \
-d '{"token":"eyJ..."}'
# Generate key pair
curl -X POST http://localhost:3000/keygen \
-H "Content-Type: application/json" \
-d '{"type":"ec","format":"jwk"}'
# OpenAPI docs
curl http://localhost:3000/docsAuthentication
Set the MCP_API_KEY environment variable to enable Bearer token authentication:
MCP_API_KEY=your-secret-key jwt mcp serve
# Then include the key in requests:
curl -X POST http://localhost:3000/encode \
-H "Authorization: Bearer your-secret-key" \
-H "Content-Type: application/json" \
-d '{"payload":{"sub":"user1"},"secret":"s","alg":"HS256"}'Security Features
- Token redaction: Full tokens are never logged; truncated to 20 chars
- Claim redaction: Configure
mcp.redactClaimsto hide sensitive claims in responses - Rate limiting: Sliding window per IP (configurable)
- CORS: Configurable allowed origins
- Input validation: All requests validated with Zod schemas
Configuration
Create a .jwt-cli.toml in your project root:
[defaults]
iss = "https://auth.myapp.com/"
aud = "myapp-api"
alg = "ES256"
[profiles.access_token]
ttl = "15m"
scopes = ["read", "write"]
[profiles.service_token]
ttl = "1h"
aud = "internal-service"
[lint]
piiClaimPatterns = ["email", "phone", "ssn"]
[lint.severityOverrides]
"missing-exp" = "error"
[mcp]
port = 3000
redactClaims = ["email", "phone"]
[mcp.rateLimit]
windowSeconds = 60
maxRequests = 100The CLI auto-discovers .jwt-cli.toml by walking upward from the current directory. Use --config <path> to specify a custom path.
Priority: CLI flags > config file > built-in defaults
Global Flags
| Flag | Description |
|---|---|
--help |
Show help |
--version |
Show version |
--fake-time <iso8601> |
Override system clock |
--config <path> |
Path to config file |
--json |
Machine-readable JSON output |
API Reference
Core Library
jwt-lab's core is a pure, I/O-free TypeScript library. All functions return Result<T, E> types — no exceptions thrown.
import { encodeToken, decodeToken, verifyToken, lintToken, generateKeyPair, parseDuration } from 'jwt-lab';See src/core/ for full API documentation with TSDoc comments.
Tech Stack
| Category | Choice |
|---|---|
| Language | TypeScript 6 (strict mode, zero any) |
| Runtime | Node.js ≥ 22 |
| JWT | jose v6 |
| CLI | commander v14 |
| Validation | zod v4 |
| HTTP | hono + @hono/node-server |
| Build | tsup (dual ESM + CJS) |
| Tests | vitest v4 |
| Terminal | picocolors, boxen, ora, cli-table3 |
Shell Completions
jwt-lab ships with built-in tab-completion scripts for Bash, Zsh, and Fish. The completions are aware of every subcommand and flag — pressing Tab surfaces commands, options, algorithm names, and file paths in context.
How it works
The jwt completions <shell> command prints a shell-specific completion script to stdout. You either eval it at shell startup or write it to a file that your shell auto-loads. No third-party tools are required.
jwt completions bash → prints a Bash completion function + `complete -F` binding
jwt completions zsh → prints a Zsh `_jwt` compdef function
jwt completions fish → prints Fish `complete` directivesBash
One-liner (current session only):
eval "$(jwt completions bash)"Persistent — add to ~/.bashrc:
echo 'eval "$(jwt completions bash)"' >> ~/.bashrc
source ~/.bashrcOr save to the system completions directory (recommended for shared machines):
jwt completions bash | sudo tee /etc/bash_completion.d/jwt > /dev/nullRequires
bash-completionpackage. Install withbrew install bash-completionon macOS orapt install bash-completionon Debian/Ubuntu.
Zsh
One-liner (current session only):
eval "$(jwt completions zsh)"Persistent — add to ~/.zshrc:
echo 'eval "$(jwt completions zsh)"' >> ~/.zshrc
source ~/.zshrcOr save to a $fpath directory (the clean approach):
# Pick any directory already in your fpath, or create one
mkdir -p ~/.zsh/completions
jwt completions zsh > ~/.zsh/completions/_jwt
# Make sure the directory is in fpath — add to ~/.zshrc if not already there:
echo 'fpath=(~/.zsh/completions $fpath)' >> ~/.zshrc
echo 'autoload -Uz compinit && compinit' >> ~/.zshrc
source ~/.zshrcoh-my-zsh users: Save to
~/.oh-my-zsh/completions/_jwt— it's already infpath.
Fish
Fish completions are discovered automatically from ~/.config/fish/completions/. Just save the script there:
jwt completions fish > ~/.config/fish/completions/jwt.fishCompletions take effect immediately — no source or restart needed.
What gets completed
| Context | Completions offered |
|---|---|
jwt <Tab> |
All subcommands with descriptions |
jwt encode <Tab> |
--secret, --key, --alg, --exp, --iss, --json, … |
jwt verify <Tab> |
--secret, --key, --jwks, --oidc-discovery, --alg, --require, --leeway, … |
jwt keygen <Tab> |
Algorithm types: RS256 RS384 RS512 ES256 ES384 ES512 EdDSA PS256 PS384 PS512 |
jwt --alg <Tab> |
Full algorithm list |
--key <Tab> |
File path completion (all shells) |
--config <Tab> |
File path completion (all shells) |
jwt completions <Tab> |
bash zsh fish |
CI/CD & Publishing
Every push and pull request to main runs the full pipeline:
| Job | Steps |
|---|---|
| Test & Build | lint → type-check → tests → build → CLI smoke test (Node 22 & 24) |
| Security Audit | npm audit at moderate severity |
| CodeQL | Static analysis for JavaScript/TypeScript (separate scheduled workflow) |
| Publish | Runs only on v* tag push → bumps version → builds → publishes to npm with provenance |
Publishing a release
# 1. Bump version
npm version 1.0.0 --no-git-tag-version
git add package.json && git commit -m "chore: bump version to 1.0.0"
git push origin main
# 2. Create a GPG-signed annotated tag
git tag -s v1.0.0 -m "Release v1.0.0"
git tag -v v1.0.0 # verify signature
# 3. Push tag — triggers the publish workflow
git push origin v1.0.0
# 4. Create a signed GitHub Release
gh release create v1.0.0 --title "v1.0.0" --notes "Release notes here" --verify-tagFor a prerelease (e.g. v1.1.0-beta.1), the package is published with the beta dist-tag automatically.
The workflow uses
npm publish --provenance, which attaches a cryptographic SLSA Level 2 attestation proving the package was built from this exact commit.
Required repository secret
| Secret | Description |
|---|---|
NPM_TOKEN |
npm Automation token with publish access — add at Settings → Secrets → Actions |
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Type check
npm run type-check
# Lint
npm run lint
# Start MCP server (dev)
npm run dev:mcpCI/CD & Publishing
Every push and pull request to main runs the full pipeline:
| Job | Steps |
|---|---|
| Test & Build | lint → type-check → tests → build → CLI smoke test (Node 22 & 24) |
| Security Audit | npm audit at moderate severity |
| CodeQL | Static analysis for JavaScript/TypeScript (separate scheduled workflow) |
| Publish | Runs only on GitHub Release publish → bumps version → builds → publishes to npm |
Publishing a release
- Create and push a tag:
git tag v1.0.0 && git push origin v1.0.0 - Create a GitHub Release from that tag (set it to Published, not Draft)
- The pipeline auto-bumps
package.json, builds, and publishes to npm
For a prerelease (e.g. v1.1.0-beta.1), the package is published with the beta dist-tag automatically.
Required repository secret
| Secret | Description |
|---|---|
NPM_TOKEN |
npm automation token with publish access — add in Settings → Secrets → Actions |
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT © jwt-lab contributors
Built with ❤️ for developers and AI agents