A bucket of files, served as an API, with no copy in between
A DuckDB connection lists the folders you allow and makes each one a view, with partitions as a column. Every read is checked twice: by DuckDB against the paths you listed, and by your storage against the credentials you gave it. Apps run saved queries, analysts write SQL over the views, and nobody, not even the admin key, reads outside those paths.
0copies of the data
1view per folder, partitions as a column
2locks on every read
Who asks
The governed core
DuckDB, in the server's process
Your bucket
↗Finance dashboardqueries: daily_events200 · X-Cache: HIT
lake · no tables of its ownViews, made as it opensauto_views: 3 viewsevents ← events/date=*/*.parquet + datecustomers ← dim/customers.parquetregions ← dim/regions.csvLock 1 · allowed_pathss3://acme-lake/events/ s3://acme-lake/dim/Enforced by DuckDB, locked as the connection opens: a query can't change it back.403 path_not_allowed
hr/salaries.parquetNot in allowed_paths, nor in the credentials' policy.Lock 2 · your bucket policycredentials read events/ and dim/ only
What can be readchecked by grant, then by DuckDB
analyst SELECT date, count(*) FROM events GROUP BY 1200 · 7 rowsanalyst SELECT * FROM read_parquet('s3://acme-lake/events/…')403 table_not_allowedadmin SELECT * FROM read_parquet('s3://acme-lake/hr/salaries.parquet')403 path_not_allowed
Stablecompatibility promiseEach read is requests and egress on your bucket: cache what's polled.
Who asksAn app, an analyst, even the adminApps run saved queries; analysts write SQL over the views, never over file paths.
↓
The governed coreGrant → cache → formatGrants and masking as for any database. Cached, the files are read once per TTL.
↓
DuckDBFolders become viewsListed as the connection opens; date=…/ partitions become one view with a date column.
↓
Lock 1, allowed_pathsNothing else can be readEnforced by DuckDB, even for the admin key: 403 path_not_allowed.
↓
Your bucket, behind lock 2Read where the files are, no copyGive the connection credentials that read only those prefixes.
whoan analyst's keydoorsaved query over view eventsallowed_paths only ✓auto views per prefix ✓grants + masking as usual ✓datas3://acme-lake/events/*.parquet · DuckDB
For data teams
Turn a bucket of Parquet into a queryable API
The lake has the data; the people who need it have a browser and curl. A DuckDB connection pointed at a prefix makes each file a view, locked to the paths you list, with no copy and no warehouse.
✓Parquet, CSV and JSON on S3, GCS, R2, HTTPS or disk. Hive-style key=value/ partitions become one view.
✓Nothing outside allowed_paths can be read, enforced by DuckDB itself as the connection opens.
✓Saved queries over the views get grants, masking and caching like any other.
✓Results back out as Parquet too, in steady memory at any size.
# a connection of nothing but files
curl -X POST https://data.example.com/api/v1/connections \
-H 'X-API-Key: <admin>' -H 'Content-Type: application/json' -d '{
"name": "lake",
"db": "duckdb",
"database": ":memory:",
"active": true,
"allowed_paths": ["s3://acme-lake/events/", "s3://acme-lake/dim/"],
"auto_views": true,
"user": "${LAKE_ACCESS_KEY}", "password": "${LAKE_SECRET}", "region": "eu-west-1"
}'
# events/date=2026-10-10/*.parquet is now a table called events
SELECT date, count(*) FROM events GROUP BY 1 ORDER BY 1 DESC LIMIT 7
The example APIs ship with a generated database and one scoped key per scenario, so you can try each of these in a few minutes without connecting anything of your own.