Lakeleto v0.4.1 Download

Using Lakeleto

Everything in Lakeleto 0.4.1, in the order you'll need it: install, the browser UI, every CLI command, the sources it reads, and how to run it for others. The examples use orders.parquet, the 5,000-row sample in examples/sample-data/. The outputs shown are what 0.4.0 prints.

Install

PlatformGet itThen
Windowslakeleto-0.4.1-x64.msi from the releaseDouble-click. Installs for your user (no admin). Start Menu → Lakeleto. Puts lakeleto on your PATH; open a new terminal.
macOSLakeleto-0.4.1.dmg (Intel + Apple Silicon)Drag to Applications. It lives in the menu bar. Menu bar → Install command line tool… for the CLI.
macOS / Linuxbrew install lucheeseng827/lakeleto/lakeletoCLI only.
Any, with Rustcargo binstall lakeletoFetches the prebuilt binary for your platform.
Dockermancube/lakeleto (amd64, arm64)See serve.
Archivelakeleto-<target>.tar.gz / .zipUnzip, run. One static executable. Linux x86_64 and arm64 (static), macOS Intel and Apple Silicon, Windows x86_64.
Windows on ARM64No prebuilt binary yetBuild from source with the command below.

Every release asset has a cosign signature (.sig, .pem), a .sha256 and SLSA provenance. Windows SmartScreen and macOS Gatekeeper will warn on first launch until code-signing certificates exist. See Troubleshooting.

Building from source? The release feature set is:

cargo install lakeleto --features serve,sql,iceberg,object-store,catalog,sqlite,postgres,mysql,delta,mcp,remote,parquet-out,compression

Quick start

In the browser

  1. Installer: open Lakeleto from the Start Menu or Applications. Your browser opens at the viewer.
    Terminal: lakeleto serve --root ~/data, then open http://127.0.0.1:8080. Or lakeleto open orders.parquet to start the server and open that file.
  2. Click a file in Files on the left. It opens in a tab on the Grid.
  3. Click a column header to sort. Type in the box under a header to filter. Switch to Schema, Profile or SQL at the top.

In the terminal

$ lakeleto schema orders.parquet          # what's in it
$ lakeleto head orders.parquet -n 20      # first rows
$ lakeleto profile --fast orders.parquet  # nulls, min, max from the footer
$ lakeleto query "SELECT city, count(*) n FROM t GROUP BY city" --file orders.parquet

Three ideas

The UI: layout

The Lakeleto window with its four regions: workspace bar and tabs at the top, sidebar on the left, the tab's views in the middle, history on the right.
Workspace bar and tabs on top. Sidebar left. The open tab in the middle. Run history on the right.
RegionWhat's there
Workspace barThe workspace picker, + New, Rename, Export, Import, Delete. On the right: the theme (◐ Auto → ☀ Light → ☾ Dark), the engine in use, and live when connected.
TabsOne per open source. ▦ is a data tab; λ a tab on its SQL view; ▤ a cached result; ⇄ a compare. + opens a new tab.
Toolbar☰ Sidebar, ⭑ Save source, Save query, ▶ Run across…, ⌘K (palette), ◷ History.
SidebarFiles (the folder you serve), Connections (saved sources), Saved queries, Variables. Drag the ⠿ handles to reorder sections; drag tabs to reorder them.
Tab viewsGrid, Schema, Profile, SQL. Above them: the source path, Copy path, the export format and Download view.
HistoryEvery SQL run in this workspace: rows, time, when. Totals at the top.

Opening a source

Click ⭑ Save source to pin the tab's source under Connections. Saved connections survive a restart and are what Run across targets.

Grid

The grid sorted by amount_usd descending with filters on city and quantity.
Sorted by amount_usd (▼), filtered by city and quantity. The footer says 290 matched of 5,000.
Type in a filter boxMatches
Singapore or ~Singaporecontains the text
!~testdoesn't contain it
^cust_00, $_9starts with / ends with
>= 100, > 100, < 5, <= 5comparisons
= pro, != freeequal / not equal
in: pro, enterpriseany of a comma-separated list
null, !nullempty / not empty

With SQL in the build (the release has it), sort and filter run in the engine over the whole table and are exact. Without it, they run over the first 200,000 rows and the footer says the view is partial.

JSON sources get a Records button: where in the document the rows are (/data · auto when detected). Sources with struct columns get Flatten structs, which turns user.geo.lat into its own column.

Row detail

The row detail panel listing one row's fields.
Click a row number. Esc closes it.

Schema and Profile

The profile view with null counts and ranges.
Profile: nulls, null %, distinct, min, max for each column.

Schema lists each column's name, Arrow type and nullability, plus the format, engine and row count. For Parquet and Arrow files the count comes from the footer.

Profile scans up to 10,000 rows by default (--default-scan on the server changes it). It respects the grid's filters, so filter first to profile a slice.

SQL

A SQL query and its result in dark mode.
Write SQL against t. Ctrl/⌘ + Enter or Run.

History and compare

Saved queries

A saved query filed under reports, and a variable.
reports/big-orders-by-tier, saved from the SQL tab, using {{min_amt}}.

Variables

Sidebar → + Variable → a key and a value. Use {{key}} in SQL or in a path. It's substituted as text right before the query runs.

SELECT * FROM t WHERE city = '{{city}}'        -- quote strings yourself
SELECT * FROM t WHERE amount_usd > {{min_amt}}  -- numbers bare
C:\exports\orders-{{day}}.parquet               -- in the path box too
postgres://me:{{PGPASS}}@db:5432/shop            -- secrets stay out of saved URIs

An unset {{x}} shows a warning chip until you define it. It's a text substitution, not a bound parameter, so don't paste untrusted text into a value.

Run across

Run across connections: one query over four saved sources.
One query over three monthly exports and the full file, each with its own result.
  1. Save each source you want with ⭑ Save source.
  2. On a SQL tab, write a query against t.
  3. ▶ Run across… → tick the targets → Run. Each result has an open button. A source whose columns don't fit fails on its own row only.

Export

Pick CSV, JSON or Parquet next to Download view (the API's /v1/export also does TSV and NDJSON). You get the view as it is, with its sort, filters and columns, over every matching row, not only those on screen. The cap is 1,000,000 rows and 512 MiB per file.

Databases in the UI

Sidebar Connections → + → SQLite, Postgres or MySQL → paste the URI, optionally a table → Add. A whole-database connection lists its tables; click one to open it. The ✎ on a connection edits it.

sqlite:///C:/data/app.db
sqlite:///home/me/app.db?table=orders
postgres://user:{{PGPASS}}@host:5432/shop
mysql://user:{{MYSQLPASS}}@host:3306/shop

The open-source build allows two database connections open at once.

Workspaces

A workspace holds your tabs, connections, saved queries, variables and history. Switch with the picker; + New, Rename, Delete do what they say. Export downloads a bundle and Import loads one as a new workspace, so you can hand a set of queries to a colleague.

They're stored in ~/.lakeleto/workspaces/<id>/ (workspace.json, history.jsonl, results/*.parquet). Set LAKELETO_HOME to move them.

Keyboard

The command palette.
The command palette: files, tabs, connections, saved queries and commands.
KeysDoes
Ctrl/⌘ KCommand palette (↑ ↓, Enter, Esc)
Ctrl/⌘ EnterRun the SQL tab's query
Ctrl/⌘ BShow / hide the sidebar
Alt TNew tab
Alt WClose tab
Alt RReopen the last closed tab
Alt ← / →Previous / next tab
EscClose the palette, row detail or a dialog
Enter / SpaceOn a header: sort. On a cell: copy. On a row number: detail.

The Alt and Ctrl+B shortcuts don't fire while you're typing in a box.

The CLI

lakeleto [OPTIONS] <COMMAND>

CommandDoes
schemaColumns, types, nullability, row count
headThe first N rows
profileNull %, distinct, min/max per column
infoFormat, engine, size, rows, columns
queryRead-only SQL over one or more tables
openStart the UI and open a file in the browser
serveServe the UI and the HTTP API
catalog lsBrowse Iceberg REST catalogs
enginesWhat this binary can read
mcpServe tables to an AI agent over MCP

Anywhere a <PATH> goes, you can give a local file or folder, s3://, gs://, az://, catalog:// or a database URI. lakeleto <command> --help prints the full help.

Global flags

FlagDefaultDoes
-o, --output <FMT>tabletable, json, ndjson, csv, tsv; for rows also parquet, arrow, arrows. See Output formats.
--out <FILE>stdoutWrite to a file. It appears only if the command succeeds. With no -o, the extension picks the format.
--format <NAME>inferredRead as parquet, csv, tsv, json, arrow, iceberg, delta or database.
--json-path <POINTER>detectedWhere a JSON document's rows are: /response/items or data. "" reads the whole document as one row.
--flatten[=N]offSpread struct columns into a.b.c columns; =1 for one level.
--max-decompressed <BYTES>4GiBStop a compressed read past this size (512M, 8GiB). Env LAKELETO_MAX_DECOMPRESSED.
--engine <E>autoauto, local, sql, remote.
--remote-url <URL>—Read through another Lakeleto server instead of this machine. Env LAKELETO_REMOTE_URL.
--remote-token <TOK>—Bearer token for it. Env LAKELETO_REMOTE_TOKEN.

schema

lakeleto schema <PATH>

Columns, types and nullability. For Parquet and Arrow the row count is read from the footer, without reading rows.

$ lakeleto schema orders.parquet
source : orders.parquet
format : parquet
engine : local
rows   : 5000
columns: 11

column        type           null?
------------  -------------  -----
order_id      Int32          yes
customer      Utf8           yes
city          Utf8           yes
tier          Utf8           yes
amount_usd    Float64        yes
quantity      Int32          yes
discount_pct  Float64        yes
is_refunded   Boolean        yes
rating        Float64        yes
order_ts      Timestamp(ns)  yes
ship_date     Date32         yes

head

lakeleto head <PATH> [-n, --rows <N>]

The first N rows (default 10). Reads only as far as it needs.

$ lakeleto head orders.parquet -n 5
| order_id | customer  | city     | tier       | amount_usd | quantity | discount_pct | is_refunded | rating | order_ts            | ship_date  |
|----------|-----------|----------|------------|------------|----------|--------------|-------------|--------|---------------------|------------|
| 100000   | cust_0332 | Berlin   | pro        | 23.29      | 13       | 0.05         | false       | 3.0    | 2024-06-13T04:40:00 | 2024-01-22 |
| 100001   | cust_0155 | Nairobi  | enterprise | 66.53      | 1        | 0.0          | false       | 4.0    | 2024-03-10T08:12:00 | 2024-02-03 |
| 100002   | cust_0405 | Toronto  | free       | 164.86     | 12       | 0.1          | false       | 1.0    | 2024-06-21T00:31:00 | 2024-06-12 |
| 100003   | cust_0667 | New York | pro        | 59.14      | 16       | 0.25         | false       | 4.0    | 2024-02-24T12:57:00 | 2024-01-11 |
| 100004   | cust_0050 | New York | free       | 53.34      | 16       | 0.05         | false       | 4.0    | 2024-04-02T18:46:00 | 2024-04-05 |

5 row(s)

profile

lakeleto profile <PATH> [--scan <N>] [--fast]

FlagDefaultDoes
--scan <N>10000Rows to scan for nulls, distinct values, min, max and samples.
--fastoffParquet only: nulls, min and max from the footer for the whole file, no scan. No distinct counts.
$ lakeleto profile orders.csv
source : orders.csv
engine : local
rows   : unknown (scanned 5000)

| column       | type         | nulls | null% | distinct | min                 | max                 |
|--------------|--------------|-------|-------|----------|---------------------|---------------------|
| order_id     | Int64        | 0     | 0.0%  | 5000     | 100000              | 104999              |
| customer     | Utf8         | 242   | 4.8%  | 898      | cust_0001           | cust_0900           |
| city         | Utf8         | 0     | 0.0%  | 10       | Berlin              | Toronto             |
| tier         | Utf8         | 0     | 0.0%  | 3        | enterprise          | pro                 |
| amount_usd   | Float64      | 0     | 0.0%  | 4531     | 5.01                | 1172.35             |
| quantity     | Int64        | 0     | 0.0%  | 19       | 1                   | 19                  |
| discount_pct | Float64      | 0     | 0.0%  | 5        | 0                   | 0.25                |
| is_refunded  | Boolean      | 0     | 0.0%  | 2        | false               | true                |
| rating       | Float64      | 774   | 15.5% | 5        | 1                   | 5                   |
| order_ts     | Timestamp(s) | 0     | 0.0%  | 4953     | 2024-01-01T00:59:00 | 2024-06-28T22:13:00 |
| ship_date    | Date32       | 0     | 0.0%  | 180      | 2024-01-01          | 2024-06-28          |

info

lakeleto info <PATH>

$ lakeleto info orders.parquet
path   : orders.parquet
format : parquet
engine : local
size   : 115.1 KiB
rows   : 5000
columns: 11

For a catalog:// table, info also says whose credentials read it: vended, catalog or ambient.

query

lakeleto query <SQL> [--file <PATH>] [--table <NAME=PATH>]…

FlagDoes
--file <PATH>Register a source as the table t.
--table <NAME=PATH>Register a source under a name. Repeat for joins.

One SELECT, WITH or EXPLAIN statement. Anything that writes is refused. With a database URI, the SQL runs in the database and names its tables.

$ lakeleto query "SELECT city, count(*) AS orders, round(sum(amount_usd), 2) AS revenue
    FROM t GROUP BY city ORDER BY revenue DESC LIMIT 5" --file orders.parquet
| city      | orders | revenue  |
|-----------|--------|----------|
| Sydney    | 503    | 66834.05 |
| Toronto   | 517    | 66242.49 |
| Mumbai    | 515    | 65590.18 |
| Singapore | 550    | 64419.91 |
| London    | 493    | 62170.92 |

5 row(s)
# join two sources
$ lakeleto query "SELECT o.city, avg(p.score) FROM o JOIN p ON o.city = p.city GROUP BY 1" \
    --table o=orders.parquet --table p=people.csv

# a whole SQLite database: its own table names
$ lakeleto query "SELECT city, count(*) FROM orders GROUP BY city" --file 'sqlite:///home/me/app.db'

# a catalog table
$ lakeleto query "SELECT count(*) FROM t" --table t=catalog://prod/sales/orders

Output formats

-oFor
tableReading in a terminal (the default)
json, ndjsonjq, scripts
csv, tsvSpreadsheets. Nested values become JSON text in the cell.
parquetDuckDB, Polars, pandas, Spark, with types kept
arrowArrow IPC file / Feather v2: pandas.read_feather, polars.read_ipc
arrowsArrow IPC stream, to pipe: polars.read_ipc_stream(sys.stdin.buffer)
$ lakeleto head orders.parquet -n 5 -o csv
order_id,customer,city,tier,amount_usd,quantity,discount_pct,is_refunded,rating,order_ts,ship_date
100000,cust_0332,Berlin,pro,23.29,13,0.05,false,3.0,2024-06-13T04:40:00,2024-01-22
100001,cust_0155,Nairobi,enterprise,66.53,1,0.0,false,4.0,2024-03-10T08:12:00,2024-02-03
100002,cust_0405,Toronto,free,164.86,12,0.1,false,1.0,2024-06-21T00:31:00,2024-06-12
100003,cust_0667,New York,pro,59.14,16,0.25,false,4.0,2024-02-24T12:57:00,2024-01-11
100004,cust_0050,New York,free,53.34,16,0.05,false,4.0,2024-04-02T18:46:00,2024-04-05
# a filtered slice as a Parquet file, then into DuckDB
$ lakeleto query "SELECT * FROM t WHERE amount_usd > 100" --file orders.csv --out big.parquet
$ duckdb -c "SELECT city, sum(amount_usd) FROM 'big.parquet' GROUP BY city"

# an Arrow stream straight into Polars
$ lakeleto head orders.parquet -n 100000 -o arrows | python -c "import sys, polars as pl; print(pl.read_ipc_stream(sys.stdin.buffer).describe())"

The binary formats are refused when stdout is a terminal: use --out, a redirect or a pipe.

open and serve

lakeleto open <PATH> [--addr …] [--root …] [--token …] [--default-scan …]

lakeleto serve [--addr …] [--root …] [--token …] [--default-scan …] [--workspace-remote …]

FlagDefaultDoes
--addr <HOST:PORT>127.0.0.1:8080Where to listen. Env LAKELETO_ADDR.
--root <DIR>any pathRefuse every read outside this folder, and all bucket, catalog and database URIs.
--token <TOK>none (open)Require Authorization: Bearer <TOK> on /v1/*. Env LAKELETO_TOKEN.
--default-scan <N>10000Rows the Profile view scans.
--workspace-remote <URL>local diskserve only: keep workspaces on another server. Env LAKELETO_WORKSPACE_REMOTE, token LAKELETO_WORKSPACE_REMOTE_TOKEN.
$ lakeleto open orders.parquet                     # browser opens on the file
$ lakeleto serve --root ~/data                     # browse a folder at :8080
$ lakeleto serve --addr 127.0.0.1:8090              # another port
$ docker run --rm -p 127.0.0.1:8080:8080 -v "$PWD:/data:ro" \
    mancube/lakeleto serve --addr 0.0.0.0:8080 --root /data

Stop it with Ctrl+C. The installed desktop app picks a free port itself and lives in the tray / menu bar, where Quit is.

catalog ls

lakeleto catalog ls [REFERENCE]

$ lakeleto catalog ls                         # configured catalogs (no network call)
$ lakeleto catalog ls catalog://prod/         # prod's namespaces
$ lakeleto catalog ls catalog://prod/sales/   # namespaces and tables in sales

It prints names, types and URIs, never a credential. Setup is in REST catalogs.

engines

What this binary was built with. The release binary has everything. A source build lists what's missing and the feature that adds it. With --remote-url it reports the server's engines.

Lakeleto engine backends:

  ✓ local (built-in reader)          reads: parquet, csv, tsv, json, arrow, iceberg, delta
  ✓ sql (DataFusion)                 reads: parquet, csv, tsv, json, arrow, iceberg, delta + read-only SQL
  ✓ iceberg (reader)                 reads: iceberg tables (current-snapshot Parquet)
  ✓ delta (reader)                   reads: delta lake tables (JSON transaction log)
  ✓ object-store (s3/gs/az)          reads: remote parquet, csv, tsv, json and arrow via your own creds
  ✓ catalog (Iceberg REST)           reads: catalog:// tables, with the credentials the catalog vends
  ✓ sqlite (read-only)               reads: sqlite files + connection URIs
  …

mcp

lakeleto mcp [--root <DIR>] [--max-rows N] [--max-bytes N] [--timeout SECS] [--default-scan N]

Lets an AI agent read your tables over the Model Context Protocol, on stdin and stdout. The agent's client starts it; you don't run it yourself.

# Claude Code, confined to ./data
$ claude mcp add lakeleto -- lakeleto mcp --root "$PWD/data"
// Claude Desktop, Cursor (.cursor/mcp.json), or a project .mcp.json
{
  "mcpServers": {
    "lakeleto": { "command": "lakeleto", "args": ["mcp", "--root", "/Users/me/data"] }
  }
}
ToolDoes
listA folder's subfolders and tables; also a bucket prefix, a database or a catalog namespace
describeColumns, types, row count, and Parquet footer stats, without reading rows
previewRows from any offset, optionally some columns
profileNulls, distinct, min, max, samples
queryOne read-only SQL statement
catalog_lsCatalogs, namespaces, tables
FlagDefaultDoes
--rootany pathOnly this folder; bucket, database and catalog references are refused
--max-rows1000Most rows per call
--max-bytes32768Most bytes of JSON per call; a cut result says truncated
--timeout30Seconds per call

The agent sees what the tools return, and so does the service that runs it. Point --root at data you're fine with both reading.

Sources: files and folders

SourceRecognised by
Parquet.parquet, .pq, or its magic bytes. Snappy, gzip, LZ4, zstd, Brotli.
CSV / TSV.csv, .tsv. Types are inferred.
JSON.json, .ndjson, .jsonl, .geojson. NDJSON, a top-level array, or one records member like {"data": [...]}, detected.
Arrow.arrow, .feather, .ipc files and .arrows streams.
Compressed text.csv.gz, .ndjson.zst, .tsv.bz2, .json.xz, read as they decompress
A folder of ParquetRead as one table, columns unioned. key=value folders (Hive partitions) become columns.
warehouse/orders/date=2026-07-17/part-0.parquet
warehouse/orders/date=2026-07-18/part-0.parquet

$ lakeleto query "SELECT date, count(*) FROM t GROUP BY date" --file warehouse/orders/

Iceberg and Delta

$ lakeleto open ./warehouse/db/orders
$ lakeleto open s3://my-bucket/warehouse/db/orders

S3, GCS and Azure

Credentials come from your environment, exactly as the cloud SDKs read them. Bytes go from the bucket to your machine. Parquet and Arrow are read with ranged requests, so only the footer and the parts a read needs are fetched.

$ export AWS_ACCESS_KEY_ID=… AWS_SECRET_ACCESS_KEY=… AWS_REGION=us-east-1
$ lakeleto schema s3://my-bucket/events/2026-07-18.parquet
$ export AWS_ENDPOINT=https://…         # MinIO, R2 and other S3-compatible stores

$ export GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json
$ lakeleto profile gs://my-bucket/events.parquet

$ export AZURE_STORAGE_ACCOUNT_NAME=… AZURE_STORAGE_ACCOUNT_KEY=…
$ lakeleto head az://my-container/events.parquet

In the UI, paste s3://my-bucket/warehouse/ into a new tab to browse the prefix. Schemes: s3/s3a, gs/gcs, az/azure/abfs/abfss/adl.

--root refuses bucket URIs. Run without it when you read remote data.

Iceberg REST catalogs

Name a table by catalog instead of by location: catalog://<catalog>/<namespace>/<table>. Tested against Apache Polaris and Lakekeeper; Nessie and Unity Catalog speak the same protocol.

# ~/.lakeleto/catalogs.toml
[catalog.prod]
uri = "https://polaris.example.com/api/catalog"
warehouse = "analytics"
oauth2-server-uri = "https://polaris.example.com/api/catalog/v1/oauth/tokens"

# the secret stays out of the file
$ export LAKELETO_CATALOG__PROD__CREDENTIAL='<client-id>:<client-secret>'
$ lakeleto open catalog://prod/sales/orders

Any key can also be set as LAKELETO_CATALOG__<NAME>__<KEY>. Credentials the catalog vends are kept in memory and refreshed before they expire. Glue and S3 Tables need request signing, which isn't supported yet.

Databases

$ lakeleto head   'sqlite:///home/me/app.db?table=orders'
$ lakeleto schema 'postgres://me@db:5432/shop?table=orders'
$ lakeleto query  "SELECT city, count(*) FROM orders GROUP BY 1" --file 'mysql://me@db:3306/shop'

Connections are read-only. Without ?table= a URI names the whole database: the UI lists its tables and query uses its table names. On Windows, SQLite is sqlite:///C:/data/app.db.

Sharing it safely

By default serve listens on 127.0.0.1 and the API is open, which is fine on your own machine. Before anyone else can reach it:

$ lakeleto serve --addr 0.0.0.0:8080 --root /data --token "$(openssl rand -hex 16)"

HTTP API

serve answers the same /v1 API the UI uses (protocol 1.0, sent as X-Lakeleto-Protocol). Rows come back as JSON, or as an Arrow IPC stream with Accept: application/vnd.apache.arrow.stream.

CallReturns
GET /v1/schema?path=Columns, types, row count
GET /v1/preview?path=&limit=First rows
GET /v1/rows?path=&offset=&limit=&sort=&desc=&filter=col:op:valueA grid window
GET /v1/profile?path=&scan=Column profile (scan=0: footer only)
GET /v1/export?path=&fmt=The view as a file
POST /v1/query {"sql", "file"}SQL result
GET /v1/list?dir=A folder, prefix, database or namespace
GET /v1/enginesVersion, protocol, engines, limits
$ curl 'localhost:8080/v1/schema?path=orders.parquet'
$ curl -sX POST localhost:8080/v1/query -H 'content-type: application/json' \
    -d '{"sql":"SELECT count(*) FROM t","file":"orders.parquet"}'

Filter operators: eq ne lt le gt ge contains notcontains startswith endswith in isnull notnull. The full reference is docs/CONFIG.md in the source.

Environment variables

VariableDoes
LAKELETO_HOMEWhere workspaces and catalogs.toml live (default ~/.lakeleto)
LAKELETO_ADDR--addr for serve / open
LAKELETO_TOKEN--token
LAKELETO_MAX_DECOMPRESSED--max-decompressed
LAKELETO_REMOTE_URL, LAKELETO_REMOTE_TOKENRead through another Lakeleto server
LAKELETO_WORKSPACE_REMOTE, …_TOKENKeep workspaces on another server
LAKELETO_CATALOG__<NAME>__<KEY>Catalog settings and secrets
AWS_*, GOOGLE_APPLICATION_CREDENTIALS, AZURE_STORAGE_*Bucket credentials, read as the cloud SDKs read them

Troubleshooting

You seeDo
Windows: "Windows protected your PC"More info → Run anyway. Check the .sha256 or cosign signature if you want proof.
macOS: "cannot verify the developer"Right-click the app → Open once, or xattr -d com.apple.quarantine ./lakeleto.
Double-clicking lakeleto.exe flashes a windowIt's the CLI. Use the installer's Start Menu entry, or run it from a terminal.
Port 8080 in uselakeleto serve --addr 127.0.0.1:8090
forbidden: path is outside the server rootThe file is outside --root, or it's a bucket or database URI and --root is set.
"rebuild with --features …"Your build lacks that reader. Use the release binary, or add the feature.
An MCP client shows no Lakeleto toolsRun its exact command in a terminal: it should sit waiting for input. Claude Desktop needs absolute paths.
Run across fails on one sourceThat source lacks a column the query uses. The others still ran.

Something missing or wrong here? Open an issue.