More Speech
More Speech is not trying to be the shiniest Nostr app. It is a desktop reader and writer from Robert C. Martin, built in Clojure with a Swing interface, local storage, relay controls, trust filters, tabs, direct messages, reactions, zaps and Nostr Wallet Connect. Its value is in the shape of the tool: a practical, inspectable client for people who want to see how Nostr behaves when feeds are organized by relays, contacts, threads and trust rather than by a platform timeline.
A desktop reader with a stubbornly plain idea
More Speech is a Nostr desktop client by Robert C. Martin, the software author widely known as Uncle Bob. The repository calls it a Nostr browser in Clojure, and the README describes the mission in practical terms: a tool for communicating over the Nostr protocol. That wording matters because More Speech does not present itself as a lifestyle social network, a creator platform or a phone-first replacement for a familiar app. It is a workstation tool for reading, composing and managing messages that flow through relays.
The project has a very different temperament from most modern Nostr clients. The README says the strategy is to avoid fluff and provide a simple way to scan, read and compose messages. The interface is built with SeeSaw on top of Java Swing, not a glossy web stack. The code is Clojure, with small Java helpers for elliptic-curve operations. The public wiki talks about files named `private/keys`, `private/relays`, `private/tabs-list` and `private/user-configuration`. That is the voice of a tool made for people willing to understand the machinery they are using.
That makes More Speech valuable even if it is not the first app a casual mobile user should install. It shows a different branch of the Nostr design space: explicit relay controls, local database management, trust filters, hand-shaped tabs, profile files, subject tags, direct message commands and zaps that can be driven from a desktop. A reader looking at the Nostr ecosystem should not judge every client by mobile polish alone. More Speech is closer to a power user's mail reader for public notes.
The public project surface
The canonical public source is `github.com/unclebob/more-speech`. GitHub metadata identifies the repository as a Clojure project, created in January 2022, with the description `A Nostr browser in Clojure`, topics for Clojure, Nostr and Nostr client work, and a main branch last pushed in February 2024. During this research on June 11, 2026, the repository had more than three hundred stars and a small set of open issues. Those numbers are not the point of the article, but they help place the project: More Speech is a known public codebase, not a hidden prototype.
There are no GitHub release assets or tags in the repository. The wiki's getting-started page instead points to a standalone JAR hosted on Dropbox and gives source-run commands for Debian using Clojure, Leiningen and OpenJDK 17. That distribution model is important. More Speech is not packaged like Damus, Amethyst, Primal or Lume. It asks the user to be comfortable with Java and Clojure tooling, or at least with running a JAR directly. That alone narrows its audience.
The license surface is also worth reading carefully. The repository has an MIT license file with copyright assigned to Robert C. Martin. The `project.clj` file still contains an older Leiningen-style placeholder license block that names the Eclipse Public License. For a user simply trying the app, this discrepancy is unlikely to be the first concern. For anyone redistributing or embedding code, the actual repository license file and project metadata should both be checked before making assumptions.
Clojure, Swing and a local-first desktop stack
More Speech is built with Clojure 1.11.1 and Leiningen. The project file names SeeSaw as the GUI layer, speclj as the test framework, core.async for asynchronous work, clj-http for HTTP calls, Bouncy Castle for cryptography support and XTDB with RocksDB for production storage. That combination explains much of the app's feel. It is not a browser tab wrapped in a native shell. It is a JVM desktop application with a Swing UI, a local database and protocol code living directly in the same repository.
The source tree reinforces that structure. There are modules for configuration, data storage, database gateway code, in-memory and XTDB implementations, migration logic, Nostr protocol handling, contact lists, event composition, event dispatching, relays, tab search, trust updates, utilities, zaps and Swing windows. The tests are not a token folder. They cover Bech32 handling, storage, migration, Nostr events, protocol behavior, relays, tabs, zaps, formatting and user configuration. The GUI is less testable than the protocol core, but the internal message processing has real test coverage.
That architecture makes More Speech useful to inspect as a Nostr implementation. You can see how an app turns text into signed events, how it stores profiles and events locally, how it subscribes to relay filters, how it handles duplicates, how it updates contact lists and how it processes zap receipts. A polished consumer client often hides those flows behind service layers. More Speech keeps them close enough that a technical reader can follow the path.
Relays are not background plumbing here
More Speech treats relays as first-class objects the user can manage. The wiki's feature page describes a `Relays...` command for adding, deleting and modifying relay entries. A relay can be read as all events, no events, trusted events or web-of-trust events, and it can separately be marked writable or not. The wiki also explains connection marks, fee marks and relay information buttons. This is not a hidden default relay list. Relay behavior is part of the product surface.
The source code matches the documentation. `protocol.clj` subscribes to kinds 0, 1, 2, 3, 4, 7 and 9735, which cover metadata, text notes, relay recommendations, contact lists, encrypted direct messages, reactions and zap receipts. The app sends different subscription filters depending on the relay's read mode. `:read-all` pulls a broad feed, `:read-trusted` follows trusted contacts, and `:read-web-of-trust` expands to contacts trusted by contacts. It also batches historical requests and throttles loading when the websocket backlog grows.
That explicit relay model is one of More Speech's strongest teaching features. Many Nostr users only discover relays when a post disappears, a profile does not load or a paid relay changes what they see. More Speech exposes the choice earlier. A reader can understand that a client is not merely consuming `the Nostr feed`. It is opening websocket connections, sending filters, receiving events, storing what it can and deciding which relays deserve read or write attention.
Tabs are filters, not decoration
The tab system is another part of More Speech that deserves its own explanation. The wiki says the tabs at the top of the app are filters. The default `all` tab contains every recent note received from connected relays. A `trusted` tab contains notes by authors the user explicitly trusts. User-defined tabs can include selected authors, selected message roots or reply-chain fragments, and they can block authors or threads. This is closer to a personal search and reading system than to a generic app navigation bar.
The `private/tabs-list` wiki page shows why this matters. A tab can hold a `:selected` list and a `:blocked` list. A selected ID can be a public key, a root message or a reply message. The result is a feed shaped by relationships and threads, not only by chronological relay intake. The feature page also describes right-click menus for adding authors or notes to tabs, blocking authors or notes from tabs, renaming tabs and adding pattern filters.
This is one of the places where More Speech feels most unlike mainstream social media. The user is invited to build reading surfaces. A topic, person, thread or trusted region of the network can become a tab. That is powerful, but it is not frictionless. The wiki still contains notes about manual file editing and restart requirements for some tab changes, and the to-do list includes further tab improvements. More Speech gives control, but it assumes patience.
Trust is a visible filtering mechanism
More Speech's trust model is not a vague reputation score. The wiki's users window description says the left pane lists trusted authors, while the right pane can show authors from the selected tab, recent global notes or the web of trust. Moving a user into the trusted pane adds that author to the user's trusted contacts. Moving a trusted user out removes them from contacts and the web of trust. In the reading view, trusted names and untrusted names are displayed differently, and users trusted by trusted people can be shown with a relationship marker.
The implementation uses Nostr contact lists as part of that model. The composer can publish a kind 3 contact list with `p` tags and petnames. The dispatcher processes incoming kind 3 contact lists. The protocol layer can request profiles and contacts for chosen public keys and can use trustees to shape relay filters. More Speech's trust surface is therefore not just local UI state. It overlaps with Nostr's public contact-list mechanism while still giving the local reader control over what to display.
There are important limits. A trust list is not a safety oracle. It does not prove that a person is good, that a note is true or that a relay is honest. It is a reading filter. It helps a user reduce noise by paying attention to selected authors and second-degree contacts. That is still useful because Nostr has no central platform ranking engine. More Speech gives the reader a visible alternative: decide whom to trust, let those choices shape tabs and relays, and keep the mechanism inspectable.
Profiles, keys and local private files
More Speech creates and manages a local profile rather than asking for an email login. The getting-started page says the first run generates a private and public key, gives the user a temporary name and creates default tabs. The profile wiki page describes fields such as name, about text, picture, NIP-05 identifier, lud16 zap address and private key. The app can also accept an existing private key in hex or nsec form through the profile UI.
The private key is stored in the local `private/keys` file and encrypted with a password. The wiki warns users not to edit that file by hand and says the default password is `password`. That detail is a useful warning signal for modern readers. More Speech is a hands-on desktop client from an earlier phase of the ecosystem. Anyone testing it with an important identity should first understand the local file layout, change weak defaults where applicable, and prefer a test key until they are comfortable with the app's behavior.
The app also periodically exports profile metadata and imports profile metadata depending on user configuration. The `user_configuration` wiki page describes settings for how often profile metadata is sent to relays and how often profile histories are requested. Again, the client is explicit. It does not pretend a Nostr profile is a cloud account profile. It is signed metadata sent to relays, cached locally and refreshed according to client choices.
Writing notes, subjects, mentions and direct messages
When a user writes a note, More Speech composes a Nostr event locally. The code adds pubkey, created_at, proof-of-work adjusted id and Schnorr signature, then wraps the event in an `EVENT` message for relays. Text events become kind 1 unless the content is recognized as a direct message command. Replies add event reference tags, root and reply references where available, people references for the parent author, optional subject tags and hashtag tags. The app also adds a client tag containing the More Speech version.
The subject handling is historically interesting because NIP-14, the subject-tag NIP, explicitly mentions More Speech as an implementation. More Speech treats a subject as something close to email thread structure, which fits its desktop-reader personality. It wants note lists to have readable subjects rather than only the first words of content. That is another reason the app feels less like a mobile timeline and more like a message browser.
Direct messages are created through a simple text convention. If the content begins with a `D` command aimed at a tagged user, the composer encrypts the content and emits kind 4. The dispatcher decrypts direct messages when the local user is the recipient or sender and hides private events that are not addressed to the user. This is the older Nostr encrypted DM model, not the newer gift-wrap and MLS direction. Readers should treat it as compatibility-era private messaging, not as a high-assurance secure messenger.
Zaps and wallet connect are unusually concrete
More Speech has a real zap implementation, not only a lightning emoji near a post. The wiki describes a `Zap author...` action, zap markers in the UI, zap details, zap receipts, a local `private/zap.log` file and optional automatic thanks messages. The source code handles lud16 and lud06 zap addresses, LNURL callback requests, kind 9734 zap request events and kind 9735 zap receipts. It can copy an invoice to the clipboard when no wallet connection is present.
The more interesting part for the NWC ecosystem is the NIP-47 path. The profile UI has a Wallet Connect button in the wiki, and the migrator tests mention wallet-connect storage. The zap code parses a wallet-connect URI, opens the wallet relay, requests wallet info with kind 13194, sends a kind 23194 request, listens for kind 23195 responses and uses the `pay_invoice` method. This is exactly the kind of old but practical NWC integration that explains why More Speech appears in ecosystem maps.
More Speech also has an auto-zap feature that shows how desktop clients can work around mobile or app-store constraints. The wiki describes running More Speech on a laptop or desktop with a valid wallet connection, then sending yourself a direct message from another client in a specific zap command format. The desktop app receives the message and pays the zap. That is not a mainstream consumer workflow, but it is a clever Nostr pattern: one client can act as an always-on payment helper for another client that cannot or should not hold the same wallet permissions.
Relay auth, proof of work and protocol coverage
More Speech handles more than basic kind 1 notes. The event dispatcher includes NIP-42 relay authentication handling. When a relay sends an `AUTH` challenge, the app creates a kind 22242 event with relay and challenge tags, signs it and sends it back as an `AUTH` message. That matters for paid or restricted relays because authentication is how a relay can ask a client to prove control of a key before accepting reads or writes.
The code also uses proof of work when composing events. `config.clj` defines a proof-of-work default, and event composition calls an id-generation path that can work until the event id has the configured leading-zero difficulty. The wiki's feature page explains proof of work as a way to make a message acceptable to relays or clients that require spam resistance. A reader does not need to love proof of work to understand why a desktop client might expose or enforce it.
The source and tests show support for Bech32 references, including npub, note, nprofile and relay hints in NIP-19 style data. More Speech also stores and displays relay recommendations from kind 2, verifies signatures except for configured skipped kinds, tracks duplicate events by relay, displays reaction counts and pulls relay info documents with an `application/nostr+json` accept header. It is a broad Nostr client, even if many of its feature edges are workmanlike rather than polished.
Local storage is a feature and a maintenance burden
More Speech stores data locally. The code includes an XTDB/RocksDB production database, an in-memory database for tests and migration logic for private files, profiles, contact lists, messages, keys and wallet-connect data. The wiki's database-management section tells users that the database lives in `prod-db` and recommends checking its size periodically. It also documents a compression command that decants recent data into a new database and renames the old database.
That design gives the app a durable desktop feel. Notes, profiles, contact lists, relays and zap receipts are not only ephemeral UI state. They can be requested, cached, filtered, migrated and compressed. For readers who care about personal archives, this is attractive. For readers who want an app that never asks them to understand storage, it may be too much.
The local-first approach also changes the risk profile. A local database can contain public notes, profiles, relay traces, direct-message metadata and payment-related logs. The private directory can contain sensitive key material. Backups, disk encryption, malware exposure and old data retention matter. More Speech is transparent about files and directories, but transparency does not remove the user's responsibility to protect the machine running it.
Where the rough edges show
More Speech's rough edges are visible because the project is public and because the wiki is unusually candid. The getting-started page warns that the initial backlog may be in the thousands and asks the user to wait for it to drain. The private tabs page says some changes may require manual edits or restarts. The to-do list contains unfinished items around tab filters, relay recommendations, search, event references, NIP-05, relay reconnection, database cleanup and better GUI gestures.
GitHub issues add more concrete user friction. A UI scaling issue from 2022 asks how to make the Swing interface readable on a 4K monitor. A dark-mode issue asks for a darker theme. A startup issue from 2023 discusses Java class-version trouble and OpenJDK 17. These are not surprising for a JVM desktop client that is distributed outside app stores. They are still important because they tell a reader what kind of installation and display problems may appear before the app feels useful.
The development timeline also matters. The last public commit on main is from February 2024, and the most recent visible burst of feature work is in 2023 around relay auth, relay manager behavior, tabs, zaps and data storage cleanup. That does not make More Speech dead, but it does mean a user should test it like a technical tool, not like a freshly maintained mainstream client. Check the repository, run with a test key, read the wiki, and expect to solve some environment problems.
Who should try it
More Speech is best for Nostr readers who want to understand the network rather than only consume a polished feed. It is a good fit for developers, protocol students, desktop power users, Clojure readers, relay operators, and people who like explicit local files and visible controls. It is also interesting for anyone studying how NIP-47 wallet connect was integrated into early desktop social clients.
It is not the easiest starting point for a nontechnical person. There is no app-store listing, no current official marketing site, no recent packaged release flow in GitHub, and the setup path may involve Java, Clojure, Leiningen or a standalone JAR link from the wiki. The interface is intentionally plain. Some features carry old-Nostr assumptions. A user importing a valuable key without understanding the local key file and password model would be moving too fast.
The reason to keep More Speech in the Nostr app shelf is not that it beats every modern client at polish. It is that it expresses a serious Nostr idea with unusual clarity. Relays are editable. Trust is visible. Tabs are filters. Zaps can be invoice-based or wallet-connect based. Events are stored locally. The protocol is close enough to the surface that a curious reader can learn from it. In an ecosystem full of phone feeds, that makes More Speech worth its own page.
Sources worth opening
Useful sources start with the More Speech README, wiki and source files, then branch into Uncle Bob's blog post, GitHub issues, the Nostr NIPs behind the implemented event types, and the Clojure/SeeSaw/XTDB tools that shape the application.
- More Speech GitHub repository
- More Speech README
- More Speech wiki home
- More Speech wiki: Features
- More Speech wiki: Getting Started
- More Speech wiki: private keys
- More Speech wiki: private relays
- More Speech wiki: private tabs
- More Speech wiki: user configuration
- More Speech wiki: to-do list
- More Speech project.clj
- More Speech MIT license
- More Speech config.clj
- More Speech event composers
- More Speech protocol module
- More Speech event dispatcher
- More Speech zaps module
- More Speech contact list module
- More Speech relay manager UI
- More Speech tabs UI
- More Speech zaps tests
- More Speech Bech32 tests
- Uncle Bob blog: Functional Classes
- GitHub issue 24: UI scaling
- GitHub issue 32: dark mode
- GitHub issue 41: startup and Java version
- SeeSaw Clojure GUI toolkit
- Leiningen build tool
- speclj testing framework
- XTDB documentation
- Nostr NIPs repository
- NIP-01 basic protocol flow
- NIP-05 DNS-based identifiers
- NIP-10 text notes and threads
- NIP-13 proof of work
- NIP-14 subject tag
- NIP-19 Bech32 encoded entities
- NIP-42 client authentication to relays
- NIP-47 Nostr Wallet Connect
- NIP-57 Lightning zaps





