Drive
Drive is a light, object-store-backed file store. Users drag files — a large Parquet export, a CSV, an Excel sheet — into the Drive page; the bytes are staged in the deployment's existing object-store bucket, and any Parquet/CSV/JSON file can be loaded into a lakehouse table with one click.
It is the UI counterpart to the headless definite run load CLI flow.
Storage layout
Drive needs no new bucket and no new credentials. The deployment already has one object-store bucket with the lakehouse's data files; Drive adds one sibling prefix:
| Prefix | Contents |
|---|---|
lake/ | DuckLake's own Parquet data files |
lake/_staging/ | Files staged by definite run load by default |
drive/ | Drive uploads — drive/{file_id}/{filename} |
All three prefixes are covered by the same object-store credentials
(GCS_HMAC_KEY_ID/GCS_HMAC_SECRET, S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEY
or S3 IRSA through the AWS credential chain, or
AZURE_STORAGE_ACCOUNT/AZURE_STORAGE_KEY) that the API and job-runner pods
already use. If jobRunner.staging.uri is explicitly moved outside the
lakehouse prefix, grant that custom staging prefix to the same object-store
identity as well.
On Azure the store's "bucket" is the account host, so these are prefixes
inside the configured container (<container>/lake/, <container>/drive/,
and so on).
Bucket CORS — required
Drive uploads go directly from the browser to object storage via presigned
PUT URLs, so multi-GB files never pass through the API pod. A browser
cross-origin PUT requires the bucket to have a CORS policy that allows GET
and PUT from the deployment's web origin.
GCS example (cors.json, applied with gcloud storage buckets update gs://YOUR_BUCKET --cors-file=cors.json):
[
{
"origin": ["https://your-deployment.example.com"],
"method": ["GET", "PUT"],
"responseHeader": ["Content-Type"],
"maxAgeSeconds": 3600
}
]
The equivalent S3 CORS rule allows the same AllowedOrigins / AllowedMethods
(GET, PUT) and AllowedHeaders (Content-Type).
Azure Blob
Azure uploads use an account-key SAS PUT to the blob endpoint, and that PUT
carries an x-ms-blob-type header, so the storage account's CORS rules must
allow that header in addition to GET/PUT. Set CORS on the storage account
(Portal: Settings > Resource sharing (CORS) > Blob service, or the CLI):
az storage cors add \
--services b \
--methods GET PUT \
--origins https://your-deployment.example.com \
--allowed-headers 'x-ms-blob-type,content-type,content-length' \
--exposed-headers '*' \
--max-age 3600 \
--account-name YOUR_ACCOUNT --account-key "$AZURE_STORAGE_KEY"
Private-endpoint / VNet-restricted accounts. The browser (not the API pod)
connects straight to https://<account>.blob.core.windows.net, so that
endpoint has to be reachable and DNS-resolvable from the end user's browser.
A storage account locked behind a private endpoint only answers inside the
VNet, so end users must reach it over that same private network (corporate
network, VPN, or ExpressRoute) with the privatelink.blob.core.windows.net
private DNS zone in effect. If browsers cannot resolve or reach the blob
endpoint, Drive uploads fail even with correct CORS. This is the same
browser-to-storage requirement S3 and GCS have; Azure private endpoints just
make it explicit.
Without a CORS rule, browser uploads fail with a CORS error; the headless
definite run load flow is unaffected (the CLI is not a browser).
Lifecycle
- Uploads are persistent by default — they stay until explicitly deleted.
- An upload can be marked temporary with a TTL of 1–30 days. Expired files are swept (object + metadata row) the next time the file list is fetched.
- Deleting a file removes both the object and the
drive_filesrow.
API
All endpoints are under /api/v1/drive. Reads require any authenticated user;
uploads, loads, and deletes require the editor role. Drive files are
workspace-shared — every user sees every file.
| Method + path | Purpose |
|---|---|
POST /presign | Mint a presigned PUT URL; creates a pending row. |
POST /files/{id}/complete | Confirm the upload finished; flips the row to ready. |
GET /files | List all ready files (also sweeps expired ones). |
GET /files/{id}/download-url | Short-lived presigned GET URL. |
POST /files/{id}/load | Load the file into a lakehouse table. |
DELETE /files/{id} | Delete the object and its row. |
Row actions
Each file row has a three-dot (⋮) menu:
- Ask Fi — opens a fresh Fi conversation pre-seeded with the file's path, so you can ask the agent to analyze it.
- Load into lakehouse — wrap the file in a table (Parquet/CSV/JSON only).
- Copy path — copy the file's
gs://…/s3://…object path to the clipboard. Paste it to Fi, or into aread_csv()/read_parquet()query. - Download — open a short-lived signed URL.
- Delete — remove the object and its row.
Working with Fi
The Fi agent can read Drive files directly. It has no object-store credentials
of its own and does not need them: its definite_sql_query tool runs through
the lakehouse, whose DuckDB session holds the bucket-wide credentials. The
access path Fi reaches for depends on the file type:
| File type | How Fi reads it |
|---|---|
| Tabular (Parquet/CSV/JSON) | SELECT * FROM read_parquet('gs://…/drive/…/sales.parquet') (or read_csv_auto / read_json_auto) |
Text (.md, .txt, .log, .sql, .yaml, source code…) | SELECT content FROM read_text('gs://…/drive/…/notes.md') |
Binary / arbitrary (.xlsx, .pdf, images, .zip…) | definite run drive get <path-or-id> <local-path> into the sandbox, then process with bash + Python |
Fi's system prompt has a Drive section that points it at each path. Nothing needs to be granted to the Fi sandbox itself.
CLI: definite run drive get
Download a Drive file by its gs:///s3:// path or by its bare file id:
# Path form — destination defaults to the filename in the path.
definite run drive get gs://acme-lake/drive/946aacbb-…/notes.md
# Bare id form — destination is required.
definite run drive get 946aacbb-ea0a-4776-b854-a5e654d2b6d8 ./notes.md
# Stream to stdout (no envelope on stdout — info goes to stderr):
definite run drive get gs://…/drive/…/data.csv -
Use --force to overwrite an existing destination file. This is how Fi pulls
binary files (xlsx, pdf, images) into its sandbox so they can be processed
locally; humans use it the same way.
Limitations (v1)
- Flat filenames only — no nested folders.
- Single presigned
PUT, so uploads cap at 5 GB on S3 and roughly 5,000 MiB on Azure Blob (GCS is higher); multipart / block upload is a follow-up. Same limitation asdefinite run load. - Only Parquet, CSV, and JSON can be loaded into the lakehouse; other file types are stored but not loadable.
- Azure Blob uploads need the storage account's CORS rules to allow the
x-ms-blob-typeheader and the blob endpoint to be reachable from end-user browsers (see Bucket CORS).