A keyboard-driven terminal client for public Nostr chats. Join geographic or named channels, combine chats in local groups, and connect directly or through SOCKS5/Tor.
Compatible with BitChat public geohash chats and Nymchat named chats when clients use the same channel and share a relay.
Messages are public and unencrypted. Do not send secrets. Ephemeral events are not a guarantee that a relay or another participant will delete a message.
Download the binary for your platform from GitHub Releases.
| Target | Asset |
|---|---|
| Linux amd64 | strchat-tui-linux |
| Android / Termux arm64 | strchat-tui-android-arm64 |
| macOS Apple silicon | strchat-tui-macos-arm |
| macOS Intel | strchat-tui-macos-intel |
| Windows amd64 | strchat-tui.exe |
On Linux, download SHA256SUMS alongside the binary, then verify and run it:
sha256sum --ignore-missing -c SHA256SUMS &&
chmod +x strchat-tui-linux &&
./strchat-tui-linuxOn macOS and Android, also mark the downloaded file executable with chmod +x.
Use --version (or -v) to print the version. To build from source, see
Development.
Enter commands in the Input pane:
/nick alice
/join lobby
Type a message and press Enter to send it to the active chat. Use Chats or
/set <name> to switch chats; only the active chat or group is subscribed.
/join ucfv0 joins a geographic channel because ucfv0 is a valid geohash;
/join lobby joins a named channel. Geographic peers must use the exact same
geohash and precision. 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>... |
Create and activate a local group of joined chats |
/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 proof-of-work for the active chat/group; default 0 |
/block, /unblock |
Manage blocked participants |
/filter, /mute |
Include or hide matching text/regex |
/help |
Full command reference and aliases |
Use nickname completion to address a known participant with @nick#suffix.
In a group, a leading mention sends to that participant's chat instead of the
selected member. This is a public reply, not a DM. Completion uses full
public keys when short identifiers collide; ambiguous recipients are rejected.
| Key | Action |
|---|---|
Ctrl+D |
Quit from any pane (/quit also works) |
Tab / Shift+Tab |
Cycle between visible panes |
Alt+I |
Focus Input |
Alt+O |
Focus Messages |
Alt+C |
Focus Chats |
Alt+N |
Focus Info; open it temporarily on small terminals |
Alt+L |
Focus Logs |
Ctrl+P / Ctrl+N in Input |
Recall older/newer messages and commands |
| Up/Down in Chats | Move through chats and groups |
| Enter in Chats | Activate the highlighted chat/group, or create a group from marked chats |
| Space in Chats | Mark/unmark a chat for a group |
| Delete in Chats | Leave the highlighted chat or remove the group |
| Up/Down in Messages or Logs | Scroll |
` in Messages or Logs |
Maximize/restore the pane when space permits |
Input history holds the last 100 submitted lines for the current run only. Moving forward past the newest entry restores your unfinished draft; history is not saved to disk. Pane titles and the footer show contextual shortcuts.
A group combines joined geographic and named chats in one chronological feed. It is a local view, not a new room: Enter sends to one selected member, not to every chat.
For example, join two chats and combine them:
/join lobby coffee
/set lobby coffee
You can also mark chats with Space in Chats and press Enter to create a group. To choose where to send:
- Highlight the active group in Chats and press
Alt+Nto focus Info. - Use Up/Down to choose a member; Home/End select the first/last. The
▶marker shows the destination immediately, without an extra confirmation. - Press
Alt+Ito return to Input, then type and send with Enter.
The first member is selected by default. Each group's choice is saved across restarts while that member remains in the group; removing it selects the first remaining member. Deleting the group removes its saved choice. A save error is reported if the new choice cannot be written to disk.
The ● thread markers identify messages from the selected chat. Switching the
destination does not hide other chats, change subscriptions or move scrollback.
A leading @nick#suffix overrides the destination for that message without
changing the selection. Unknown or changed destinations are refused rather
than silently rerouted.
On small terminals, Alt+N temporarily replaces the feed with Info; Alt+I
returns to the feed and input without losing the draft.
/filter <pattern>keeps only messages matching at least one enabled filter. With no enabled filters, all messages pass this check./mute <pattern>hides messages matching any enabled mute rule, even if they also match a filter. Filter selects what to include; mute selects what to exclude.
Both match message content using a case-sensitive literal substring or a
Go regular expression wrapped in /.../.
For example, /filter bitcoin keeps messages containing bitcoin, while
/mute /spam|scam/ hides messages containing spam or scam.
The same regexp has opposite effects. With no other rules, used separately:
| Command | Effect |
|---|---|
/filter /\p{Latin}/ |
Show only messages containing a Latin letter, including é and ñ. |
/mute /\p{Latin}/ |
Hide every message containing a Latin letter. |
For example, Привет is hidden by this filter but passes this mute; hello
and Привет, world pass the filter but are hidden by the mute. URLs containing
Latin letters match too. /mute /[A-Za-z]/ matches only ASCII Latin letters,
unlike \p{Latin}. A mute hides the entire message, not just the matching
characters or one line.
Use /filter or /mute without arguments to list rules, or with a rule number
to toggle it. /unfilter <number> and /unmute <number> remove a rule; omit
the number to clear the corresponding list. Rules are saved in the configuration
and affect newly received messages, not retained scrollback.
/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.
The choice is saved and restored at the next launch. Switching themes recolors
the interface and retained messages without resetting input, history or scrollback.
With a local Tor SOCKS listener running on port 9050, start the client with:
./strchat-tui-linux --socks=127.0.0.1:9050Substitute your platform's binary name if different. The --socks option also
accepts socks5://host:port and socks5h://host:port. Pass it on every launch;
it is not saved in the configuration. Proxy authentication is not supported.
All relay connections use the proxy, including geographic relays, reserves,
redirects and loopback destinations. Relay hostnames, including .onion, are
resolved by the proxy. Use an IP address for the proxy itself to avoid local
resolution of its hostname. If the proxy fails, there is no direct fallback.
Add an onion relay with the usual command, replacing the placeholder with its actual address:
/relay ws://<relay-address>.onion
Use ws:// for an onion service offering plain WebSocket on port 80, or wss://
if it offers TLS with a trusted certificate. TLS verification stays enabled.
Tor changes transport routing; public content and persistent identities can
still be linked, so it is not an anonymity guarantee.
- Named chats use configured relays, or bundled defaults when none are set. Failed configured relays do not trigger a switch to unrelated public defaults.
- Geographic chats use the nearest five relays from each of two bundled BitChat catalogs, in addition to configured relays. The catalogs are included in the binary; the client does not download relay lists at startup.
- If a geographic relay fails, the client can temporarily try the next five relays from that catalog while continuing to retry the primary set. A working primary is not replaced just because a reserve is faster.
- Connection attempts and retries are paced. A shared limit of 256 relay connections applies to the active chat/group; a warning reports reduced coverage if a large group or relay list exceeds it.
Use /relay to inspect configured relays and /relay <url> to add one.
Prefer wss:// on untrusted networks; ws:// is available for local or onion
deployments. Relay URLs containing credentials are rejected.
Geographic messages use kind 20000 with a g tag, compatible with BitChat's
public message format when both clients use the same geohash and share a relay.
Named messages use kind 23333 with a d tag, also used by Nymchat; these are
not BitChat location channels. Reserve relays cannot guarantee overlap with a
peer that only listens to its nearest set. If peers cannot see each other,
check the exact channel and configure a shared relay on both clients.
Keep /pow 0 when communicating with peers that do not mine proof-of-work.
The client is live-first: it does not fetch BitChat's one-hour history or send presence heartbeats. Relay acceptance is not proof of recipient delivery. Unacknowledged sends are reported as unconfirmed; there is no automatic resend or persistent outbox.
Configuration is saved in the OS user-config directory under
strchat-tui/config.json. On Linux this is
$XDG_CONFIG_HOME/strchat-tui/config.json, or
~/.config/strchat-tui/config.json when XDG_CONFIG_HOME is unset.
The file contains a private key: do not share it or commit it to a repository. It also stores joined chats/groups, selected group destinations, relays, nickname, theme, filters and mutes. Individual chats use separate session keys that rotate on restart; group messages use the persistent main key.
Go 1.27 or newer is required. Build and check the project from the repository root:
go build -trimpath -o strchat-tui ./cmd/strchat-tui
./strchat-tui --version
go test ./...
go test -race ./...
go vet ./...Optional benchmarks and loopback relay-scale check:
go test ./internal/client ./internal/tui -run '^$' -bench . -benchmem
STRCHAT_RELAY_SCALE=1 go test ./internal/client -run '^TestRelayGroupSafetyCeilingLoopback$' -count=1 -timeout=90s -vMage is pinned in go.mod; no separate installation is needed. List targets
or build all release binaries:
go run github.com/magefile/mage -l
go run github.com/magefile/mageThe default target builds the five assets listed above into the repository
root with CGO_ENABLED=0; no C compiler is required. For individual targets:
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. The individual linux and android
targets write strchat-tui; use the default target for release asset names.
MIT; see LICENSE. The bundled public georelay data is pinned and
attributed in internal/client/georelays.go.
