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.