Consumer

The right panel: tail a topic, read what arrives, and search or export it.

Three tabs: Messages is the list itself, Configholds the two settings a watch is started with (the start position and the buffer budget), and Schema is where you supply the schemas that decode Avro values into their fields. Config's two settings are set before you subscribe and are locked while a watch runs, since neither can be changed under a reader without changing what they are reading. Stop the watch to change them.

Subscribe, pause, resume

SubscribeStarts watching from wherever Config says. Returns once assigned: a bad topic or dead cluster is reported here, not lost in a thread.
PauseStops reading for real. What was read stays: the message you paused to study cannot be evicted from under you.
ResumeReads again from latest, not from where it paused, since catching up is the very bottleneck pausing avoids. The gap is skipped, and the panel says so.
StopEnds the watch. What was read still stays: stopping is not clearing.

Start position

Where a watch begins:

Latest

From the end: tail what arrives next. The default.

Earliest

From the beginning of what the topic still holds.

Offset

From a specific offset in each partition.

Timestamp

From the first message at or after a moment in time.

Last N

The last N messages of each partition, or what it has if it holds fewer.

Buffer budget

How much a tab may hold before the oldest are dropped: a byte budget, not a message count, because a topic of fifty-byte events and one of thirty-kilobyte documents cost wildly different memory at the same count. Set in megabytes (8 MB – 1 GB, default 64 MB, room for a couple of hundred thousand small JSON messages). The history lives in the backend; the panel reads a window of it.

Reading a message

Nothing assumes what the producer sent. Each value is worked out from its bytes:

jsonLaid out to read, never reparsed, so an id past 2⁵³ stays itself.
textPrintable UTF-8, left as it is.
binaryBytes that are not readable text, shown as hex. The first 64 bytes of them, with the number left over stated after: enough to recognise a payload, and the row stays a row.
avroA Confluent Schema Registry frame: a zero byte, then a four byte schema id, then the body. Decoded to its fields when a schema on the Schema tab fits; reported as its id with the body not decoded otherwise.
tombstoneA null value: the key marked for deletion.
emptyA value that is present and carries zero bytes. Its own label, because Kafka keeps this one and deletes the key for a tombstone, and a list that showed both the same way would hide the difference the producer was careful to make.

What an avro row tells you. With no schema supplied you get the schema id and the first 64 bytes of the body as hex, not the fields inside it. Supply the writer's schema on the Schematab and a fitting one decodes the body to its fields (see below).

The frame is recognised by its leading zero byte, so any value that starts with one is read this way. A payload that is not Avro but happens to begin with a zero byte will be labelled avro here, and the id it reports will be nonsense.

A Raw toggle shows the whole record (key, headers, value) as one copyable JSON object, for when the rendering itself is what is in doubt.

The Schema tab: decoding Avro

Supply the writer's schemas here and avro rows decode to their fields, with no registry connection: you bring the schema, this reads the bytes with it. For when you know the shape but are not wired to a Schema Registry.

Add schemaOpens a dialog: choose the side (value or key), paste or upload the .avsc, and add it. The dialog flags invalid JSON as you type; a schema Avro itself rejects is refused on apply, with the reason shown inline.
Each rowOne added schema: its side, its name, and its format. Expand it for the raw text, edit it, or remove it. A dot on the tab marks that at least one is applied.

More than one, per side. Add several value schemas and several key schemas; each incoming frame is tried against the ones for its side in turn, and the first that fully fits decodes it. A frame no supplied schema fits is shown raw, not forced into the wrong one. This is how a topic carrying more than one record type stays readable without a registry.

Up to 10 schemas. Past that, or for a schema that references another named type or a $ref, connect a Schema Registry below: a self-contained schema is the limit of what you can bring by hand.

Schema registry: resolving automatically

A registry resolves framed rows the supplied schemas do not, by the schema id in each message: the reader fetches that id from the registry, decodes, and caches it. You configure the URL and auth once; there is no catalog or subject to choose, the id in the message names the schema.

Add registryOpens an inline form: the registry URL, the auth (none, basic for a Confluent Cloud API key and secret, or bearer), and under TLS a private CA and a client certificate for mTLS. Connect verifies the registry is reachable before configuring it, so a bad url or credential is reported there.
ConnectedThe connected registry shows as a row with its URL and auth; edit it to reconnect with new values, or disconnect it. A registry connected with less than full TLS verification carries a not verified mark on that row, amber, the same measure the padlock uses. It is a live connection, not saved: it is gone when the tab disconnects or the app closes.

Supplied schemas win. A framed row is tried against the schemas on this tab first; the registry is the fallback for the ones they do not decode. A row raw under both stays raw.

When they disagree. If a schema you supplied decodes a row but the registry holds a different schema for the same id, the page warns that the supplied schema may be stale; the value shown is the one your schema produced.

This is a viewer, not a consumer. It shows everything it reads: what fits a schema is decoded, what does not is shown raw, and nothing is skipped. A production consumer should not read past a message it cannot decode; it should validate at the source and dead-letter what does not fit. The forgiving display here is for reading a topic, not a model for how to consume one.

Reading further back

The list shows 100 messages at a time, newest first, which is why the status bar can say a thousand are held while the screen shows a hundred. The rest are not lost, they are behind the two controls at the ends of the list:

Older messagesAt the foot, when there are older ones to reach. Steps back another hundred.
Back to newestAt the head, once you have stepped back. Returns to the top of the list.

While you are reading further back the list holds still, even on a busy topic: arrivals keep filling the buffer and the counts keep climbing, but the page you are on is not pulled out from under you. Come back to newest and it follows the topic again.

Filtering

The search runs in the backend over everything held, not just the rows on screen: a case-insensitive substring across the key, the value and the headers. It searches what was read, never the topic.

A row a schema has decoded, from the Schema tab or from the registry, is searched as its decoded document, so on an Avro topic you can search for the value of a field. A row that is still raw is searched as it reads on screen.

Export

Writes everything the current filter matches to a file:

JSON array (.json)JSON Lines (.jsonl)CSV (.csv)

A content only option writes just the values, without where they came from, for replaying, seeding a fixture, or pasting into a request. It is the match, not the window: what you searched for, not the hundred rows that happened to be on screen.

What a record looks like. A JSON value is written as JSON, not as an escaped string, so .value.orderId reads straight out of the file; its numbers are carried as the characters they arrived in, never parsed and rewritten, so an id past 2⁵³ stays itself. A side the panel decoded is written as the decoded document. Text is written as text, bytes that are not text as base64, a tombstone as null.

The key follows the same rules and is never dropped, framed or not. Headers are a list of {key, value} pairs under the same text-or-base64 rule, and are left out when there are none. What the panel calls binary is binary in the file too: the two share one idea of readable.

Export opens a file picker for where to save. Dismiss it and nothing is written: a cancelled save leaves no file and says nothing.

Clearing

Clear empties what was read, both the list and the buffer behind it, and resets the counts to zero. The watch is untouched: if it was running it keeps running, and the next message arrives into an empty list. Useful for marking a point in time, since what came before is what you have just thrown away.

It is the one thing here that discards messages. Stopping does not, and neither does pausing, since what was read is what you subscribed to see.

The status bar

Live · 1,204 held / 3,890 seen · 42 MB / 64 MB · lag 0 · client id: a3f9c1

The first word answers "am I seeing the topic?", not "is the watch on?": Live while tailing the end, Catching up while lag is being worked off, Paused, Stopped, and Reconnecting when the cluster stops answering under a running watch. That last one comes with the reason, "could not reach the cluster at" the address, in the message area; when the broker answers again the bar says so on the first evidence of it, and Stop returns at once during an outage rather than waiting on the broker.

Held / seen: kept right now, and received in total including those since dropped.

Bytes / budget: how close the buffer is to dropping its oldest.

Lag: how far behind the end of the topic you are. Zero while tailing from latest; the number that matters while catching up.

Client id: what this connection calls itself to the cluster. See Connecting.

Lag is measured against the broker every few seconds rather than on every message, since it is one request per partition and the answer moves in seconds anyway. So it reads 0 for the first moments of a watch, before the first measurement comes back, and it stops moving when the watch does: a paused or stopped reader shows the last figure it had, not how far the topic has run on since.