<p>Most features arrive quietly. This one arrived as a good, sharp question.</p><p>We'd just shipped PostgreSQL support, and the natural next step was ClickHouse. My first sketch reached for the easy path: ClickHouse has an HTTP interface, you can POST a query and get JSON back, done by lunch. And the answer came back, gently but firmly: "ClickHouse over HTTP is not what I want."</p><p>Fair. So let's talk about why that instinct was right, and what it took to honor it.</p><h2>The question hiding inside the question</h2><p>There was a second thread in that conversation, and it's worth pulling on: "why can't we just use the Laravel ClickHouse driver the project already has?"</p><p>It's a completely reasonable thing to expect. The project's right there. Its <code>.env</code> knows the connection. Its <code>composer.json</code> has a driver. Why reinvent it?</p><p>Because Conductor isn't a PHP process. It's a compiled Rust app. When the database browser connects, it isn't running your Laravel code or loading Composer packages — it can't, any more than your terminal app can "borrow" your project's npm modules. It reads the values from <code>.env</code> (host, port, user, password, database) and then it has to speak the database's wire protocol itself, from Rust.</p><p>That's the same reason MySQL and Postgres support uses Rust clients, not PDO. So the real choice was never "Rust vs. the Laravel driver." It was "which way should Rust talk to ClickHouse" — and there were two honest options.</p><h2>HTTP vs. native — and why native won</h2><p>ClickHouse exposes two doors:</p><ul><li><p><strong>HTTP (port 8123)</strong> — simple, universal, server renders the values for you. Genuinely fine; lots of tools use it.</p></li><li><p><strong>Native TCP (port 9000)</strong> — the binary protocol <code>clickhouse-client</code> itself speaks. Lower overhead, and the one most people mean when they say "connect to ClickHouse."</p></li></ul><p>The preference for native wasn't fussiness. It's the protocol your ClickHouse setup is most likely to expose and trust, the one your existing connection details point at, the one that behaves like the CLI you already know. HTTP would've been <em>a</em> connection; native is <em>the</em> connection.</p><p>So 0.4.2 talks to ClickHouse on port 9000, native protocol, via the Rust <code>klickhouse</code> crate. No HTTP.</p><h2>The hard part, and the trick that made it easy</h2><p>Here's the catch with native protocol: HTTP lets the server turn values into text for you. Native hands you binary, and the client has to make sense of it — across ClickHouse's enormous type zoo: <code>Int256</code>, <code>UInt128</code>, <code>Decimal64</code>, <code>DateTime64</code>, <code>UUID</code>, <code>Enum16</code>, <code>Array</code>, <code>Tuple</code>, <code>Map</code>, <code>IPv6</code>… A database browser that only handles int and string is a toy.</p><p>Two things made this tractable.</p><p><strong>A dynamic row reader.</strong> A generic browser can't know your columns at compile time. The <code>klickhouse</code> crate has a <code>Row</code> trait whose deserialize step hands you <code>(name, type, value)</code> for each cell — so a tiny custom row type captures any result without a predefined struct:</p><pre><code class="language-rust">struct ChRow { cells: Vec&lt;(String, Value)&gt; }
// deserialize_row(map) -&gt; keep (name, value) for every column
</code></pre><p><code>Value</code><strong> already knows how to print itself.</strong> The crate's <code>Value</code> enum — every ClickHouse type — implements <code>Display</code> with ClickHouse-style formatting. So turning any cell into grid text is almost nothing:</p><pre><code class="language-rust">match v {
    Value::Null =&gt; None,
    other       =&gt; Some(other.to_string()),
}
</code></pre><p>A <code>Decimal64(2, 1995)</code> prints as <code>19.95</code>. An <code>Array(['a','b'])</code> prints as <code>['a','b']</code>. A <code>DateTime64</code> prints as a timestamp. We didn't have to hand-format the type zoo — the crate did.</p><p>One more bit of plumbing: <code>klickhouse</code> is async, and Conductor's database commands are synchronous. Tauri already carries a Tokio runtime, so each query just does a <code>block_on</code> — the async machinery stays invisible.</p><h2>How it feels</h2><p>Open the DB panel, <em>Manual connection…</em>, pick ClickHouse (the port snaps to 9000), fill in host/user/password/database, <em>Connect</em>. From there it's the same browser you already know:</p><ul><li><p>The table list (<code>SHOW TABLES</code>) in the side panel.</p></li><li><p>Click a table → full-window grid with sort, per-column filters, and paging.</p></li><li><p>A query tab for the analytical questions ClickHouse is built for:</p></li></ul><pre><code class="language-sql">SELECT toStartOfHour(ts) AS hour, count() AS hits
FROM events
WHERE ts &gt;= now() - INTERVAL 1 DAY
GROUP BY hour ORDER BY hour;
</code></pre><ul><li><p>Structure (<code>DESCRIBE TABLE</code>) to see column types and spot the <code>Nullable(...)</code> ones.</p></li><li><p>⤓ Excel to hand a result to someone who lives in spreadsheets.</p></li></ul><h2>The one honest "no"</h2><p>ClickHouse tabs are read-only — no inline cell editing. That's not laziness; it's respect for what ClickHouse is. It's a columnar analytics engine, not an OLTP row store — you don't double-click a cell and <code>UPDATE</code> one row; you <code>ALTER … UPDATE</code> as a background mutation. So rather than pretend, Conductor simply detects there's no primary key to edit against and keeps those tabs to what they're good at: reading, querying, exporting. A tool that knows its own limits is a tool you can trust.</p><h2>The throughline</h2><p>The easy version of this feature would have worked. But "works" and "right" aren't the same word. The right version speaks the protocol your ClickHouse actually expects, handles its full type system honestly, and declines to fake an editing model that doesn't fit.</p><p>That's the quiet promise underneath all of Conductor: it's a tool, and a tool should be honest — about how it connects, what it shows, and what it won't pretend to do. It reads your <code>.env</code>, speaks the database's own language, and never once reaches for a model.</p><p>Four engines now — MySQL, PostgreSQL, ClickHouse, SQLite — all in the same panel, all a click from the project you're already in. Open one. Ask it something. Stay right where you are. 🪵🔥</p><p><em>Elyra Conductor 0.4.2 adds ClickHouse to the database browser over the native protocol (port 9000), with auto-update built in. Conductor is a Rust app that reads your </em><code>.env</code><em> and speaks each database's wire protocol directly — it connects, queries, and exports, and it never reasons.</em></p>