Skip to main content

Clients overview

Celeriant speaks a binary protocol over TCP, with optional TLS and mTLS. Use a client library or the CLI. Do not hand-roll the wire format; the framing, dictionary compression and Identify handshake have sharp edges the libraries already handle.

What exists, and where to get it

ClientRuntimeInstall today
.NET (Celeriant.Client)net8.0, net9.0, net10.0NuGet, 0.8.0: dotnet add package Celeriant.Client. Pulls in Celeriant.Transport at the same version.
Rust (celeriant_client_tokio)Rust, tokiocrates.io, 0.2.0: cargo add celeriant_client_tokio celeriant_msg celeriant_wal. The request types and AggregateKey live in the two companion crates.
CLI / TUI (celeriant_cli)native binarySource only: cargo build --release -p celeriant_cli in the server repo. It is not in the container image, which ships only the celeriant server binary.

Feature matrix

The two libraries cover the same operations. The CLI is where the gaps are.

.NETRust (tokio)CLI
Write / read / delete / trimyesyesyes
Aggregate detailsyesyesyes
Register schemayesyesyes
List orgs / types / aggregates, all shardsstreamedstreamedcollected, then printed
Auto-paginating readyesyesno, one page
Watch (live subscribe)yesyesTUI only
Expected version + client idempotencyyesyesyes
TLS / mTLSyesyesyes
API key, RSA-keypair identityyesyesyes
Follows leader redirects on writesyesyesTUI only
Route reads to followersopt-inopt-inTUI only
Request compressionwith an identity configuredwith an identity configuredwith --api-key or a key pair

Three rows need a sentence each.

Compression rides on the Identify handshake. The server hands over its zstd dictionary in the Identify response, and a connection that never identifies sends and receives everything uncompressed. The .NET and Rust pools identify only when you set an identity (IdentityConfig / with_identity), and the CLI only when you pass identity flags.

The CLI's one-shot commands talk to exactly one node. Each command opens a single connection to --server, with no pool, no redirect and no follower routing. Point a write at a follower and you get the not-leader error back. The TUI uses the Rust pool and gets all three.

An API key and a key pair do different jobs. The API key sets the connection's access level; the key pair proves a client id. When both are configured, .NET sends only the API key, so the key pair proves nothing. Rust and the CLI send both. See Identity and authentication.

RSA-2048, nothing larger

Identify is a fixed-size frame: the body must fit 1007 bytes (1024 less the 17-byte header), uncompressed. An RSA-2048 public key plus its nonce signature fits. RSA-3072 and RSA-4096 do not; the base64 key and signature alone run past the cap. What happens with a larger key depends on the client:

  • Rust and the CLI fail every dial with a wire MessageTooLarge error before the frame is sent.
  • .NET does not check. The server refuses the oversized frame and closes the socket, so every connect fails with ConnectionFailedException, which looks like a network fault. Use RSA-2048.

Which one

  1. A service on .NET or Rust: that runtime's client. Both pool connections per node, follow leader redirects, and pin reads to the leader unless you opt in to follower reads.
  2. Operating, scripting, poking at data: the CLI. Reach for the TUI when you want to watch a stream live or need a pool.

The pages here cover the .NET client, the Rust client, and the CLI and TUI.