nats.rs
CLAUDE.md1.6k starsChanged 3 years agoApache-2.0View on GitHub
This is the official Rust client for NATS.io, a high-performance messaging system. The active crate is async-nats — a fully async, Tokio-based client. The nats crate (synchronous client) is deprecated. It receives security fixes only. All new work happens in async-nats. Do not modify the nats/ directory unless explicitly asked. nats-server: Tests require the nats-server binary. Install via Go: All of these must pass before a PR is merged:
- Installs packages
We split this CLAUDE.md into the rules it gives an agent
10 rules. About 0 are common, 4 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 100
All of these must pass before a PR is merged:
- Very specific: line 126
Always run cargo +nightly fmt before committing.
- Very specific: line 211
Always add the docsrs annotation so docs.rs shows feature requirements.
- Very specific: line 252
Never unwrap() or expect() in library code.
- Very specific: line 286
Use server.restart() to test reconnection.
- Distinctive: line 327
Consider MSRV (1.89.0) when adding dependencies.
- Distinctive: line 331
All public items must have doc comments.
- Very specific: line 332
Use /// # Examples with code blocks.
- Very specific: line 333
Use no_run for examples that need a server: `no_run .
- Distinctive: line 352
Do not remove or modify license headers.
≈ “Common” is an estimate: at least 1 in 5 of the 25,680 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
AGENTS.md — AI Agent Instructions for nats.rs
Project Overview
This is the official Rust client for NATS.io, a high-performance messaging system.
The active crate is async-nats — a fully async, Tokio-based client.
The
natscrate (synchronous client) is deprecated. It receives security fixes only. All new work happens inasync-nats. Do not modify thenats/directory unless explicitly asked.
Repository Structure
nats.rs/
├── async-nats/ # Primary crate — async NATS client (this is where you work)
│ ├── src/
│ │ ├── lib.rs # Entry point, core types (Command, ServerOp, ClientOp, connect())
│ │ ├── client.rs # Client handle — publish, subscribe, request, flush
│ │ ├── connection.rs# Low-level I/O — protocol parsing, read/write buffers
│ │ ├── connector.rs # Connection establishment, reconnection, auth handshake
│ │ ├── options.rs # ConnectOptions builder
│ │ ├── error.rs # Generic Error<Kind> pattern
│ │ ├── header.rs # HeaderMap — NATS message headers
│ │ ├── subject.rs # Subject type, ToSubject trait
│ │ ├── status.rs # StatusCode (100-999 NATS protocol codes)
│ │ ├── message.rs # Message types
│ │ ├── tls.rs # TLS configuration
│ │ ├── auth.rs # Auth trait definitions
│ │ ├── auth_utils.rs# Credential file parsing
│ │ ├── crypto.rs # Crypto feature support
│ │ ├── id_generator.rs
│ │ ├── jetstream/ # JetStream API (feature-gated)
│ │ │ ├── context.rs # JetStream context — streams, publishing
│ │ │ ├── stream.rs # Stream management, consumer creation
│ │ │ ├── consumer/ # Pull, Push, Ordered consumers
│ │ │ ├── kv/ # Key-Value store (feature: "kv")
│ │ │ ├── object_store/ # Object store (feature: "object-store")
│ │ │ ├── errors.rs # JetStream error codes
│ │ │ ├── message.rs # JetStream message types
│ │ │ ├── publish.rs # PublishAck
│ │ │ └── response.rs # Response wrapper
│ │ └── service/ # Service API (feature: "service")
│ │ ├── mod.rs # Service, ServiceBuilder
│ │ ├── endpoint.rs # Endpoint handling
│ │ └── error.rs # Service errors
│ ├── tests/ # Integration tests (require running nats-server)
│ │ ├── configs/ # NATS server config files for tests
│ │ ├── client_tests.rs
│ │ ├── jetstream_tests.rs
│ │ ├── kv_tests.rs
│ │ ├── object_store.rs
│ │ ├── service_tests.rs
│ │ └── ...
│ ├── examples/ # Runnable examples
│ └── benches/ # Criterion benchmarks
├── nats-server/ # Test harness — launches real nats-server instances
├── nats/ # DEPRECATED sync client — do not modify
└── nats-core/ # Experimental embedded/no_std client (separate)Build & Test Commands
# Format (required: nightly toolchain)
cargo +nightly fmt
# Lint — CI denies all clippy warnings
cargo clippy --benches --tests --examples --all-features -- --deny clippy::all
# Build
cargo build --all-targets
# Test (standard — requires nats-server binary in PATH)
cargo test --features slow_tests,websockets -- --nocapture
# Test specific TLS backend
cargo test tls --no-default-features \
--features jetstream,kv,object-store,service,nkeys,nuid,crypto,websockets,ring
# Test feature combinations (thorough)
bash .cargo-hack-check.sh
# Build docs
cargo doc --no-deps --all-features
# Check MSRV (1.89.0)
cargo +1.89.0 check
# Check licenses
cargo deny check licensesnats-server: Tests require the nats-server binary. Install via Go:
go install github.com/nats-io/nats-server/v2@mainCI Requirements
All of these must pass before a PR is merged:
| Check | Command |
| Format | cargo +nightly fmt -- --check |
| Clippy | cargo clippy --benches --tests --examples --all-features -- --deny clippy::all |
| Tests | cargo test --features slow_tests,websockets |
| Docs | cargo doc --no-deps --all-features |
| MSRV | Build with Rust 1.89.0 |
| Licenses | cargo deny check licenses |
| Spelling | cargo spellcheck --code 1 |
| Examples | cargo check --examples |
| Min versions | cargo check --locked --all-features --all-targets (with -Zminimal-versions) |
| TLS backends | Tests run separately with ring, aws-lc-rs, and fips |
| Platforms | Ubuntu, macOS, Windows |
Environment: CI sets RUSTFLAGS="-D warnings" — all warnings are errors.
Formatting Rules
Defined in .rustfmt.toml:
max_width = 100reorder_imports = trueformat_code_in_doc_comments = true- Edition: 2018 (rustfmt setting — the crate itself is edition 2021)
Always run cargo +nightly fmt before committing.
Architecture
Internal Communication
Client (cloneable handle)
│ sends Command variants via mpsc channel
▼
ConnectionHandler (single task)
│ manages subscriptions, multiplexer, ping/pong
│ drives reconnection via Connector
▼
Connection (protocol I/O)
│ read_buf (BytesMut) / write_buf (VecDeque<Bytes>)
│ parses ServerOp, serializes ClientOp
▼
NATS Server (TCP / TLS / WebSocket)Clientis a lightweight, cloneable handle. All protocol work happens inConnectionHandler.- Commands:
Publish,Request,Subscribe,Unsubscribe,Flush,Drain,Reconnect. - Request-reply uses a multiplexer: one subscription handles all requests via inbox tokens.
Key Types
| Type | Location | Purpose |
Client | client.rs | User-facing handle — publish, subscribe, request |
Connection | connection.rs | Protocol I/O — reads ServerOp, writes ClientOp |
Connector | connector.rs | Establishes connections, handles reconnection |
ConnectOptions | options.rs | Builder for connection configuration |
Subject | subject.rs | Validated subject string backed by Bytes |
HeaderMap | header.rs | NATS message headers (HashMap<HeaderName, Vec<HeaderValue>>) |
StatusCode | status.rs | Protocol status codes (100, 200, 404, 408, 409, 503) |
jetstream::Context | jetstream/context.rs | JetStream API entry point |
jetstream::stream::Stream | jetstream/stream.rs | Stream management |
Consumer<T> | jetstream/consumer/ | Pull/Push/Ordered consumers |
kv::Store | jetstream/kv/ | Key-Value store API |
object_store::ObjectStore | jetstream/object_store/ | Object store API |
service::Service | service/mod.rs | Service API |
Feature Flags
# Default: everything enabled
default = ["server_2_10", "server_2_11", "server_2_12", "service", "ring",
"jetstream", "nkeys", "crypto", "object-store", "kv", "websockets", "nuid"]
# Subsystems (each gates a module)
jetstream # JetStream API — pulls in time, serde_nanos, tryhard, base64
kv # Key-Value store (requires jetstream)
object-store # Object store (requires jetstream + crypto)
service # Service API — pulls in time, serde_nanos
# Crypto backends (pick one)
ring # Default crypto backend
aws-lc-rs # Alternative backend
fips # FIPS mode (requires aws-lc-rs)
# Other
nkeys # NKey authentication
nuid # NUID-based ID generation (falls back to rand if disabled)
crypto # Encryption support
websockets # WebSocket transport
experimental # Experimental features
# Server version markers (no code, just enable version-specific fields/methods)
server_2_10
server_2_11
server_2_12
# Test-only
slow_tests # Long-running, time-sensitive tests
compatibility_tests # Cross-client compatibility testsWhen adding new code gated by a feature, follow this pattern:
#[cfg(feature = "jetstream")]
#[cfg_attr(docsrs, doc(cfg(feature = "jetstream")))]
pub mod jetstream;Always add the docsrs annotation so docs.rs shows feature requirements.
Error Handling Pattern
The crate uses a generic Error<Kind> type. Every subsystem defines its own ErrorKind enum:
// 1. Define the kind enum
#[derive(Clone, Debug, PartialEq)]
pub enum FooErrorKind {
NotFound,
TimedOut,
Other,
}
impl Display for FooErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotFound => write!(f, "not found"),
Self::TimedOut => write!(f, "timed out"),
Self::Other => write!(f, "unknown error"),
}
}
}
// 2. Define the error type alias
pub type FooError = Error<FooErrorKind>;
// 3. Construct errors
FooError::new(FooErrorKind::NotFound)
FooError::with_source(FooErrorKind::TimedOut, source_error)
// 4. Match on errors
match result {
Err(err) if err.kind() == FooErrorKind::NotFound => { /* handle */ }
Err(err) => return Err(err.into()),
Ok(val) => val,
}Rules:
- Never
unwrap()orexpect()in library code. - Chain error sources with
.with_source(kind, source). - Tests assert on
.kind(), not string messages. - The top-level
Errortype isBox<dyn std::error::Error + Send + Sync + 'static>.
Testing Patterns
Server Setup
Tests use the nats-server crate to launch real server instances:
use nats_server;
#[tokio::test]
async fn my_test() {
// Basic server
let server = nats_server::run_server("tests/configs/jetstream.conf");
let client = async_nats::connect(server.client_url()).await.unwrap();
// With options
let client = async_nats::ConnectOptions::new()
.event_callback(|event| async move { println!("{event:?}") })
.connect(server.client_url())
.await
.unwrap();
// Cluster (3 nodes)
let cluster = nats_server::run_cluster("tests/configs/jetstream.conf");
}- Server configs live in
tests/configs/. - Servers clean up JetStream storage on drop.
- Use
server.restart()to test reconnection.
Feature-Gated Tests
#[cfg(feature = "kv")]
mod kv_tests {
#[tokio::test]
async fn create_bucket() { /* ... */ }
}Error Assertions
let err = client.publish("bad subject", "".into()).await.unwrap_err();
assert_eq!(err.kind(), PublishErrorKind::BadSubject);Code Conventions
API Design
- Builder pattern for complex configurations (
ConnectOptions,ServiceBuilder). ToSubjecttrait — methods accepting subjects are generic:fn publish(&self, subject: impl ToSubject, ...).- Async everywhere — all I/O methods are async, using Tokio.
Streamtrait — subscribers and consumers implementfutures::Stream.Clone-friendly —Clientis cheap to clone (Arc internals).
Naming
- Error types:
FooError(type alias forError<FooErrorKind>). - Error kinds:
FooErrorKindenum. - Feature-gated modules match feature names (
jetstream,kv,service). - Config structs are named
Configwithin their module.
Imports
- Group: std → external crates → crate-internal.
use crate::error::Error;for the generic error type.use super::*is used sparingly (mainly in tests).
Dependencies
- New dependencies must have licenses allowed in
deny.toml: MIT, Apache-2.0, ISC, BSD-2-Clause, BSD-3-Clause. - Consider MSRV (1.89.0) when adding dependencies.
- Feature-gate optional dependencies with
dep:syntax:dep:time,dep:serde_nanos.
Docs
- All public items must have doc comments.
- Use
/// # Exampleswith code blocks. - Use
no_runfor examples that need a server:```no_run. - Feature-gated items get
#[cfg_attr(docsrs, doc(cfg(feature = "...")))].
Commit & PR Standards
- Linear history — rebase, no merge commits.
- Atomic, reasonably-sized commits.
- Well-formed commit messages (reference).
- Discuss changes via issues before starting work.
- Raise PRs early for visibility.
License
Apache-2.0. All source files carry the license header:
// Copyright 2020-2023 The NATS Authors
// Licensed under the Apache License, Version 2.0Do not remove or modify license headers. New files should include them.
Trending
Less talk lately.about the whole repository
Who's talking about nats-io/nats.rs
- servo/rust-url“Parsing of IPv6 addresses seems to incorrectly include `[]`s”GitHub · May 26, 2022 · 6 reactions
- KooshaPari/ShareCLI“chore(deps): bump async-nats from 0.49.1 to 0.50.0”GitHub · Jul 23, 2026 · 3 reactions
- vectordotdev/vector“Migrate to async_nats NATS client”GitHub · Dec 11, 2022 · 3 reactions
- KooshaPari/ShareCLI“chore(deps): update async-nats requirement from 0.38 to 0.49”GitHub · Jul 3, 2026 · 2 reactions
- wasmCloud/wasmCloud“feat: upgrade `wrpc`, `async-nats`, `wasmtime`”GitHub · Jul 9, 2024 · 2 reactions
The biggest 5 of 52 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 52
Talked about on GitHub, Hacker News and Stack Overflow (52 times).
On GitHub · 50
- May 26, 2022Parsing of IPv6 addresses seems to incorrectly include `[]`s — servo/rust-url6 reactions
- Jul 23, 2026chore(deps): bump async-nats from 0.49.1 to 0.50.0 — KooshaPari/ShareCLI3 reactions · 5 comments
- Dec 11, 2022Migrate to async_nats NATS client — vectordotdev/vector3 reactions · 1 comment
- Jul 3, 2026chore(deps): update async-nats requirement from 0.38 to 0.49 — KooshaPari/ShareCLI2 reactions · 4 comments
- Jul 9, 2024feat: upgrade `wrpc`, `async-nats`, `wasmtime` — wasmCloud/wasmCloud2 reactions · 1 comment
45 more on GitHub
- Oct 30, 2026chore(deps): bump async-nats from 0.50.0 to 0.51.0 — KooshaPari/ShareCLI1 reaction · 8 comments
- Oct 1, 2026chore(deps)(deps): bump the major group across 1 directory with 9 updates — KooshaPari/AgilePlus1 reaction · 7 comments
- Sep 17, 2026chore(deps): bump the cargo-major group across 1 directory with 27 updates — vectordotdev/vector1 reaction · 1 comment
- Aug 31, 2026chore(deps): bump the cargo-major group across 1 directory with 28 updates — vectordotdev/vector1 reaction · 1 comment
- Aug 27, 2026chore(deps): bump the cargo-major group across 1 directory with 28 updates — vectordotdev/vector1 reaction · 1 comment
- Aug 27, 2026chore(deps): bump the cargo-major group across 1 directory with 30 updates — vectordotdev/vector1 reaction · 1 comment
- Aug 13, 2026chore(deps): bump the cargo-major group across 1 directory with 29 updates — vectordotdev/vector1 reaction · 1 comment
- Aug 13, 2026chore(deps): bump the cargo-major group across 1 directory with 27 updates — vectordotdev/vector1 reaction · 1 comment
- Aug 11, 2026Build(deps): bump async-nats from 0.49.1 to 0.50.0 — starknet-innovation/krusty-kms1 reaction
- Aug 7, 2026chore(deps): bump the cargo-major group across 1 directory with 28 updates — vectordotdev/vector1 reaction · 1 comment
- Aug 6, 2026chore(deps): bump the cargo-major group across 1 directory with 43 updates — vectordotdev/vector1 reaction
- Jul 16, 2026chore(deps): bump the patches group across 1 directory with 24 updates — vectordotdev/vector1 reaction · 2 comments
- Jul 16, 2026chore(deps): bump the patches group across 1 directory with 25 updates — vectordotdev/vector1 reaction · 2 comments
- Jul 13, 2026chore(deps): bump the patches group across 1 directory with 25 updates — vectordotdev/vector1 reaction · 2 comments
- Jul 13, 2026chore(deps): bump the patches group across 1 directory with 24 updates — vectordotdev/vector1 reaction · 2 comments
- Jul 9, 2026chore(deps): bump the patches group across 1 directory with 23 updates — vectordotdev/vector1 reaction · 2 comments
- Jul 1, 2026chore(deps): bump the patches group across 1 directory with 25 updates — vectordotdev/vector1 reaction · 2 comments
- Apr 27, 2026chore(deps): bump async-nats from 0.46.0 to 0.47.0 — vectordotdev/vector1 reaction · 1 comment
- Jun 20, 2025chore(deps): move futures to dev-deps — EmbarkStudios/tryhard1 reaction · 4 comments
- Apr 29, 2025Support NATS — arkflow-rs/arkflow1 reaction · 4 comments
- Jul 24, 2023fix(*): support deleting all versions of manifest — wasmCloud/wadm1 reaction
- Jul 10, 2023Breaking change in 0.21.4 (#1339) — rustls/rustls1 reaction · 5 comments
- Jun 21, 2022Official Rust client — mnetship/ratsio1 reaction · 1 comment
- Oct 30, 2026chore(deps): bump async-nats from 0.50.0 to 0.51.0 — botondocska/bpmninja0 reactions · 1 comment
- Oct 30, 2026deps: bump async-nats from 0.50.0 to 0.51.0 — joinedcontext/Antares-NGSI-LD-Context-Broker0 reactions
- Oct 30, 2026chore(deps): bump async-nats from 0.50.0 to 0.51.0 — powersemmi/ruststream-nats0 reactions
- Oct 29, 2026Bug: false alarm on NATS ack message is back since v3.0.0 (regression of #20447) — risingwavelabs/risingwave0 reactions
- Oct 27, 2026build(deps): bump async-nats in / — 192d-Wing/OstrichPKI0 reactions · 1 comment
- Oct 10, 2026build(deps): bump async-nats from 0.47.0 to 0.50.0 — greenticai/greentic-dw0 reactions
- Oct 1, 2026chore(deps): bump async-nats from 0.47.0 to 0.51.0 — laminardb/laminardb0 reactions · 2 comments
- Sep 26, 2026chore(deps): Bump async-nats from 0.49.1 to 0.50.0 — risingwavelabs/risingwave0 reactions · 1 comment
- Sep 16, 2026chore(deps): Bump async-nats from 0.46.0 to 0.50.0 — AngleNet/rising0 reactions
- Sep 6, 2026chore(deps)(deps): bump async-nats from 0.49.1 to 0.50.0 — Okan-wqm/aquaculture_platform0 reactions
- Sep 2, 2026chore(deps): bump async-nats from 0.38.0 to 0.50.0 — 2mes4/agora0 reactions
- Aug 23, 2026build(deps): bump async-nats from 0.47.0 to 0.50.0 — theoxfaber/agentic-payment-risk-governor0 reactions · 1 comment
- Aug 3, 2026Bump async-nats from 0.39.0 to 0.50.0 — ArsCodeAmatoria/proven-stack0 reactions
- Aug 3, 2026Bump async-nats from 0.49.1 to 0.50.0 — theviralfindsmy-hub/GangNiaga-LLM-Dashboard0 reactions
- Aug 3, 2026chore(deps): bump async-nats from 0.38.0 to 0.50.0 in /backend — systemvanta/windmill-platform0 reactions
- Aug 2, 2026build(deps): bump async-nats from 0.49.1 to 0.50.0 — realm-labs/lattice0 reactions
- Jul 20, 2026Update async-nats requirement from 0.49 to 0.50 — Flux-Robotics/rofr0 reactions
- Jun 27, 2026chore(deps): bump async-nats from 0.37.0 to 0.49.1 — craaraju-ctrl/Tredo0 reactions · 1 comment
- Jun 26, 2026chore(deps): bump async-nats from 0.46.0 to 0.49.1 in /services/harness — quarkloop/agent-go0 reactions · 1 comment
- Jun 22, 2026chore(deps): update async-nats requirement from 0.42.0 to 0.50.0 — testcontainers/testcontainers-rs-modules-community0 reactions · 1 comment
- Jun 8, 2026chore(deps)(deps): Bump async-nats from 0.49.0 to 0.49.1 — wienerlabs/wiener-log0 reactions · 1 comment
- Jun 5, 2026build(deps): bump async-nats from 0.38.0 to 0.49.1 in /backend/sync-engine — samertts/Iraq-National-Workforce-Platform-INWP0 reactions · 1 comment
On Hacker News · 1
- Jan 22, 2022Nats.rs: Rust Client for NATS2 points
On Stack Overflow · 1
- Jan 4, 2023Rust: MissingOrMalformedExtensions while connecting to Nats0 votes
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

