EKOS — Enterprise Knowledge Operating System

AI never touches raw enterprise systems.
One exception, and it's audited.

EKOS's Key Invariant says AI consumes knowledge through the Runtime only, never raw systems directly. RFC 0056 is the first place that line gets crossed on purpose: ClickHouse schema compiled into the ledger like any other source, plus ekos clickhouse ask — an LLM-built, SELECT-only, redacted, ledger-audited live query. Every screen on this page is real output from a real ClickHouse container, unedited, including the queries that failed.

ClickHouse system.tables compiled KIR objects ekos clickhouse ask redacted, audited answer
§ 01 / the invariant
Checked before a line of code was written

No MCP tool, no connector, had ever crossed this line before.

EKOS's own stated invariant
AI systems consume reconstructed knowledge
through the Runtime only; they
never touch raw enterprise systems
directly.

A repo-wide read confirmed it, before design started: every MCP tool, the AiRuntime, and every connector in this codebase only ever touched the local ledger — even crates/simulation, the closest precedent. RFC 0056 treats the live-query capability as an explicit, scoped, audited exception to that invariant, not a quiet extension of it: SELECT-only, redacted on the way out, and every call recorded as evidence — never a silent second path around the Runtime.

§ 02 / stage 1 — compiled metadata
Real ClickHouse container, real run, unedited

The same pipeline every other source goes through. Zero invariant risk.

terminal — real container on a live port
$ ekos build
Build complete.
  Connectors re-scanned: 7

$ ekos recover
clickhouse-analyzer complete tables=2
Recover complete.
  ClickHouse tables analysed: 2

$ ekos resolve && ekos compile && ekos commit
Commit complete.
  Objects written: 2
  Evidence records: 2

ClickHouseObserver reads ClickHouse's stock HTTP interface (system.tables/system.columns, no native driver) and ClickHouseAnalyzerPass compiles every table into a real ObjectKind::Table KIR object — the exact build.rs/recover.rs wiring the RFC 0012 Snowflake/Oracle scaffolds never got. No LLM call in this stage, no new invariant surface.

Open the real transcript: build-recover.txt
§ 03 / a real compiled object
ekos_test.orders — real, unedited

Real columns. Real evidence. Searchable like anything else EKOS has compiled.

ekos query object — real, unedited
Object: ekos_test.orders (Table)
  Properties:
    source_system: "clickhouse"
    engine: "MergeTree"
    columns: [{"data_type":"UInt64","name":"order_id"},
              {"data_type":"String","name":"customer_name"},
              {"data_type":"Decimal(10, 2)","name":"amount"},
              {"data_type":"String","name":"status"},
              {"data_type":"DateTime","name":"created_at"}]
  Evidence:
    [100%] clickhouse:ekos_test.orders — "table ekos_test.orders observed
    via ClickHouse system.tables/system.columns"

This is the same ObjectKind::Table kind file-based SQL DDL already compiles to — not a new Custom("ClickHouseTable") kind. A ClickHouse orders table and a Postgres orders table with overlapping columns become real cross-system identity candidates for free, with zero new exclusion-list code in crates/identity.

Open the real transcript: query-object-orders.txt
§ 04 / stage 2 — the pipeline
Six stages, one live-system crossing, all of it audited

Schema-grounded question in. Validated, redacted, audited answer out.

01
question
02
compiled schema context
03
LLM-built SQL
04
SELECT-only gate
05
live execution
06
redact + audit

Stage 03 grounds the LLM in the real compiled schema from Stage 1, never a guess. Stage 04 parses the generated SQL with sqlparser's real ClickHouseDialect and hard- rejects anything that isn't exactly one Statement::Query — no INSERT/DROP, no multi-statement batches, a LIMIT injected into the parsed AST if missing. Only SQL that sqlparser itself parsed and re-emitted ever reaches ClickHouse.

§ 05 / a real successful query
Checked against the actual seed data, not just "didn't crash"

Correct SQL, correct answer, live.

ekos clickhouse ask --json — real, unedited
$ ekos clickhouse ask --json "how many orders have status shipped?"
{
  "sql": "SELECT COUNT(order_id) FROM ekos_test.orders WHERE status = 'shipped' LIMIT 1000",
  "rows": [{ "COUNT(order_id)": "3" }],
  "audit_event_id": "be4f1866-7fe2-475e-aaf5-32566cbd1d0d"
}

Seed data: 5 orders, status shipped on rows 1, 3, 5 — 3 is the correct answer, verified against the fixture that was actually inserted, not assumed correct because the pipeline didn't error.

Open the real file: ask-shipped-count.json
§ 06 / honest, not hidden
A 1.5B local model, a hallucinated filter, a real live rejection

The gate guarantees safety, not correctness. This is what that looks like.

ekos clickhouse ask — real, unedited failure
$ ekos clickhouse ask "list the 3 highest-amount orders, showing customer_name and amount"
Error: query execution failed: api error 404: Code: 47. DB::Exception:
Unknown expression identifier `name` in scope SELECT name, amount FROM
(... WHERE status = 'completed') ...  (UNKNOWN_IDENTIFIER)

The model invented a status = 'completed' filter — that value never existed in the real data — and referenced name instead of the aliased customer_name. It's still exactly one well-formed SELECT, so the safety gate has nothing to reject; ClickHouse's real schema does the rejecting instead. No row returned, no ledger entry written, no confident wrong answer — a plain error surfaced straight through.

Open the real transcript: ask-hallucinated-join.txt
§ 07 / the audit trail, for real
Queried straight from the live ledger's own SQLite file

One Event/Evidence pair per successful query. Zero for the failed ones.

sqlite3 .ekos/ledger/ledger.db — real, unedited
SELECT entry_type, count(*)
FROM entries GROUP BY entry_type;

event    | 3
evidence | 6
object   | 3

3 event rows for the 3 queries that actually succeeded against ClickHouse — the 2 that errored (a bad join, the hallucinated filter above) left no trace, because record_query_event only runs after execution succeeds. 6 evidence rows: 2 table evidences from Stage 1, 3 query-SQL evidences from Stage 2, 1 unrelated file evidence. Row data itself is never ledgered — only the fact that a query ran, its SQL, and a content-hash of the result.

Open the real transcript: audit-ledger-counts.txt
§ 08 / the MCP gate, both directions
Real stdio JSON-RPC session, not a mocked call_tool()

Off by default. Verified absent and present, over the real protocol.

[clickhouse].enable-mcp-query = true
tools/list -> contains ekos_clickhouse_query: true
tools/call -> {"COUNT(order_id)": "3"}
gate unset (default)
tools/list -> contains ekos_clickhouse_query: false
tools/call -> isError: true
  "ekos_clickhouse_query is disabled -- set
   [clickhouse].enable-mcp-query = true"

The gate is enforced twice, independently: hidden from tools/list so a well-behaved agent never sees it, and re-checked inside call_tool so a client calling it by name directly — without ever having listed it — is still rejected.

Open the real transcript: mcp-gate-transcript.txt
Try it

Point it at any ClickHouse database EKOS has already compiled.

terminal
# compile the schema once
$ export EKOS_CLICKHOUSE_URL=http://localhost:8123
$ export EKOS_CLICKHOUSE_DATABASE=analytics
$ ekos build && ekos recover

# ask a real question, live, redacted, audited
$ ekos clickhouse ask "how many orders shipped last week?"
EKOS · RFC 0056 (ClickHouse Connector) · every example on this page is real output, unedited, from a real ClickHouse container · github.com/alexeyban/EKOS