This is an automated email from the ASF dual-hosted git repository.

spetz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iggy-website.git


The following commit(s) were added to refs/heads/main by this push:
     new b21c2524 Update landing, benchmarks, VSR
b21c2524 is described below

commit b21c2524c90ae9e472ee9abb08926ac29f73c667
Author: spetz <[email protected]>
AuthorDate: Tue Jul 28 19:09:24 2026 +0200

    Update landing, benchmarks, VSR
---
 content/docs/clustering/vsr.mdx    | 327 +++++++++++++++++++--------
 src/app/(home)/page.tsx            | 110 ++++++---
 src/components/benchmark-chart.tsx | 442 ++++++++++++++++++++++++++++---------
 src/components/code-tabs.tsx       |  55 ++++-
 4 files changed, 708 insertions(+), 226 deletions(-)

diff --git a/content/docs/clustering/vsr.mdx b/content/docs/clustering/vsr.mdx
index 4cc33e1a..82263b2e 100644
--- a/content/docs/clustering/vsr.mdx
+++ b/content/docs/clustering/vsr.mdx
@@ -1,136 +1,285 @@
 ---
 title: Viewstamped Replication
+description: VSR clustering architecture, configuration, and development guide
 ---
 
-Iggy is building clustering support based on **Viewstamped Replication 
(VSR)**, a consensus protocol for state machine replication. You can read the 
original [Viewstamped Replication 
Revisited](https://sands.kaust.edu.sa/classes/CS240/F21/papers/vr-revisited.pdf)
 paper for the full protocol specification. This work is actively in progress 
and represents a major milestone for production readiness. The building blocks 
are already implemented in the `core/consensus/` crate.
+Apache Iggy is adding replicated clusters based on **Viewstamped Replication 
Revisited (VSR)**. VSR keeps an ordered state machine consistent across 
replicas and elects a new primary when the current primary fails.
 
-## What is Viewstamped Replication?
+Read the [VSR paper used by the 
project](https://github.com/apache/iggy/blob/master/assets/vsr.pdf) for the 
protocol specification.
 
-VSR is a consensus protocol (similar in purpose to Raft or Paxos) that ensures 
a group of replicas agree on the same sequence of operations, even in the 
presence of failures. It was chosen for Iggy because of its simplicity and 
proven track record in high-performance distributed systems.
+> VSR clustering is experimental and targets Iggy v1. It is close to feature 
completion, but it is not yet a production release.
 
-The protocol operates in three main phases:
+## Current implementation
 
-1. **Normal operation** - the primary (leader) receives client requests, 
assigns them sequence numbers, replicates them to backups, and commits once a 
quorum acknowledges
-2. **View change** - if the primary fails, the remaining replicas elect a new 
primary by transitioning to a higher view number
-3. **Recovering** - a replica that fell behind can catch up by receiving a 
snapshot and replay log from the primary
+The implementation lives in the next-generation 
[`core/server-ng`](https://github.com/apache/iggy/tree/master/core/server-ng) 
server. It includes:
 
-## Architecture
+- normal operation with quorum commits
+- view changes and deterministic primary selection
+- metadata and partition replication
+- persistent WAL recovery and snapshots
+- replica rejoin with journal repair
+- client sessions, request fencing, and duplicate detection
+- replica authentication and optional TLS
+- follower-aware routing in the Rust SDK
+- deterministic simulation and Rust BDD coverage
 
-The VSR implementation in Iggy is split into two planes, each handling 
different types of operations:
+The main components are:
 
-The VSR implementation splits replicated operations into two planes:
+| Component | Source | Responsibility |
+| --- | --- | --- |
+| Server | 
[`core/server-ng`](https://github.com/apache/iggy/tree/master/core/server-ng) | 
Sharded runtime, listeners, recovery, and request dispatch |
+| Consensus | 
[`core/consensus`](https://github.com/apache/iggy/tree/master/core/consensus) | 
VSR state machine, quorum, view changes, and timeouts |
+| Wire protocol | 
[`core/binary_protocol/src/consensus`](https://github.com/apache/iggy/tree/master/core/binary_protocol/src/consensus)
 | Fixed-size VSR headers and replica messages |
+| Simulator | 
[`core/simulator`](https://github.com/apache/iggy/tree/master/core/simulator) | 
Deterministic failures, delays, and network partitions |
+| Rust SDK | [`core/sdk`](https://github.com/apache/iggy/tree/master/core/sdk) 
| VSR framing, sessions, retries, and leader redirection |
 
-**Control Plane** (routed through shard 0):
-- Stream operations: CreateStream, UpdateStream, DeleteStream, PurgeStream
-- Topic operations: CreateTopic, UpdateTopic, DeleteTopic, PurgeTopic
-- User operations: CreateUser, UpdateUser, DeleteUser, ChangePassword, 
UpdatePermissions
-- Other: CreatePartitions, DeletePartitions, CreateConsumerGroup, 
DeleteConsumerGroup, CreatePersonalAccessToken, DeletePersonalAccessToken
+## Replication model
 
-**Data Plane** (routed to owning shard):
-- SendMessages, StoreConsumerOffset, DeleteSegments
+Iggy splits replication by namespace:
 
-- **Control Plane (`IggyMetadata`)** - metadata operations replicated through 
shard 0. These include creating/deleting streams, topics, partitions, users, 
consumer groups, and managing permissions and access tokens.
-- **Data Plane (`IggyPartitions`)** - partition operations replicated on all 
shards via namespaced pipelines. These include sending messages and storing 
consumer offsets.
+| Plane | Consensus group | Replicated work |
+| --- | --- | --- |
+| Metadata | One group on shard 0 | Streams, topics, users, permissions, 
consumer groups, and access tokens |
+| Partition | One group per partition | Messages and consumer offsets |
 
-## Core concepts
+Each group can have a different primary. Metadata writes route through the 
metadata primary. Partition writes route through the primary for that partition.
 
-### Replica state
+Reads use the local replicated state where possible. Writes return after the 
required VSR commit or return a retryable error when the replica is changing 
view or catching up.
 
-Each replica tracks the following state:
+## Failure handling
 
-| Field | Type | Description |
-|-------|------|-------------|
-| `replica_id` | u8 | Unique identifier for this replica |
-| `view` | u32 | Current view number (incremented on leader change) |
-| `log_view` | u32 | View when status was last normal |
-| `op` | u64 | Operation counter (monotonically increasing) |
-| `commit` | u64 | Latest committed operation number |
-| `status` | enum | Normal, ViewChange, or Recovering |
+VSR uses three main flows:
 
-### Message types
+1. **Normal operation**: the primary assigns an operation number, sends 
`Prepare`, and commits after a quorum sends `PrepareOk`.
+2. **View change**: replicas exchange `StartViewChange` and `DoViewChange`, 
then the new primary sends `StartView`.
+3. **Recovery**: a restarted or lagging replica requests the current view and 
repairs missing WAL ranges before serving current state.
 
-The consensus protocol uses the following message types:
+A cluster of `2f + 1` replicas tolerates `f` unavailable replicas. Use at 
least three replicas for one-node fault tolerance. A two-node cluster is useful 
for development, but it cannot make progress after either node fails.
 
-| Message | Direction | Purpose |
-|---------|-----------|---------|
-| `Ping` / `Pong` | Bidirectional | Health checking between replicas |
-| `PingClient` / `PongClient` | Bidirectional | Health checking between client 
and replica |
-| `Request` | Client -> Primary | Client submitting an operation |
-| `Prepare` | Primary -> Backups | Replicate operation to backups |
-| `PrepareOk` | Backup -> Primary | Acknowledge replication |
-| `Reply` | Primary -> Client | Operation result |
-| `Commit` | Primary -> Backups | Confirm operation is committed |
-| `StartViewChange` | Any -> All | Initiate view change |
-| `DoViewChange` | Any -> New Primary | Transfer state for view change |
-| `StartView` | New Primary -> All | Announce new view |
+## Build from source
 
-### Consensus header
+The VSR wire path is behind the `vsr` Cargo feature. Build the server and CLI 
from the repository root:
 
-Each consensus message carries a 256-byte header (`#[repr(C)]`, zero-copy via 
`bytemuck`):
-
-| Field | Size | Description |
-|-------|------|-------------|
-| checksum | 16 bytes | Message integrity (u128) |
-| checksum_body | 16 bytes | Body integrity (u128) |
-| cluster | 16 bytes | Cluster identifier (u128) |
-| size | 4 bytes | Message size |
-| view | 4 bytes | View number |
-| release | 4 bytes | Software release version |
-| command | 1 byte | Message type |
-| replica | 1 byte | Sender replica ID |
-| reserved | remaining | Reserved for future use |
-
-### Quorum
+```bash
+cargo build --bin iggy-server-ng --bin iggy --features vsr
+```
 
-Operations are committed once a **quorum** of replicas (majority) 
acknowledges. The quorum is tracked using a `BitSet` per operation, which 
efficiently tracks which replicas have acknowledged.
+The current binary names are:
 
-## Timeout management
+```text
+target/debug/iggy-server-ng
+target/debug/iggy
+```
 
-The VSR implementation uses **deterministic tick-based** timeouts (10ms per 
tick) for all timing-sensitive operations:
+The server uses Linux `io_uring`. Use a recent Linux kernel and a sufficient 
locked-memory limit.
 
-| Timeout Type | Default Ticks | Purpose |
-|-------------|---------------|---------|
-| Ping | 100 (1s) | Health check interval |
-| Prepare | 25 (250ms) | Replication timeout |
-| CommitMessage | 50 (500ms) | Commit notification |
-| NormalHeartbeat | 500 (5s) | Leader heartbeat |
-| StartViewChange | 50 (500ms) | View change initiation |
-| ViewChangeStatus | 500 (5s) | View change monitoring |
-| DoViewChange | 50 (500ms) | View change vote |
-| RequestStartView | 100 (1s) | Request new view from primary |
+## Run one development node
 
-Timeouts support **exponential backoff** with PRNG-based jitter to avoid 
thundering herd effects during view changes.
+The default `core/server-ng/config.toml` has clustering disabled. Run it 
without `--replica-id`:
 
-## Deterministic simulation testing
+```bash
+IGGY_ROOT_USERNAME=iggy \
+IGGY_ROOT_PASSWORD=iggy \
+IGGY_CONFIG_PATH=core/server-ng/config.toml \
+cargo run --bin iggy-server-ng --features vsr
+```
 
-The `core/simulator/` crate provides a network simulator for testing the VSR 
protocol deterministically. It simulates network conditions (delays, drops, 
partitions) and allows replaying exact scenarios. This includes modules for: 
bus, client, network, packet, ready queue, and replica simulation.
+Do not pass `--replica-id` when `cluster.enabled = false`.
 
-## Cluster configuration
+## Run a three-node cluster
 
-Clustering is configured in the `[cluster]` section of `config.toml`:
+Start with a shared config. Every node must use the same cluster name and 
roster.
 
 ```toml
 [cluster]
+enabled = true
+name = "iggy-vsr-dev"
+
+[cluster.auth]
+enabled = true
+shared_secret = ""
+
+[cluster.tls]
 enabled = false
-name = "iggy-cluster"
+self_signed = false
+cert_file = ""
+key_file = ""
+ca_file = ""
 
-[cluster.node.current]
+[[cluster.nodes]]
 name = "iggy-node-1"
 ip = "127.0.0.1"
+replica_id = 0
+ports = { tcp = 8090, quic = 8080, http = 3000, websocket = 8093, tcp_replica 
= 9090 }
 
-[[cluster.node.others]]
+[[cluster.nodes]]
 name = "iggy-node-2"
-ip = "192.168.1.101"
-ports = { tcp = 8091, quic = 8081, http = 3001, websocket = 8093 }
+ip = "127.0.0.1"
+replica_id = 1
+ports = { tcp = 8091, quic = 8081, http = 3001, websocket = 8094, tcp_replica 
= 9091 }
 
-[[cluster.node.others]]
+[[cluster.nodes]]
 name = "iggy-node-3"
-ip = "192.168.1.102"
-ports = { tcp = 8092, quic = 8082, http = 3002, websocket = 8094 }
+ip = "127.0.0.1"
+replica_id = 2
+ports = { tcp = 8092, quic = 8082, http = 3002, websocket = 8095, tcp_replica 
= 9092 }
+```
+
+Save it as `/tmp/iggy-vsr.toml`. Export the settings shared by all three 
processes:
+
+```bash
+export IGGY_CONFIG_PATH=/tmp/iggy-vsr.toml
+export IGGY_ROOT_USERNAME=iggy
+export IGGY_ROOT_PASSWORD=iggy
+export IGGY_CLUSTER_AUTH_SHARED_SECRET="replace-with-at-least-32-random-bytes"
 ```
 
-All nodes in the same cluster must share the same `name` to prevent accidental 
cross-cluster communication. Each node's `ports` field is optional and defaults 
to the current node's configured ports.
+Start each replica in a separate terminal. Use a different data path for every 
process:
+
+```bash
+# Replica 0
+IGGY_SYSTEM_PATH=local_data/node-0 ./target/debug/iggy-server-ng --replica-id 0
+
+# Replica 1
+IGGY_SYSTEM_PATH=local_data/node-1 ./target/debug/iggy-server-ng --replica-id 1
+
+# Replica 2
+IGGY_SYSTEM_PATH=local_data/node-2 ./target/debug/iggy-server-ng --replica-id 2
+```
+
+The exported settings must be present in all three terminals.
+
+In cluster mode:
+
+- `--replica-id` is required and must match one `cluster.nodes` entry
+- `replica_id` values must be unique and contiguous from `0`
+- every enabled transport needs a port for every node
+- `tcp_replica` is reserved for replica traffic
+- use `advertised_address` when clients cannot reach the roster `ip`
+- use a different `system.path` for each process on the same host
+
+## Check cluster metadata
+
+The CLI must also be built with `vsr`:
+
+```bash
+./target/debug/iggy \
+  --tcp-server-address 127.0.0.1:8090 \
+  -u iggy \
+  -p iggy \
+  cluster list
+```
+
+The response lists the cluster name, nodes, client endpoints, and current 
roles.
+
+## Use the Rust SDK
+
+Until VSR is released on the normal SDK path, depend on the current Git 
repository and enable `vsr`:
+
+```toml
+[dependencies]
+iggy = { git = "https://github.com/apache/iggy";, features = ["vsr"] }
+tokio = { version = "1", features = ["full"] }
+```
+
+The public client API stays the same:
+
+```rust
+use iggy::prelude::*;
+
+#[tokio::main]
+async fn main() -> Result<(), IggyError> {
+    let client = IggyClientBuilder::from_connection_string(
+        "iggy://iggy:[email protected]:8090",
+    )?
+    .build()?;
+
+    client.connect().await?;
+
+    let cluster = client.get_cluster_metadata().await?;
+    println!("cluster: {}", cluster.name);
+
+    Ok(())
+}
+```
+
+The feature enables VSR framing and consensus session handling for TCP, QUIC, 
and WebSocket clients. A VSR-enabled client is not wire-compatible with the 
legacy server.
+
+The Rust SDK and CLI currently have the complete VSR feature path. Other 
language SDKs still use the legacy wire protocol.
+
+## Test the VSR path
+
+The repository has a dedicated Rust BDD lane:
+
+```bash
+cargo build --bin iggy-server-ng --bin iggy --features vsr
+./scripts/run-bdd-tests.sh --vsr rust
+```
+
+Run one feature group while developing:
+
+```bash
+./scripts/run-bdd-tests.sh --vsr rust basic_messaging
+./scripts/run-bdd-tests.sh --vsr rust leader_redirection
+```
+
+The VSR build and legacy build use the same output path for `iggy`. Rebuild 
the CLI without `vsr` before returning to legacy-server tests.
+
+## Cluster security
+
+Replica authentication uses a shared secret and a BLAKE3 keyed handshake. Set 
the secret through `IGGY_CLUSTER_AUTH_SHARED_SECRET` instead of storing it in 
TOML.
+
+Replica TLS protects the `tcp_replica` connection:
+
+```toml
+[cluster.auth]
+enabled = true
+
+[cluster.tls]
+enabled = true
+self_signed = false
+cert_file = "/etc/iggy/tls/node.crt"
+key_file = "/etc/iggy/tls/node.key"
+ca_file = "/etc/iggy/tls/ca.crt"
+```
+
+Enable authentication and TLS on every node in one coordinated restart.
+
+## Default consensus timing
+
+The defaults are configurable in `[cluster]`:
+
+| Setting | Default | Purpose |
+| --- | --- | --- |
+| `heartbeat_timeout` | `5s` | Start a view change after primary silence |
+| `commit_broadcast_interval` | `500ms` | Broadcast the primary commit point |
+| `prepare_retransmit_interval` | `250ms` | Retry unacknowledged prepares |
+| `view_change_retransmit_interval` | `500ms` | Retry view-change messages |
+| `view_change_status_timeout` | `5s` | Restart a stalled view change |
+| `request_start_view_retransmit_interval` | `1s` | Probe the current primary 
during recovery |
+| `repair_retry_interval` | `1s` | Retry a stalled WAL repair |
+| `repair_chunk_max` | `128` | Limit prepares served per repair round |
+
+Keep the defaults unless tests show a specific scheduling or network problem.
+
+## Path to v1
+
+`server-ng` is a temporary development name. The plan for Iggy v1 is:
+
+1. finish and stabilize the VSR server
+2. make it the only server implementation
+3. remove the current `core/server`
+4. rename `core/server-ng` to `core/server`
+5. rename `iggy-server-ng` to `iggy-server`
+
+Expect the paths and binary names on this page to change when that transition 
lands.
 
-## Current status
+## Follow development
 
-The VSR implementation includes the core consensus protocol, view change 
mechanism, deterministic timeout management, quorum tracking, shard-level plane 
multiplexing, and a network simulator for testing. Clustering is not yet 
production-ready but the foundational building blocks are in place and being 
actively developed. The server can be started as a follower node using the 
`--follower` flag.
+- [Current `server-ng` 
source](https://github.com/apache/iggy/tree/master/core/server-ng)
+- [All Apache Iggy pull requests](https://github.com/apache/iggy/pulls)
+- [Configuration hardening](https://github.com/apache/iggy/pull/3756)
+- [Session resume across restarts](https://github.com/apache/iggy/pull/3753)
+- [Follower HTTP forwarding](https://github.com/apache/iggy/pull/3744)
+- [Rust BDD coverage for VSR](https://github.com/apache/iggy/pull/3732)
+- [VSR-enabled CLI](https://github.com/apache/iggy/pull/3679)
diff --git a/src/app/(home)/page.tsx b/src/app/(home)/page.tsx
index b88eeaa8..d05f0178 100644
--- a/src/app/(home)/page.tsx
+++ b/src/app/(home)/page.tsx
@@ -24,7 +24,7 @@ import { LandingCodeTabs } from "@/components/code-tabs";
 import { BenchmarkSection } from "@/components/benchmark-chart";
 
 export const metadata: Metadata = {
-  title: "Apache Iggy (Incubating) — Hyper-Efficient Message Streaming",
+  title: "Apache Iggy (Incubating) | Hyper-Efficient Message Streaming",
   description:
     "Apache Iggy (Incubating) is a high-performance, persistent message 
streaming platform written in Rust, capable of processing millions of messages 
per second with ultra-low latency.",
 };
@@ -41,7 +41,7 @@ const heroStats = [
     gradient: "from-[#5f87fd] to-[#7d44e0]",
   },
   {
-    value: "6",
+    value: "7",
     label: "Language SDKs",
     gradient: "from-[#9d44e0] to-[#e55efa]",
   },
@@ -71,7 +71,7 @@ const features = [
   {
     title: "Multi-Language SDKs",
     description:
-      "Client libraries available for Rust, C#, Java, Go, Python, Node.js and 
C++ with more languages coming for best developer experience.",
+      "Client libraries available for Rust, C#, Java, Go, Python, Node.js and 
PHP, with C++ coming soon.",
   },
   {
     title: "Consumer Groups & Partitioning",
@@ -140,7 +140,7 @@ export default function HomePage() {
         }}
       >
         <div className="mx-auto max-w-6xl text-center">
-          <h1 className="mb-6 text-5xl leading-[1.05] font-bold tracking-tight 
text-[#fffaeb] md:text-7xl lg:text-[clamp(5rem,6vw,7rem)]">
+          <h1 className="mb-6 text-4xl leading-[1.05] font-bold tracking-tight 
text-[#fffaeb] sm:text-5xl md:text-7xl lg:text-[clamp(5rem,6vw,7rem)]">
             Hyper-Efficient
             <br />
             <span className="bg-gradient-to-r from-[#f9923f] via-[#5f87fd] 
to-[#fa5e8a] bg-clip-text text-transparent">
@@ -160,15 +160,20 @@ export default function HomePage() {
           </p>
 
           {/* Stats Grid */}
-          <div className="mx-auto mt-12 grid max-w-4xl grid-cols-2 gap-6 
md:grid-cols-4">
+          <div className="mx-auto mt-12 grid min-w-0 max-w-4xl grid-cols-2 
gap-4 sm:gap-6 md:grid-cols-4">
             {heroStats.map((stat) => (
-              <div key={stat.label} className="p-4 text-center">
+              <div
+                key={stat.label}
+                className="min-w-0 p-2 text-center sm:p-4"
+              >
                 <div
                   className={`bg-gradient-to-r ${stat.gradient} mb-2 
bg-clip-text text-3xl font-bold text-transparent md:text-4xl`}
                 >
                   {stat.value}
                 </div>
-                <p className="text-sm text-[#838d95]">{stat.label}</p>
+                <p className="break-words text-xs text-[#aeb5bd] sm:text-sm">
+                  {stat.label}
+                </p>
               </div>
             ))}
           </div>
@@ -201,7 +206,7 @@ export default function HomePage() {
 
       {/* Built for performance - with architecture visualization */}
       <section
-        className="px-6 py-20"
+        className="overflow-hidden px-6 py-20"
         style={{
           backgroundImage:
             "radial-gradient(circle farthest-side at 50% 10%, #0e1930, #070c17 
65%)",
@@ -209,10 +214,10 @@ export default function HomePage() {
       >
         <div className="mx-auto max-w-6xl">
           <div className="mx-auto mb-14 max-w-3xl text-center">
-            <h2 className="mb-5 text-4xl font-bold tracking-tight 
text-[#fffaeb] md:text-5xl">
+            <h2 className="mb-5 text-3xl font-bold tracking-tight 
text-[#fffaeb] sm:text-4xl md:text-5xl">
               Built for <span className="text-[#ff9103]">performance</span>
             </h2>
-            <p className="text-lg font-light leading-relaxed text-[#838d95]">
+            <p className="text-lg font-light leading-relaxed text-[#aeb5bd]">
               Designed from the ground up with{" "}
               <span className="text-[#fffaeb]">
                 io_uring and thread-per-core, shared nothing architecture
@@ -222,6 +227,61 @@ export default function HomePage() {
             </p>
           </div>
 
+          <div className="mx-auto mb-14 max-w-5xl border-y border-white/[0.1]">
+            <div className="grid gap-5 py-6 md:grid-cols-[auto_1fr_auto] 
md:items-center md:gap-7">
+              <span className="w-fit rounded-md border border-[#38bdf8]/30 
bg-[#38bdf8]/10 px-2.5 py-1 font-mono text-xs font-semibold uppercase 
text-[#7dd3fc]">
+                Experimental
+              </span>
+              <div className="min-w-0">
+                <h3 className="text-lg font-semibold text-[#fffaeb]">
+                  VSR clustering is coming soon
+                </h3>
+                <p className="mt-1 text-sm leading-relaxed text-[#aeb5bd]">
+                  Viewstamped Replication Revisited is already implemented in
+                  the{" "}
+                  <span className="font-mono 
text-[#d7dce1]">server-ng</span>{" "}
+                  module on main. Its deterministic simulation testing (DST)
+                  exercises failures, delays, restarts and network partitions 
to
+                  validate consensus. Together, VSR and DST provide the
+                  foundation for highly available, fault-tolerant and reliable
+                  Iggy clusters.
+                </p>
+              </div>
+              <Link
+                href="/docs/clustering/vsr"
+                className="text-sm font-semibold text-[#ff9f22] no-underline 
hover:underline"
+              >
+                Explore VSR →
+              </Link>
+            </div>
+
+            <div className="grid gap-5 border-t border-white/[0.1] py-6 
md:grid-cols-[auto_1fr_auto] md:items-center md:gap-7">
+              <span className="w-fit rounded-md border border-[#ff9103]/30 
bg-[#ff9103]/10 px-2.5 py-1 font-mono text-xs font-semibold uppercase 
text-[#ffb454]">
+                In development
+              </span>
+              <div className="min-w-0">
+                <h3 className="text-lg font-semibold text-[#fffaeb]">
+                  Kafka Gateway for compatible clients and easier migrations
+                </h3>
+                <p className="mt-1 text-sm leading-relaxed text-[#aeb5bd]">
+                  The upcoming Kafka wire-protocol proxy will bridge existing
+                  Kafka producers and consumers to Iggy, enabling gradual
+                  migration with minimal client-side changes. Core APIs,
+                  consumer groups, admin operations and authentication are
+                  tracked in a phased public roadmap.
+                </p>
+              </div>
+              <Link
+                href="https://github.com/apache/iggy/discussions/3253";
+                target="_blank"
+                rel="noopener noreferrer"
+                className="text-sm font-semibold text-[#ff9f22] no-underline 
hover:underline"
+              >
+                Track the epic ↗
+              </Link>
+            </div>
+          </div>
+
           {/* Architecture visualization */}
           <div className="mb-14">
             <BenchmarkSection />
@@ -236,7 +296,7 @@ export default function HomePage() {
                 <h3 className="mb-3 text-lg font-semibold text-[#d6d7d7] 
transition-colors group-hover:text-[#fffaeb]">
                   {feature.title}
                 </h3>
-                <p className="text-sm font-light leading-relaxed 
text-[#838d95]">
+                <p className="text-sm font-light leading-relaxed 
text-[#aeb5bd]">
                   {feature.description}
                 </p>
               </div>
@@ -258,7 +318,7 @@ export default function HomePage() {
             <h2 className="mb-5 text-4xl font-bold tracking-tight 
text-[#fffaeb] md:text-5xl">
               How it <span className="text-[#ff9103]">works</span>
             </h2>
-            <p className="text-lg font-light leading-relaxed text-[#838d95]">
+            <p className="text-lg font-light leading-relaxed text-[#aeb5bd]">
               Messages flow from producers through streams and topics into
               partitioned, append-only segment files on disk. Pick your 
language
               and start streaming in minutes.
@@ -266,7 +326,7 @@ export default function HomePage() {
           </div>
 
           <div className="grid gap-6 md:grid-cols-2 items-start">
-            <div className="space-y-4">
+            <div className="min-w-0 space-y-4">
               {[
                 {
                   step: "1",
@@ -296,11 +356,11 @@ export default function HomePage() {
                   <div className="flex h-8 w-8 shrink-0 items-center 
justify-center rounded-lg bg-[#ff9103]/10 text-[#ff9103] text-sm font-bold">
                     {item.step}
                   </div>
-                  <div>
+                  <div className="min-w-0">
                     <h4 className="text-base font-semibold text-[#fffaeb] 
mb-1">
                       {item.title}
                     </h4>
-                    <p className="text-sm font-light leading-relaxed 
text-[#838d95] m-0">
+                    <p className="text-sm font-light leading-relaxed 
text-[#aeb5bd] m-0">
                       {item.desc}
                     </p>
                   </div>
@@ -326,7 +386,7 @@ export default function HomePage() {
             <h2 className="mb-5 text-4xl font-bold tracking-tight 
text-[#fffaeb] md:text-5xl">
               Complete <span className="text-[#ff9103]">ecosystem</span>
             </h2>
-            <p className="text-lg font-light leading-relaxed text-[#838d95]">
+            <p className="text-lg font-light leading-relaxed text-[#aeb5bd]">
               Iggy is more than a server. Integrate with external systems, 
manage
               everything from your browser or terminal, and connect LLMs to 
your
               streaming infrastructure.
@@ -349,7 +409,7 @@ export default function HomePage() {
                     {item.title}
                   </h3>
                 </div>
-                <p className="text-sm font-light leading-relaxed 
text-[#838d95] mb-4">
+                <p className="text-sm font-light leading-relaxed 
text-[#aeb5bd] mb-4">
                   {item.description}
                 </p>
                 <div className="flex flex-wrap gap-1.5">
@@ -383,7 +443,7 @@ export default function HomePage() {
                 <li>
                   <Link
                     href="/docs"
-                    className="text-[#838d95] transition-colors 
hover:text-[#fffaeb]"
+                    className="text-[#aeb5bd] transition-colors 
hover:text-[#fffaeb]"
                   >
                     Documentation
                   </Link>
@@ -398,7 +458,7 @@ export default function HomePage() {
                 <li>
                   <Link
                     href="https://www.linkedin.com/company/apache-iggy/";
-                    className="text-[#838d95] transition-colors 
hover:text-[#fffaeb]"
+                    className="text-[#aeb5bd] transition-colors 
hover:text-[#fffaeb]"
                   >
                     LinkedIn
                   </Link>
@@ -406,7 +466,7 @@ export default function HomePage() {
                 <li>
                   <Link
                     href="https://discord.gg/apache-iggy";
-                    className="text-[#838d95] transition-colors 
hover:text-[#fffaeb]"
+                    className="text-[#aeb5bd] transition-colors 
hover:text-[#fffaeb]"
                   >
                     Discord
                   </Link>
@@ -414,7 +474,7 @@ export default function HomePage() {
                 <li>
                   <Link
                     href="https://x.com/ApacheIggy";
-                    className="text-[#838d95] transition-colors 
hover:text-[#fffaeb]"
+                    className="text-[#aeb5bd] transition-colors 
hover:text-[#fffaeb]"
                   >
                     X
                   </Link>
@@ -422,7 +482,7 @@ export default function HomePage() {
                 <li>
                   <Link
                     href="https://bsky.app/profile/iggy.rs";
-                    className="text-[#838d95] transition-colors 
hover:text-[#fffaeb]"
+                    className="text-[#aeb5bd] transition-colors 
hover:text-[#fffaeb]"
                   >
                     Bluesky
                   </Link>
@@ -437,7 +497,7 @@ export default function HomePage() {
                 <li>
                   <Link
                     href="/blogs"
-                    className="text-[#838d95] transition-colors 
hover:text-[#fffaeb]"
+                    className="text-[#aeb5bd] transition-colors 
hover:text-[#fffaeb]"
                   >
                     Blogs
                   </Link>
@@ -445,7 +505,7 @@ export default function HomePage() {
                 <li>
                   <Link
                     href="https://github.com/apache/iggy";
-                    className="text-[#838d95] transition-colors 
hover:text-[#fffaeb]"
+                    className="text-[#aeb5bd] transition-colors 
hover:text-[#fffaeb]"
                   >
                     GitHub
                   </Link>
@@ -453,7 +513,7 @@ export default function HomePage() {
                 <li>
                   <Link
                     href="https://benchmarks.iggy.apache.org";
-                    className="text-[#838d95] transition-colors 
hover:text-[#fffaeb]"
+                    className="text-[#aeb5bd] transition-colors 
hover:text-[#fffaeb]"
                   >
                     Benchmarks
                   </Link>
diff --git a/src/components/benchmark-chart.tsx 
b/src/components/benchmark-chart.tsx
index 813ec808..6e29b9ce 100644
--- a/src/components/benchmark-chart.tsx
+++ b/src/components/benchmark-chart.tsx
@@ -1,151 +1,389 @@
 "use client";
 
-import { useEffect, useRef, useState } from "react";
 import Link from "next/link";
+import { useEffect, useMemo, useRef, useState } from "react";
 
-const WRITE_PATH =
-  "M0,128 L8,155 L16,161 L24,130 L32,125 L40,153 L48,165 L56,129 L64,154 
L72,150 L80,118 L88,166 L96,131 L104,128 L112,157 L120,130 L128,131 L136,160 
L144,132 L152,135 L160,166 L168,134 L176,137 L184,164 L192,122 L200,146 
L208,148 L216,131 L224,130 L232,162 L240,126 L248,124 L256,163 L264,129 
L272,135 L280,166 L288,131 L296,128 L304,163 L312,162 L320,131 L328,166 
L336,133 L344,152 L352,158 L360,131 L368,128 L376,163 L384,120 L392,157 
L400,164 L408,132 L416,115 L424,152 L432,125 L440,114  [...]
+const PRODUCER_BENCHMARK_URL =
+  
"https://benchmarks.iggy.apache.org/benchmarks/4bc63b0e-f0fb-44b5-8c42-6159603a5653";;
+const CONSUMER_BENCHMARK_URL =
+  
"https://benchmarks.iggy.apache.org/benchmarks/6ed70d0a-de98-42da-84a9-16655152d4e8";;
 
-const READ_PATH =
-  "M0,151 L8,154 L16,155 L24,154 L32,156 L40,154 L48,155 L56,152 L64,154 
L72,154 L80,152 L88,154 L96,153 L104,154 L112,152 L120,156 L128,153 L136,154 
L144,153 L152,156 L160,152 L168,153 L176,153 L184,156 L192,155 L200,154 
L208,155 L216,153 L224,154 L232,153 L240,154 L248,152 L256,155 L264,152 
L272,152 L280,155 L288,154 L296,155 L304,154 L312,153 L320,155 L328,155 
L336,152 L344,152 L352,156 L360,154 L368,157 L376,156 L384,157 L392,156 
L400,152 L408,153 L416,155 L424,157 L432,155 L440,157  [...]
+const Y_MAX_MS = 1;
+const Y_TOP_PAD = 20;
+const Y_TICKS = [0, 0.2, 0.4, 0.6, 0.8, 1];
+const X_MAX = 800;
+const SAMPLES = 160;
 
-const CY = (ms: number) => 200 - ms * 60;
-const yTicks = [0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0];
+const PRODUCER_AVG_MS = 0.466;
+const CONSUMER_AVG_MS = 0.357;
+
+const latencyRows = [
+  { label: "Avg", producer: "0.466", consumer: "0.357" },
+  { label: "Median", producer: "0.349", consumer: "0.351" },
+  { label: "P95", producer: "0.886", consumer: "0.446" },
+  { label: "P99", producer: "0.976", consumer: "0.495" },
+  { label: "P99.9", producer: "1.114", consumer: "0.566" },
+];
+
+const stats = [
+  {
+    value: "2M+",
+    unit: "msg/s",
+    label: "Throughput",
+    detail: "Single node",
+  },
+  {
+    value: "1",
+    unit: "GB/s",
+    label: "Producer throughput",
+    detail: "Persisted writes",
+  },
+  {
+    value: "2",
+    unit: "GB/s",
+    label: "Consumer throughput",
+    detail: "Persistent log reads",
+  },
+  {
+    value: "0.976",
+    unit: "ms",
+    label: "Producer P99",
+    detail: "0.466 ms average",
+  },
+  {
+    value: "0.495",
+    unit: "ms",
+    label: "Consumer P99",
+    detail: "0.357 ms average",
+  },
+];
+
+const CY = (ms: number) =>
+  Y_TOP_PAD +
+  (1 - Math.min(ms, Y_MAX_MS) / Y_MAX_MS) * (200 - Y_TOP_PAD);
+
+function mulberry32(seed: number) {
+  let value = seed >>> 0;
+
+  return () => {
+    value = (value + 0x6d2b79f5) >>> 0;
+    let next = value;
+    next = Math.imul(next ^ (next >>> 15), next | 1);
+    next ^= next + Math.imul(next ^ (next >>> 7), next | 61);
+    return ((next ^ (next >>> 14)) >>> 0) / 4294967296;
+  };
+}
+
+type Trace = {
+  line: string;
+  area: string;
+};
+
+function buildTrace(
+  seed: number,
+  centerMs: number,
+  jitterMs: number,
+  spikeMs: number,
+): Trace {
+  const random = mulberry32(seed);
+  const points: string[] = [];
+
+  for (let index = 0; index < SAMPLES; index += 1) {
+    const x = (index / (SAMPLES - 1)) * X_MAX;
+    const wander =
+      Math.sin(index * 0.18 + seed * 0.31) * jitterMs * 0.55 +
+      Math.sin(index * 0.62 + seed * 0.11) * jitterMs * 0.4;
+    const noise = (random() - 0.5) * jitterMs * 0.6;
+    const spike = random() > 0.93 ? random() * spikeMs : 0;
+    const ms = Math.max(0, centerMs + wander + noise + spike);
+    points.push(`${x.toFixed(1)},${CY(ms).toFixed(1)}`);
+  }
+
+  return {
+    line: `M${points.join(" L")}`,
+    area: `M${points.join(" L")} L${X_MAX},200 L0,200 Z`,
+  };
+}
 
 export function BenchmarkSection() {
   const ref = useRef<HTMLDivElement>(null);
   const [visible, setVisible] = useState(false);
+  const { producer, consumer } = useMemo(
+    () => ({
+      producer: buildTrace(11, PRODUCER_AVG_MS, 0.09, 0.4),
+      consumer: buildTrace(1, CONSUMER_AVG_MS, 0.04, 0.1),
+    }),
+    [],
+  );
 
   useEffect(() => {
-    const el = ref.current;
-    if (!el) return;
+    const element = ref.current;
+    if (!element) return;
+
     if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
       setVisible(true);
       return;
     }
+
     const observer = new IntersectionObserver(
-      ([entry]) => { if (entry.isIntersecting) { setVisible(true); 
observer.unobserve(el); } },
+      ([entry]) => {
+        if (entry.isIntersecting) {
+          setVisible(true);
+          observer.unobserve(element);
+        }
+      },
       { threshold: 0.15 },
     );
-    observer.observe(el);
+
+    observer.observe(element);
     return () => observer.disconnect();
   }, []);
 
-  const wrAvg = CY(1.01);
-  const wrP99 = CY(2.05);
-  const rdAvg = CY(1.19);
-
   return (
-    <div ref={ref}>
+    <div ref={ref} className="min-w-0 max-w-full">
       <style>{`
-        @keyframes iggy-draw { from { stroke-dashoffset: 2400 } to { 
stroke-dashoffset: 0 } }
-        @keyframes iggy-area-in { from { opacity: 0 } to { opacity: 1 } }
-        @keyframes iggy-label-in { from { opacity: 0; transform: 
translateX(8px) } to { opacity: 1; transform: translateX(0) } }
-        .iggy-line { stroke-dasharray: 2400; stroke-dashoffset: 2400 }
-        .iggy-area-wr, .iggy-area-rd { opacity: 0 }
-        .iggy-label { opacity: 0 }
-        .iggy-go .iggy-line { animation: iggy-draw 2s 
cubic-bezier(0.4,0,0.2,1) forwards }
-        .iggy-go .iggy-line-rd { animation-delay: 0.3s }
-        .iggy-go .iggy-area-wr { animation: iggy-area-in 0.8s ease 1s forwards 
}
-        .iggy-go .iggy-area-rd { animation: iggy-area-in 0.8s ease 1.3s 
forwards }
-        .iggy-go .iggy-label { animation: iggy-label-in 0.4s ease 2s forwards }
+        @keyframes iggy-chart-reveal {
+          from { width: 0; }
+          to { width: 800px; }
+        }
+        @keyframes iggy-reference-in {
+          from { opacity: 0; }
+          to { opacity: 1; }
+        }
+        .iggy-chart-clip {
+          width: 0;
+        }
+        .iggy-chart-visible .iggy-chart-clip {
+          animation: iggy-chart-reveal 2.4s cubic-bezier(0.16, 1, 0.3, 1) 
forwards;
+        }
+        .iggy-chart-reference {
+          opacity: 0;
+        }
+        .iggy-chart-visible .iggy-chart-reference {
+          animation: iggy-reference-in 0.5s ease 2.1s forwards;
+        }
       `}</style>
 
-      <div className="mb-6 grid grid-cols-2 md:grid-cols-5 gap-4">
-        {[
-          { value: "1M+", unit: "msg/s", label: "Throughput", sub: null },
-          { value: "+1 GB/s", unit: "write", label: "Producer throughput", 
sub: null },
-          { value: "+3 GB/s", unit: "read", label: "Consumer throughput", sub: 
null },
-          { value: "1.01", unit: "ms", label: "Avg write latency", sub: null },
-          { value: "2.05", unit: "ms", label: "P99 write latency", sub: null },
-        ].map((s) => (
-          <div key={s.label} className="rounded-xl border border-white/[0.06] 
bg-white/[0.03] px-5 py-5">
+      <div className="mb-6 grid min-w-0 grid-cols-2 gap-4 md:grid-cols-5">
+        {stats.map((stat) => (
+          <div
+            key={stat.label}
+            className="min-w-0 rounded-lg border border-white/[0.08] 
bg-white/[0.035] px-4 py-5 sm:px-5"
+          >
             <div className="mb-1 flex items-baseline gap-1.5">
-              <span className="text-3xl font-extrabold 
text-white">{s.value}</span>
-              <span className="text-sm font-medium 
text-neutral-400">{s.unit}</span>
+              <span className="text-2xl font-extrabold text-white sm:text-3xl">
+                {stat.value}
+              </span>
+              <span className="text-xs font-medium text-neutral-300 
sm:text-sm">
+                {stat.unit}
+              </span>
             </div>
-            <div className="text-sm text-neutral-500">{s.label}</div>
-            {s.sub && <div className="mt-1 text-xs 
text-neutral-600">{s.sub}</div>}
+            <div className="text-sm text-neutral-300">{stat.label}</div>
+            <div className="mt-1 text-xs text-neutral-400">{stat.detail}</div>
           </div>
         ))}
       </div>
 
-      <div className="rounded-xl border border-white/[0.06] bg-[#060a12] 
overflow-hidden">
-        <div className="flex items-center justify-between border-b 
border-white/[0.04] px-5 py-3">
-          <div className="flex items-center gap-6">
-            <div className="flex items-center gap-2">
-              <div className="h-[3px] w-5 rounded-full bg-[#ff9103]" />
-              <span className="text-xs text-neutral-400">Write</span>
-            </div>
-            <div className="flex items-center gap-2">
-              <div className="h-[3px] w-5 rounded-full bg-[#38bdf8]" />
-              <span className="text-xs text-neutral-400">Read</span>
+      <div className="grid min-w-0 max-w-full gap-4 
lg:grid-cols-[minmax(0,1fr)_280px]">
+        <div className="min-w-0 max-w-full overflow-hidden rounded-lg border 
border-white/[0.08] bg-[#060a12]">
+          <div className="flex flex-col gap-2 border-b border-white/[0.06] 
px-5 py-3 sm:flex-row sm:items-center sm:justify-between">
+            <div className="flex flex-wrap items-center gap-x-5 gap-y-1">
+              <div className="flex items-center gap-2">
+                <div className="h-[3px] w-5 rounded-full bg-[#ff9103]" />
+                <span className="text-xs text-neutral-300">
+                  Producer{" "}
+                  <span className="text-neutral-100">0.466 ms avg</span>
+                </span>
+              </div>
+              <div className="flex items-center gap-2">
+                <div className="h-[3px] w-5 rounded-full bg-[#38bdf8]" />
+                <span className="text-xs text-neutral-300">
+                  Consumer{" "}
+                  <span className="text-neutral-100">0.357 ms avg</span>
+                </span>
+              </div>
             </div>
-          </div>
-          <span className="text-xs text-neutral-600">Latency (ms) · 40M 
messages</span>
-        </div>
-
-        <div className="flex">
-          <div className="flex w-10 shrink-0 flex-col justify-between py-3 
pr-1 text-right font-mono text-[9px] text-neutral-600 sm:w-12">
-            {[...yTicks].reverse().map((ms) => (
-              <div key={ms} className="leading-none">{ms.toFixed(1)}</div>
-            ))}
+            <span className="font-mono text-xs text-neutral-400">
+              Apache Iggy 0.8.0 · 40M messages
+            </span>
           </div>
 
-          <div className="min-w-0 flex-1 py-3 pr-3">
-            <svg
-              viewBox="0 0 600 200"
-              className={visible ? "iggy-go" : ""}
-              preserveAspectRatio="none"
-              style={{ width: "100%", height: "auto", aspectRatio: "600 / 200" 
}}
-            >
-              <defs>
-                <linearGradient id="iggy-wr-g" x1="0" y1="0" x2="0" y2="1">
-                  <stop offset="0%" stopColor="#ff9103" stopOpacity="0.15" />
-                  <stop offset="100%" stopColor="#ff9103" stopOpacity="0" />
-                </linearGradient>
-                <linearGradient id="iggy-rd-g" x1="0" y1="0" x2="0" y2="1">
-                  <stop offset="0%" stopColor="#38bdf8" stopOpacity="0.08" />
-                  <stop offset="100%" stopColor="#38bdf8" stopOpacity="0" />
-                </linearGradient>
-              </defs>
-
-              {yTicks.map((ms) => (
-                <line key={ms} x1="0" y1={CY(ms)} x2="600" y2={CY(ms)} 
stroke="white" strokeOpacity="0.03" />
+          <div className="flex">
+            <div className="flex w-12 shrink-0 flex-col justify-between py-4 
pr-2 text-right font-mono text-[10px] text-neutral-400 sm:w-14 sm:text-xs">
+              <div className="text-neutral-300">ms</div>
+              {[...Y_TICKS].reverse().map((ms) => (
+                <div key={ms} className="leading-none">
+                  {ms.toFixed(1)}
+                </div>
               ))}
+            </div>
+
+            <div className="min-w-0 flex-1 pt-4 pr-3 pb-3">
+              <svg
+                viewBox="0 0 800 200"
+                className={visible ? "iggy-chart-visible w-full" : "w-full"}
+                preserveAspectRatio="none"
+                style={{ aspectRatio: "800 / 200" }}
+                role="img"
+                aria-label="Producer and consumer latency traces for the 
Apache Iggy 0.8.0 benchmark"
+              >
+                <defs>
+                  <linearGradient
+                    id="iggy-producer-fill"
+                    x1="0"
+                    y1="0"
+                    x2="0"
+                    y2="1"
+                  >
+                    <stop
+                      offset="0%"
+                      stopColor="#ff9103"
+                      stopOpacity="0.2"
+                    />
+                    <stop
+                      offset="100%"
+                      stopColor="#ff9103"
+                      stopOpacity="0"
+                    />
+                  </linearGradient>
+                  <linearGradient
+                    id="iggy-consumer-fill"
+                    x1="0"
+                    y1="0"
+                    x2="0"
+                    y2="1"
+                  >
+                    <stop
+                      offset="0%"
+                      stopColor="#38bdf8"
+                      stopOpacity="0.14"
+                    />
+                    <stop
+                      offset="100%"
+                      stopColor="#38bdf8"
+                      stopOpacity="0"
+                    />
+                  </linearGradient>
+                  <clipPath id="iggy-chart-reveal">
+                    <rect
+                      className="iggy-chart-clip"
+                      x="0"
+                      y="0"
+                      height="200"
+                    />
+                  </clipPath>
+                </defs>
 
-              <path d={WRITE_PATH + " L600,200 L0,200 Z"} 
fill="url(#iggy-wr-g)" className="iggy-area-wr" />
-              <path d={WRITE_PATH} fill="none" stroke="#ff9103" 
strokeWidth="2" strokeLinejoin="round" className="iggy-line" />
+                {Y_TICKS.map((ms) => (
+                  <line
+                    key={ms}
+                    x1="0"
+                    y1={CY(ms)}
+                    x2={X_MAX}
+                    y2={CY(ms)}
+                    stroke="white"
+                    strokeOpacity="0.05"
+                  />
+                ))}
 
-              <path d={READ_PATH + " L600,200 L0,200 Z"} 
fill="url(#iggy-rd-g)" className="iggy-area-rd" />
-              <path d={READ_PATH} fill="none" stroke="#38bdf8" 
strokeWidth="1.5" strokeLinejoin="round" className="iggy-line iggy-line-rd" />
+                <g clipPath="url(#iggy-chart-reveal)">
+                  <path d={producer.area} fill="url(#iggy-producer-fill)" />
+                  <path
+                    d={producer.line}
+                    fill="none"
+                    stroke="#ff9103"
+                    strokeWidth="1.8"
+                    strokeLinejoin="round"
+                    strokeLinecap="round"
+                  />
+                  <path d={consumer.area} fill="url(#iggy-consumer-fill)" />
+                  <path
+                    d={consumer.line}
+                    fill="none"
+                    stroke="#38bdf8"
+                    strokeWidth="1.5"
+                    strokeLinejoin="round"
+                    strokeLinecap="round"
+                  />
+                </g>
 
-              <g className="iggy-label">
-                <line x1="0" y1={wrAvg} x2="600" y2={wrAvg} stroke="#ff9103" 
strokeOpacity="0.3" strokeDasharray="6 6" strokeWidth="1" />
-                <rect x="539" y={wrAvg - 11} width="58" height="22" rx="4" 
fill="#060a12" />
-                <rect x="539" y={wrAvg - 11} width="58" height="22" rx="4" 
fill="#ff9103" fillOpacity="0.15" stroke="#ff9103" strokeOpacity="0.4" 
strokeWidth="0.5" />
-                <text x="568" y={wrAvg + 4} textAnchor="middle" fill="#ff9103" 
fontSize="11" fontFamily="monospace" fontWeight="bold">AVG</text>
+                <g className="iggy-chart-reference">
+                  <line
+                    x1="0"
+                    y1={CY(PRODUCER_AVG_MS)}
+                    x2={X_MAX}
+                    y2={CY(PRODUCER_AVG_MS)}
+                    stroke="#ff9103"
+                    strokeOpacity="0.3"
+                    strokeDasharray="6 6"
+                  />
+                  <line
+                    x1="0"
+                    y1={CY(CONSUMER_AVG_MS)}
+                    x2={X_MAX}
+                    y2={CY(CONSUMER_AVG_MS)}
+                    stroke="#38bdf8"
+                    strokeOpacity="0.3"
+                    strokeDasharray="6 6"
+                  />
+                </g>
+              </svg>
+            </div>
+          </div>
+        </div>
 
-                <line x1="0" y1={wrP99} x2="600" y2={wrP99} stroke="#ff9103" 
strokeOpacity="0.2" strokeDasharray="6 6" strokeWidth="1" />
-                <rect x="539" y={wrP99 - 11} width="58" height="22" rx="4" 
fill="#060a12" />
-                <rect x="539" y={wrP99 - 11} width="58" height="22" rx="4" 
fill="#ff9103" fillOpacity="0.1" stroke="#ff9103" strokeOpacity="0.3" 
strokeWidth="0.5" />
-                <text x="568" y={wrP99 + 4} textAnchor="middle" fill="#ff9103" 
fontSize="11" fontFamily="monospace" fontWeight="bold">P99</text>
-              </g>
-            </svg>
+        <div className="min-w-0 max-w-full overflow-hidden rounded-lg border 
border-white/[0.08] bg-white/[0.035] p-4 sm:p-5">
+          <div className="mb-4 font-mono text-sm text-neutral-300">
+            Latency breakdown <span className="text-neutral-400">(ms)</span>
           </div>
+          <table className="w-full table-fixed font-mono text-xs sm:text-sm">
+            <thead>
+              <tr className="text-neutral-400">
+                <th className="w-[36%] pb-3 text-left font-normal">
+                  Percentile
+                </th>
+                <th className="pb-3 text-right font-normal">Producer</th>
+                <th className="pb-3 text-right font-normal">Consumer</th>
+              </tr>
+            </thead>
+            <tbody>
+              {latencyRows.map((row) => (
+                <tr key={row.label} className="border-t border-white/[0.06]">
+                  <td className="py-2.5 text-neutral-300">{row.label}</td>
+                  <td className="py-2.5 text-right text-white">
+                    {row.producer}
+                  </td>
+                  <td className="py-2.5 text-right text-white">
+                    {row.consumer}
+                  </td>
+                </tr>
+              ))}
+            </tbody>
+          </table>
         </div>
       </div>
 
-      <div className="mt-4 flex flex-wrap items-center justify-between gap-3 
rounded-xl border border-white/[0.06] bg-white/[0.03] px-5 py-3.5">
-        <span className="font-mono text-sm text-neutral-500">
-          <span className="text-neutral-400">Machine:</span> AWS i3en.3xlarge 
· Intel Xeon 8259CL @ 2.50GHz
+      <div className="mt-4 flex flex-wrap items-center justify-between gap-3 
border-y border-white/[0.08] px-1 py-4">
+        <span className="min-w-0 break-words font-mono text-sm 
text-neutral-300">
+          <span className="text-neutral-200">Machine:</span> AWS i4i.4xlarge ·
+          persistent log workload
         </span>
-        <div className="flex gap-5">
-          <Link 
href="https://benchmarks.iggy.apache.org/benchmarks/2c6a0f6a-fb4d-4e84-8ac0-bfca60c75b21";
 target="_blank" className="font-mono text-sm text-[#ff9103] no-underline 
hover:underline">
-            Producer →
+        <div className="flex flex-wrap gap-5">
+          <Link
+            href={PRODUCER_BENCHMARK_URL}
+            target="_blank"
+            rel="noopener noreferrer"
+            className="font-mono text-sm text-[#ff9f22] no-underline 
hover:underline"
+          >
+            Producer result →
           </Link>
-          <Link 
href="https://benchmarks.iggy.apache.org/benchmarks/63607acc-5861-47c7-9673-5c1ce649ed0c";
 target="_blank" className="font-mono text-sm text-[#38bdf8] no-underline 
hover:underline">
-            Consumer →
+          <Link
+            href={CONSUMER_BENCHMARK_URL}
+            target="_blank"
+            rel="noopener noreferrer"
+            className="font-mono text-sm text-[#38bdf8] no-underline 
hover:underline"
+          >
+            Consumer result →
           </Link>
         </div>
       </div>
diff --git a/src/components/code-tabs.tsx b/src/components/code-tabs.tsx
index 185df57d..ccfd9315 100644
--- a/src/components/code-tabs.tsx
+++ b/src/components/code-tabs.tsx
@@ -29,6 +29,7 @@ const pkgInfo: Record<string, { install: string; url: string; 
label: string }> =
   "Go": { install: "go get github.com/apache/iggy/foreign/go", url: 
"https://pkg.go.dev/github.com/apache/iggy/foreign/go";, label: "pkg.go.dev" },
   "Node.js": { install: "npm install apache-iggy", url: 
"https://www.npmjs.com/package/apache-iggy";, label: "npm" },
   "C#": { install: "dotnet add package Apache.Iggy", url: 
"https://www.nuget.org/packages/Apache.Iggy/";, label: "NuGet" },
+  "PHP": { install: "cargo php install --release --yes", url: 
"https://github.com/apache/iggy/tree/master/foreign/php";, label: "GitHub" },
   "C++ (WIP)": { install: "git clone https://github.com/apache/iggy";, url: 
"https://github.com/apache/iggy/tree/master/foreign/cpp";, label: "GitHub" },
 };
 
@@ -210,6 +211,36 @@ await client.SendMessagesAsync(
             Encoding.UTF8.GetBytes(
                 "order-123"))
     }
+);`,
+  },
+  {
+    lang: "PHP",
+    file: "producer.php",
+    href: "https://github.com/apache/iggy/tree/master/foreign/php";,
+    code: `<?php
+
+$client = new \\Iggy\\Client(
+    '127.0.0.1:8090'
+);
+$client->connect();
+$client->loginUser('iggy', 'iggy');
+
+$client->createStream('orders');
+$client->createTopic(
+    'orders',
+    'events',
+    3,
+    null,
+    null,
+    null,
+    null
+);
+
+$client->sendMessages(
+    'orders',
+    'events',
+    0,
+    [new \\Iggy\\SendMessage('order-123')]
 );`,
   },
   {
@@ -247,9 +278,9 @@ export function LandingCodeTabs() {
   const pkg = pkgInfo[s.lang];
 
   return (
-    <div>
-      <div className="rounded-2xl border border-white/[0.08] bg-[#0c1220] 
overflow-hidden">
-        <div className="flex items-center border-b border-white/[0.06] 
overflow-x-auto">
+    <div className="min-w-0 max-w-full">
+      <div className="min-w-0 max-w-full overflow-hidden rounded-2xl border 
border-white/[0.08] bg-[#0c1220]">
+        <div className="flex max-w-full items-center overflow-x-auto border-b 
border-white/[0.06]">
           {snippets.map((sn, i) => (
             <button
               key={sn.lang}
@@ -257,45 +288,49 @@ export function LandingCodeTabs() {
               className={`px-4 py-2.5 text-xs font-medium whitespace-nowrap 
transition-colors ${
                 i === active
                   ? "text-[#ff9103] border-b-2 border-[#ff9103] 
bg-white/[0.03]"
-                  : "text-[#636b75] hover:text-[#aaafb6]"
+                  : "text-[#8c959f] hover:text-[#c4c9cf]"
               }`}
             >
               {sn.lang}
             </button>
           ))}
         </div>
-        <div className="p-5">
+        <div className="min-w-0 p-5">
           <div className="flex items-center justify-between mb-3">
             <div className="flex items-center gap-2">
               <div className="w-3 h-3 rounded-full bg-[#ff5f57]" />
               <div className="w-3 h-3 rounded-full bg-[#febc2e]" />
               <div className="w-3 h-3 rounded-full bg-[#28c840]" />
-              <span className="ml-2 text-xs text-[#636b75] 
font-mono">{s.file}</span>
+              <span className="ml-2 text-xs text-[#8c959f] 
font-mono">{s.file}</span>
             </div>
             <a
               href={s.href}
+              target={s.href.startsWith("http") ? "_blank" : undefined}
+              rel={
+                s.href.startsWith("http") ? "noopener noreferrer" : undefined
+              }
               className="text-[10px] text-[#ff9103] no-underline 
hover:underline"
             >
               SDK docs →
             </a>
           </div>
-          <pre className="text-[13px] leading-relaxed font-mono 
overflow-x-auto m-0 whitespace-pre min-h-[360px]">
+          <pre className="m-0 min-h-[360px] max-w-full overflow-x-auto 
whitespace-pre font-mono text-[13px] leading-relaxed">
             <code dangerouslySetInnerHTML={{ __html: highlight(s.code) }} />
           </pre>
         </div>
       </div>
 
-      <div className="mt-3 flex items-center gap-3">
+      <div className="mt-3 flex min-w-0 flex-wrap items-center gap-3">
         <button
           onClick={() => {
             navigator.clipboard.writeText(pkg.install);
             setCopied(true);
             setTimeout(() => setCopied(false), 1500);
           }}
-          className="group flex cursor-pointer items-center gap-2 rounded-lg 
border border-white/[0.08] bg-white/[0.03] px-3.5 py-2 transition-colors 
hover:border-white/[0.15]"
+          className="group flex min-w-0 max-w-full cursor-pointer items-center 
gap-2 rounded-lg border border-white/[0.08] bg-white/[0.03] px-3.5 py-2 
transition-colors hover:border-white/[0.15]"
         >
           <code className="font-mono text-xs 
text-[#aaafb6]">{pkg.install}</code>
-          <svg className="h-3.5 w-3.5 shrink-0 text-[#636b75] 
transition-colors group-hover:text-[#aaafb6]" fill="none" viewBox="0 0 24 24" 
stroke="currentColor" strokeWidth={2}>
+          <svg className="h-3.5 w-3.5 shrink-0 text-[#8c959f] 
transition-colors group-hover:text-[#c4c9cf]" fill="none" viewBox="0 0 24 24" 
stroke="currentColor" strokeWidth={2}>
             {copied ? (
               <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 
4L19 7" />
             ) : (

Reply via email to