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
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:
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.
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.
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:
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:
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
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.