nostrdevkit/nostr: a Rust workspace split by concern, not by feature
Rust implementation of the nostr protocol, high-level client library, Nostr Wallet Connect and more.
At a glance
- What is it?
- The nostrdevkit/nostr repository is a Cargo workspace of about twenty crates that separate the protocol from signers, storage, gossip and wallet integration, so an application can depend on the protocol crate alone or on the full SDK. NIP coverage is wide but explicitly incomplete, and several features sit behind crate flags that the README marks as a boundary rather than a guarantee.
- Who is it for?
- The workspace fits Rust developers who want nostr support without adopting a framework, since the protocol crate, the storage traits and the signer implementations can be picked independently, and it fits applications that need to swap an LMDB backend for SQLite.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One Cargo workspace, roughly twenty crates, each with a single job
The repository is organised as a Rust workspace rather than a single crate, and the split is by concern. The workspace manifest makes the shape explicit:
[workspace]
members = [
# Protocol
"nostr",
# Nostr SDK
"nostr-sdk",The full member list covers the protocol crate nostr, the higher level nostr-sdk, the database crates, the gossip crates, the signer crates and two extras for Blossom and Nostr Wallet Connect. Two members are worth noticing because they are not shipped code: database/nostr-database-test-suite and gossip/nostr-gossip-test-suite. The test suites are separate crates so that every storage backend can be run against one shared set of expectations, which is the only way a trait-based storage design stays honest across LMDB, SQLite, memory and nostrdb. The manifest pins the edition at 2024, the minimum supported Rust version at 1.85.0, and the licence at MIT, and the version line across the workspace is 0.45. That version number appearing in every internal dependency declaration means the crates are released together, so mixing a new protocol crate with an old storage backend is not something the workspace structure prevents.
Signers are where your keys actually live
Three signer crates deal with the question of where a private key is held and who is allowed to sign. nostr-browser-signer implements the browser capability described by NIP-07, the window.nostr interface. nostr-browser-signer-proxy exists so that a native application can drive the browser signer, which is the mechanism that lets a desktop or mobile app ask the user's browser extension to sign without the key ever entering the native process. nostr-connect implements Nostr Connect, NIP-46, which is a remote signing protocol: a signer service sits on one side and a client on the other, communicating over a pairing flow rather than a shared file. The nwc crate, Nostr Wallet Connect, NIP-47, sits in a similar space but for money rather than identity, giving a client a payment interface to a wallet. The design consequence is that the protocol crate knows nothing about where keys live. A Rust application can start with a local key, move to a browser extension, and later to a remote signer, without the event handling code changing.
Storage is defined as traits, with four backends behind them
The database section follows the same pattern. nostr-database provides the events database traits, and the backends implement them: nostr-memory for an in-memory store, nostr-lmdb on LMDB, nostr-ndb on nostrdb, and nostr-sqlite on SQLite. For most applications the choice comes down to operational preference rather than API surface, since the trait boundary is what the code sees. SQLite is the pragmatic default for a single process application. LMDB is suited to workloads that want a memory-mapped key-value store with its own write characteristics. nostrdb is the storage engine built for nostr data specifically. The in-memory backend is for tests and for short-lived processes. The important part for an adopter is the shared test suite crate, because it is the mechanism that keeps the four implementations interchangeable, and it is the reason the workspace can claim the same trait API across all of them.
Gossip is a separate concern from storing events
Gossip, which is how a client learns about events from the network rather than from its own database, is split out into its own trait crate with an in-memory implementation, a SQLite implementation and its own test suite, mirroring the database structure. The duplication is deliberate: the gossip layer answers a different question. The database answers what this client already knows, while gossip answers what it might want to fetch next, and the two have different performance characteristics and different lifetimes. Treating them as one problem tends to produce a store that is slow to query and quick to update, or the reverse. nostr-blossom is a further separate library, described as one for interacting with the Blossom protocol, and it is the mechanism for handling file blobs rather than events, which is a payload type that does not fit the event model.
NIP coverage is wide, incomplete, and hidden behind feature flags
The README carries a table of every NIP with an implementation somewhere in the workspace, and it defines its own marks. A supported entry means implemented and tested behind the relevant feature flag, and an unsupported entry means not available anywhere in the repository yet. The table also warns that some features are behind crate flags or available only when using a specific crate, for example signer integrations. The supported set spans the core protocol flow, follow lists, OpenTimestamps attestations, encrypted direct messages, DNS-based internet ids, key derivation from a mnemonic seed, the browser capability, event deletion, e and p tags, the relay information document, proof of work, subject tags, the marketplace, private direct messages, reposts, bech32-encoded entities, the URI scheme, comments, long-form content, extra metadata and tags, reactions, delegated event signing, public chat, custom emoji, unknown events, labeling, git stuff, torrents, sensitive content, user statuses, external identities in profiles, expiration timestamps, client authentication to relays, versioned encrypted payloads, event counts and Nostr Connect. The gaps are specific and worth knowing about: mentions, text note references, relay-based groups and draft events are listed as not available. NIP-08 and NIP-29 are the two that matter most for a chat product, and NIP-37 draft events matter for any editor workflow.
The justfile shows how the project checks and ships itself
The workspace uses just for its task runner, and the recipes describe the discipline more clearly than a contributing guide would. There is a pre-commit path and a CI path, and the difference between them is that the CI variant runs the formatting check first:
# Execute the pre-commit checks
precommit: fmt check-crates check-docs
# Execute continuous integration (CI) checks
ci: check-fmt check-crates check-docsFormatting is delegated to a script under contrib/scripts, the crates are checked by a separate script, and the documentation has its own check, which is the part most Rust projects skip and the one that matters for a library where the docs are the API. Coverage is run through cargo-llvm-cov, with a diff-coverage recipe that compares a branch against master and refuses to run on master itself, so the number you care about is the coverage of the change rather than the coverage of the repository. Releasing is a single confirmed command:
[confirm]
release:
cargo +stable publish --workspacePublishing the whole workspace at once is what keeps those version numbers aligned. The tree also carries rustfmt.toml, clippy.toml, a flake.nix for reproducible development environments, rust-toolchain.toml, a Cargo.lock for the workspace itself, AGENTS.md, CLAUDE.md, SECURITY.md and CONTRIBUTING.md.
What the protocol crate leaves you to build yourself
The split between nostr and nostr-sdk is the decision an adopter makes first. The protocol crate is the Rust implementation of the protocol itself: events, filters, relays and the wire behaviour. The SDK is described as a full-featured SDK for building high-performance and reliable nostr applications, which means it carries the parts you would otherwise assemble: connection handling, caching and the machinery that keeps a client working against relays that are individually unreliable. The workspace dependency list shows what the protocol layer leans on, including negentropy for set reconciliation with relays, which is how a client discovers what it is missing without downloading everything. The trade-off follows from the choice. Take the protocol crate and you get a small dependency surface and full control, but you write the reliability layer. Take the SDK and you get that layer, along with its opinions and its own release cadence. The repository has no GitHub releases, so the crates.io versions and the changelog in the repository are where release information lives, and the last push was on 2026-10-19.
Editorial conclusion
The workspace fits Rust developers who want nostr support without adopting a framework, since the protocol crate, the storage traits and the signer implementations can be picked independently, and it fits applications that need to swap an LMDB backend for SQLite. Do not choose it if you need a batteries-included client with a maintained relay list, or if your product depends on NIP-27 text note references, NIP-29 relay groups, NIP-37 draft events or NIP-08 mentions, which the table lists as not available in the repository. Before depending on it, check the feature flags of the specific crate you need, because the README states that some features exist only behind a flag or only inside a particular crate.
Frequently asked questions
Which NIPs are not implemented in nostrdevkit/nostr?
The support table in the README marks mentions, text note references, relay-based groups and draft events as not available anywhere in the repository yet. A supported mark means implemented and tested behind the relevant feature flag, and some features exist only inside a particular crate.
What is the difference between the nostr crate and nostr-sdk?
The nostr crate is the Rust implementation of the nostr protocol itself, with events, filters and relay behaviour. nostr-sdk is the higher level SDK for building reliable applications, carrying the connection and caching layer you would otherwise assemble yourself.
Which storage backends does nostr-database support?
The nostr-database crate defines the events database traits, and there are backends for an in-memory store, LMDB, nostrdb and SQLite. A shared test suite crate exists so that every backend is checked against the same expectations.
How does Nostr Wallet Connect fit into the workspace?
The nwc crate is the Nostr Wallet Connect client, NIP-47, and it sits alongside the signer crates. Those cover the browser signer for NIP-07, a proxy that lets native applications drive the browser signer, and Nostr Connect for NIP-46 remote signing.
How do I build and release the crates?
The project uses a justfile. The precommit recipe runs fmt, check-crates and check-docs, the ci recipe swaps in check-fmt, and the release recipe runs cargo +stable publish --workspace, which publishes every crate at once and keeps their versions aligned.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nostrdevkit-nostr)Was this useful?