gnat: a NATS TUI client that won't bug you
A terminal client for NATS and JetStream — streams, consumers, KV, object store, live messages, custom commands.
Earlier this year we open-sourced tempo, a Temporal TUI, and it’s been a huge unlock internally — speeding up debugging and, at the very least, my personal workflow. That kicked off a dangerous game of looking everywhere I could improve tooling and developer experience for the team at Galaxy. Tempo led pretty quickly to jig, a component library for spinning up TUIs with managed state and lifecycle, which let us iterate fast on new ideas across the systems in our stack. Stay tuned for more posts on the technology we’re using and how.
Today we’re open-sourcing gnat — a TUI client we’ve been using internally to monitor our event-driven systems running on NATS. gnat covers monitoring NATS / JetStream instances with dashboard metrics, live message inspection, key-value and object store management, a custom command system, and more.
Quick precursor for anyone unfamiliar: at its core, NATS is a lightweight event system for distributed, event-driven services. Producers publish messages to subjects (topic names) and any number of consumers subscribe and react in real time — no direct coupling, no hard dependencies. With JetStream layered on top you get durable streams, replay, and consumer state, so events can be persisted and processed reliably even when services restart or fall behind. It’s often compared to Kafka.
Features
Dashboard and real-time metrics
A view of your NATS deployment at a glance — message rates, byte throughput, API calls, server info, account limits, server errors.

Stream management
Full CRUD for JetStream streams — browse, create, edit, delete, purge, import from JSON. The list view shows message counts, storage type, replication, and live growth rates (msg/s, bytes/s).

Consumer management and lag dashboard
Browse consumers per stream; create, edit, delete; inspect delivery state. The dedicated consumer-lag dashboard aggregates lag across all streams with pending counts, ack tracking, processing rates, and redelivery metrics.

Live message monitor
Real-time message subscription for both JetStream and NATS core. Subscribe to any subject, watch messages flow in, pause/resume, and toggle delivery policies (all, last, new).

Key-value store management
Browse KV buckets, list keys with revision numbers, get/put/delete values. Live KV Watch streams PUT / DELETE operations in real time with timestamps and revisions.

Object store management
Browse object store buckets, upload/download/delete objects, inspect metadata (size, replication, sealed status).

Subject explorer
Hierarchical tree of all subjects across streams, with message counts and stream associations. Click any subject to jump straight to the message monitor.

Request / reply tester
Interactive request/reply playground — subject input, payload editor, custom headers, configurable timeout, response viewer with latency. Keeps a history of requests with timestamps.

Connection profiles and auth
Named connection profiles with credentials files, tokens, user/password, NKey seeds, and full TLS / mTLS. Environment variable expansion ($NATS_TOKEN) keeps secrets out of config. Hot-swap profiles with P.

Command bar and custom commands
: opens a command bar with tab completion. Built-in commands for navigation (:streams, :monitor, :dashboard, etc.) plus user-defined commands in config with template variables ({stream}, {consumer}, {subject}) and configurable output modes — your TUI, your shortcuts.

Bookmarks
Save frequently accessed resources (streams, consumers, KV buckets) with B and jump back to them from the bookmarks list. Persistent across sessions.

With that, let’s get gnat set up locally.
Install
Via Go:
go install github.com/galaxy-io/gnat/cmd/gnat@latest
Via Homebrew:
brew install galaxy-io/tap/gnat
Configure
gnat is configured via ~/.config/gnat/config.yaml (or wherever $XDG_CONFIG_HOME points). An example config:
theme: tokyonight-night
active_profile: local
profiles:
local:
url: nats://localhost:4222
staging:
url: nats://staging.example.com:4222
credentials: $HOME/.nkeys/staging.creds
domain: staging-js
production:
url: nats://nats.prod.example.com:4222
credentials: $HOME/.nkeys/prod.creds
domain: prod-js
tls:
cert: /etc/nats/tls/client-cert.pem
key: /etc/nats/tls/client-key.pem
ca: /etc/nats/tls/ca.pem
server_name: nats.prod.example.com
skip_verify: false
# Profile-specific commands (override globals with the same name)
commands:
backup-stream:
description: "Backup a stream to file"
cmd: "nats stream backup {stream} /tmp/{stream}-backup"
confirm: true
consumer-reset:
description: "Reset consumer to beginning"
cmd: "nats consumer edit {stream} {consumer} --deliver-policy=all"
confirm: true
token-auth:
url: nats://nats.dev.example.com:4222
token: $NATS_TOKEN
userpass-auth:
url: nats://nats.internal:4222
user: admin
password: $NATS_PASSWORD
nkey-auth:
url: nats://nats.secure.example.com:4222
nkey: $HOME/.nkeys/user.nk
# Global commands (available in all profiles)
commands:
stream-info:
description: "Show detailed stream info"
cmd: "nats stream info {stream}"
output: json
stream-report:
description: "Stream report across all streams"
cmd: "nats stream report"
output: log
consumer-info:
description: "Show consumer details"
cmd: "nats consumer info {stream} {consumer}"
output: json
purge-stream:
description: "Purge all messages from a stream"
cmd: "nats stream purge {stream} -f"
output: log
confirm: true
pub-test:
description: "Publish a test message"
cmd: "nats pub {subject} 'hello from gnat'"
output: log
kv-dump:
description: "Dump all keys in a bucket"
cmd: "nats kv ls {bucket}"
output: log
server-check:
description: "Check server health"
cmd: "nats server check connection"
output: log
A couple of sections worth calling out:
profiles: where your NATS connections live, with their auth (user/pass, NKey, certs).commands: the powerful one. Custom commands with context-aware inputs — if you have a KV bucket selected and type:kv-dump, gnat substitutes{bucket}from the selection. These commands also auto-complete in the command bar.
That’s enough to be running. Below are details for seeding demo data if you want one.
Test data seeding
Once configured, just run gnat. If you don’t have a NATS instance handy, the repo ships a seeder.
Prerequisites
- Go 1.21+
brew install go
- A NATS server (installed or via Docker)
brew install nats-server && nats-server -js -m 8222docker run -d --name nats -p 4222:4222 -p 8222:8222 nats:latest -js -m 8222
Clone the repo:
git clone https://github.com/galaxy-io/gnat
In a separate terminal, run the seeder with --live — it’ll keep running until you quit, sending events at variable intervals and handling consumer responses for request/reply testing.
cd gnat
go run cmd/seeder --live
Finally, running gnat against the localhost:4222 profile will connect to that seeded instance — a fun playground to poke around in.
Issues and contributions
This project is fully OSS, but it has largely been used as an internal tool for a small team. There are likely many features you’d like to see, or more information you want surfaced. Please open issues, feedback, and feature requests on the GitHub issues page. For bug reports, please include the system information from the debug menu — open it with ! from any view; there are keybinds on that screen to copy the debug payload.
Thanks to nats.io for an excellent open source platform and community that lets tools like this exist in the first place.
— Atterpac