A small keyboard-driven terminal client for public Nostr chats.
- Geohash channels: kind
20000, tagg; compatible with BitChat's public message format when both clients use the same geohash and a shared relay. - Named channels: kind
23333, tagd; also used by Nymchat. These are not BitChat location channels. - Local groups: combine joined chats into a view; not a separate group protocol.
Messages are public, not encrypted. Ephemeral event kinds are not expected to be stored by relays, but this is not a deletion or privacy guarantee.
Download a binary from Releases or build with Go 1.25 or newer:
go build -trimpath -o strchat-tui ./cmd/strchat-tui
./strchat-tui --versionRelease assets (no C compiler or runtime dependency needed):
| Target | Asset |
|---|---|
| Linux | strchat-tui-linux (amd64) |
| Android / Termux | strchat-tui-android-arm64 |
| macOS Apple silicon | strchat-tui-macos-arm |
| macOS Intel | strchat-tui-macos-intel |
| Windows | strchat-tui.exe (amd64) |
Mark the downloaded file executable on Linux, macOS and Android (chmod +x).
--version (or -v) prints the version and, when the release build stamped it,
the commit and build date. No cross-compilation caveats apply: the client is pure
Go and builds with CGO_ENABLED=0.
No account registration, database or background service is required. Configuration
uses the OS user-config directory under strchat-tui/config.json (on Linux,
$XDG_CONFIG_HOME/strchat-tui, or ~/.config/strchat-tui). It includes a private
key: do not publish it. Chat keys are separate ephemeral process-session keys;
local group replies retain the existing main-key behavior.
/join ucfv0
/join lobby
/nick alice
/relay
/help
/quit
A valid geohash is interpreted as a location channel. Other names are topic
channels. Only the active chat or local group is subscribed. Use the chat list or
/set <name> to switch. Geohash spelling and precision must match the other client
exactly; joining a parent cell does not subscribe to its children.
Useful commands:
| Command | Purpose |
|---|---|
/join <name> [name...] |
Join chats |
/set <name> |
Activate a chat/group |
/set <name1> <name2>... |
Combine existing chats in a local group |
/list, /del [name] |
List or leave chats |
/nick [name] |
Set or clear a nickname |
/theme [name|number], /t |
List themes or switch and save colors |
/relay [url...] |
List or add explicit relays |
/relay <number> |
Remove a configured relay |
/pow <0..16> |
Set per-chat proof-of-work; default 0, above the cap is rejected |
/block, /unblock |
Manage blocked participants |
/filter, /mute |
Include or hide matching text/regex |
/help |
Full command reference and aliases |
Use @nick#suffix completion to reply to a known participant; replies from local
combined views go to that participant's chat. This is still a public message,
not a DM.
In the input field, Ctrl+P recalls older messages and commands; Ctrl+N
moves forward and restores your unfinished draft after the newest entry.
The last 100 non-empty submitted lines are kept in memory for this run only,
including send attempts that failed; history is never written to disk.
/theme lists the available themes and marks the current selection:
default— the original green-on-black theme (used when no theme is saved).monochrome— high-contrast black and white.blue-gray— cool blue accents on a dark gray background.red-gold— red borders and gold accents on a dark warm background.
Use /theme blue-gray, /theme red-gold, or a number such as /t 4.
Colors change without restarting, including retained messages and logs; input,
history, focus and chat selection are preserved. The selected name is saved in
the theme field of config.json and restored at the next launch. Old configs
without this field (or with an unknown theme name) use default. If saving
fails, an error is shown and the previous theme remains active.
- At most 12 selected relay connections; one subscription per relay.
- Explicit relays plus the nearest five geographic relays of each BitChat catalog (the app's reviewed list and upstream georelays); defaults for named chats when no relays are configured. Empty configuration does not mean offline mode.
- No recursive relay discovery, trial publications or background relay probing.
- Georelay selection works offline from two pinned bundled catalogs only. Startup and chat switching never download a relay list, and no local file can change it; refreshing a catalog means upgrading the binary.
- Reconnect continues while a relay remains selected, with jittered backoff; changing views updates subscriptions, and leaving stops unused connections.
- Bounded message queues, wire sizes, duplicate caches and terminal scrollback.
- A positive relay
OKmeans acceptance by that relay, not delivery to a recipient. Missing acknowledgement is reported as unconfirmed; no automatic resend or durable outbox. Offline sends are rejected visibly, not silently kept. - Chat session keys survive view switches, but rotate on process restart.
ws:// supports local/private deployments; prefer wss:// on untrusted networks.
Relay URL paths and query strings are preserved. Private URLs containing embedded
credentials are rejected.
The two current BitChat implementations use different geographic relay catalogs,
so a single-catalog client can share no relay at all with a peer of the other
variant (measured: 0/5 overlap for some geohashes). This client therefore bundles
both catalogs and subscribes to the nearest relays of each, which is what makes
those peers reachable. Configure an actual shared relay with /relay <url> when a
peer is in a chat where neither nearest set is reachable. The client keeps
live-first subscriptions, a small reconnect overlap, and local size/age limits; it
does not fetch BitChat's one-hour history or send presence heartbeats.
Keep /pow 0 to avoid filtering peers that do not mine proof-of-work.
Presence, persistent per-geohash identities, bounded history and a richer PoW policy are proposals, not silently enabled features (dual-catalog selection was approved and is implemented). Generic NIP-17 DMs, NIP-29, NIP-28, NIP-EE and Marmot/MLS are outside the project scope.
go test ./...
go test -race ./...
go vet ./...
go test ./internal/client ./internal/tui -run '^$' -bench . -benchmemMage v1.15.0 is pinned in go.mod; no separate Mage installation is needed.
From the repository root, list targets or build the full release set with the
default target (all):
go run github.com/magefile/mage -l
go run github.com/magefile/mageThe default target builds all five binaries into the repository root:
strchat-tui-linux, strchat-tui-android-arm64, strchat-tui-macos-arm,
strchat-tui-macos-intel, and strchat-tui.exe. To build selected targets,
append one of these case-sensitive Mage target names:
go run github.com/magefile/mage linux
go run github.com/magefile/mage android
go run github.com/magefile/mage windows
go run github.com/magefile/mage macOSmacOS builds both macOS architectures. Mage sets CGO_ENABLED=0 for
cross-compilation, so no C compiler is required.
Measured behaviour of this client is reported, not assumed: a 1000-message burst
costs 2 screen draws instead of 2007, startup is ~15 ms to the first frame on
the development host, and a ten-minute soak over real sockets holds goroutines,
file descriptors and heap flat. Rendering benchmarks and relay soak tests are
in internal/tui/bench_test.go and internal/client/*_test.go.
MIT; see LICENSE. The bundled public georelay data is pinned and
attributed in internal/client/georelays.go.