VGI
Query APIs, models, and services like ordinary tables โ no code, just SQL.
On this page
Technical Overview
How VGI works
VGI lets DuckDB query things it can't reach natively โ an API, a model, an internal service โ with plain SQL. If someone gives you a worker (a URL or a script), you ATTACH it and its tables and functions become ordinary SQL. Want to expose your own code? You can build a worker in any language. Most people only ever query one.
What is VGI?
VGI (Vector Gateway Interface) lets DuckDB reach code and data it can't natively โ in any language, with no C++ and no compiling. Someone writes a worker (in Python, TypeScript, Go, Rust, โฆ); you ATTACH it and call the functions and query the tables it exposes as if they were built into DuckDB. Most people only ever query a worker โ building one is optional, and covered in Building a worker.
Under the hood, DuckDB and the worker hand data back and forth in a shared, efficient format called Apache Arrow IPC โ a columnar (column-by-column) way to hold a table in memory that both sides understand, so data crosses between them without being rebuilt row by row. You don't need to know anything about Arrow to use VGI; it's plumbing.
- โข Any language: Write a quick Python script or ship a compiled Go / Rust binary โ whatever speaks the VGI protocol over a pipe or HTTP. SDKs exist for a growing list of languages, with new ones added regularly โ see the languages page.
- โข Version independent: Your worker isn't compiled into DuckDB, so it keeps working as you upgrade DuckDB. (The small VGI extension itself is still version-matched to DuckDB, like any extension โ but the code you write in the worker isn't.)
- โข Process isolation & real parallelism: The worker runs in its own process, so a crash or a heavy dependency stays out of DuckDB's memory. And because each worker is a separate OS process, DuckDB pools and runs several side by side โ so even a single-threaded runtime like Python (held back by its GIL) gets true parallelism by spreading work across multiple worker processes, instead of being pinned to one core the way an in-process extension would be.
Why Query.Farm builds on VGI
Query.Farm is adopting VGI as its way forward for DuckDB extensions. A worker is dramatically faster to produce than a native C++ extension โ a coding agent can write one โ far easier to distribute, integrates cleanly with enterprise auth through OAuth, and makes it possible to stand up an entirely private extension ecosystem.
Attach a worker โ it's just SQL
DuckDB's ATTACH normally mounts another database. VGI widens what it can point at: attach a VGI worker โ a program running on your own machine or reached over HTTP โ and the tables and functions it exposes become ordinary SQL you can query, filter, and join against your local data. It's the same ATTACH you already know, aimed at a lot more than a database. If you're only querying, there's no code to write and nothing to install.
-
โข
Attach and query: Point
ATTACHat a worker's URL or path and its schemas, tables, and functions appear under the alias you choose. Runvgi_catalogsfirst to see what a worker offers. - โข Join it with your data: A worker's tables behave like local ones โ join them to Parquet files, CSVs, or other catalogs in a single query.
-
โข
Fetches only what you ask for: When the worker supports it, DuckDB sends your
WHERE, the columns you select, andLIMITto the worker, so it returns only the rows and columns your query touches instead of shipping the whole table across. (A worker that doesn't implement this falls back to reading the full table into DuckDB.)
How it connects: transports
The worker can run right beside DuckDB or anywhere you can reach over the network โ you pick when you ATTACH. Every option carries the same Arrow protocol.
-
โข
On your machine (default): Point
ATTACHat a local script or command and VGI runs it as a worker process next to DuckDB, reusing it across queries. The simplest way to start. -
โข
Over HTTP โ and onward to remote services: Point
ATTACHat a URL to reach a worker hosted anywhere. Because it's plain HTTP, a worker can bridge onward to remote services โ including over WebSockets โ so DuckDB can reach systems that Arrow Flight, gRPC, and similar transports can't. It's also the only form that runs in the in-browser WASM build. - โข Local socket or launcher: For advanced local setups a worker can also be reached over a Unix domain socket, or kept warm and shared across many DuckDB sessions by a small launcher process.
- โข Processes and threads: VGI isn't limited to a single worker: it pools multiple worker processes and supports multithreading within them, so heavy or parallel workloads fan out instead of bottlenecking on one thread.
Security: where the code runs
How much you trust a worker comes down to where its code runs โ and it's the same security question you already face with any DuckDB extension.
- โข A local worker runs on your machine: Attaching a local worker runs that program on your machine, with your privileges โ and VGI does not sandbox or isolate it (that would cost performance). It's the same risk as loading any DuckDB extension: only run workers you trust. Want to explore sandboxing or isolating local workers? Reach out to Query.Farm.
- โข HTTP can be a security boundary: Running a worker over HTTP keeps its code in a different environment, off your machine โ so the HTTP transport can act as a security boundary. That's useful if you want extension-like functionality without running untrusted code locally.
- โข Auth & identity over HTTP: Hosted workers can sit behind authentication: VGI supports OAuth and a notion of identity when you connect, so a worker knows who's asking and can gate access accordingly.
What people build with it
Anything you can write in your worker's language becomes SQL-accessible. A few common patterns:
Call APIs from SQL
Wrap a REST or gRPC service as SQL-accessible tables and functions, and join remote data with local Parquet in a single query.
Run models from SQL
Score rows through PyTorch, scikit-learn, ONNX, or any model that lives in your own runtime โ keep the model where it belongs and call it from SQL.
Reuse existing code
Make pandas / numpy transforms, Go services, or TypeScript validators available to SQL users without compiling a native DuckDB extension.
Expose custom sources
Turn internal or proprietary data sources into queryable tables and views without building native extension plumbing.
Install
INSTALL vgi FROM community;
LOAD vgi;
Quick Start
Attach a hosted worker as a SQL catalog
INSTALL vgi FROM community;
LOAD vgi;
-- Attach a remote VGI worker as if it were a database
ATTACH 'volcanos' AS volcanos (
TYPE vgi,
LOCATION 'https://vgi-volcanos.fly.dev/'
);
Query the remote service like normal tables
INSTALL vgi FROM community;
LOAD vgi;
ATTACH 'volcanos' AS volcanos (
TYPE vgi,
LOCATION 'https://vgi-volcanos.fly.dev/'
);
SELECT name, country, primary_type
FROM volcanos.smithsonian.pleistocene_volcanoes
LIMIT 10;
Inspect the catalogs a worker exposes
INSTALL vgi FROM community;
LOAD vgi;
ATTACH 'volcanos' AS volcanos (
TYPE vgi,
LOCATION 'https://vgi-volcanos.fly.dev/'
);
SELECT * FROM vgi_catalogs();
Reference
Extension Contents
Quick reference to all available functions and settings organized by category.
| Name | Type | Description |
|---|---|---|
|
Catalog
|
||
| vgi | ATTACH a VGI worker as a DuckDB catalog and query it in SQL. | |
|
Configuration
|
||
| vgi_async_prefetch | Enable async I/O prefetch for VGI table-function scans. | |
| vgi_cancel_enabled |
Notify VGI workers (on both the subprocess and HTTP transports) when a stream is torn down early, so their on_cancel hook can release resources.
|
|
| vgi_eager_load_threshold |
Per-object-kind threshold keyed by VgiCatalogSet::CacheKindName (table, view, index, scalar_function, aggregate_function, table_function, macro).
|
|
| vgi_http_timeout_seconds |
Timeout, in seconds, for VGI HTTP requests (catalog, init, and exchange operations).
|
|
| vgi_join_keys_max_bytes | Maximum estimated byte size for a join-keys batch; pushdown is skipped if exceeded. | |
| vgi_join_keys_threshold |
When a join has a VGI scan on one side, raise DuckDB's dynamic_or_filter_threshold to this value so the build side's distinct join keys are pushed to the worker as an IN filter.
|
|
| vgi_multi_branch_scans |
Rewrite VGI multi-branch table scans into LogicalSetOperation(UNION_ALL, ...) via the optimizer extension.
|
|
| vgi_oauth_enabled |
Enable interactive OAuth (PKCE or device-code) authentication on HTTP 401.
|
|
| vgi_oauth_flow | OAuth flow type. | |
| vgi_oauth_prompt |
OAuth prompt behavior sent to the authorization endpoint.
|
|
| vgi_oauth_timeout_seconds | Window, in seconds, for a human to complete interactive OAuth (device-code or browser/PKCE) authentication. | |
| vgi_secret_default_ttl_seconds | Default cache TTL, in seconds, for credentials fetched from an Orchard remote secret provider. | |
| vgi_streaming_window |
Route eligible OVER (...) queries against VGI aggregates with streaming_partitioned=true through the custom streaming operator.
|
|
| vgi_table_buffering |
Rewrite calls to TableBufferingFunction subclasses through the Sink+Source PhysicalVgiTableBufferingFunction operator.
|
|
| vgi_trust_empty_kinds |
Trust worker assertions that estimated_object_count[kind] == 0 means the kind is empty (skip the catalog_schema_contents_* RPC).
|
|
| vgi_worker_pool_idle_limit_seconds | Maximum idle time, in seconds, before pooled workers are removed. | |
| vgi_worker_pool_max |
Default per-path pool limit for VGI workers (0 = disabled).
|
|
|
Diagnostics
Look under the hood of a VGI table โ its per-column statistics and, for multi-branch tables, the branches it declares. |
||
| vgi_table_branches() | Inspect how each attached VGI table is composed from its underlying worker functions, one row per branch per table across every attached VGI catalog. | |
| vgi_table_statistics() | Show the per-column statistics DuckDB holds for a VGI table, identified by catalog, schema, and table name. | |
|
Internal
Implementation details that aren't part of the public API. Listed for completeness โ you should not call these directly. |
||
| vgi_native_delegation_marker() | Internal: a placeholder table function used by VGI's multi-branch scan rewriter to carry filter and projection pushdown before they are handed to the real worker-backed scan. | |
|
Maintenance
Clear VGI's caches and pooled workers to force fresh state on the next call. |
||
| vgi_clear_cache() | Clear cached catalog metadata (schemas, tables, functions, statistics) for all attached VGI catalogs, returning one row per catalog cleared. | |
| vgi_worker_pool_flush() | Clear all subprocess-pooled VGI workers, returning one row with the count of workers flushed. | |
|
OAuth & identity
Inspect and manage the OAuth identity a catalog authenticates with: who you are, the tokens currently held, and signing out. |
||
| vgi_oauth_identity() | Report the OIDC identity for each attached VGI catalog (catalog_name, origin, authenticated, sub, email, name, issuer, and the full decoded id_token claims as JSON). | |
| vgi_oauth_logout() | Forget cached OAuth tokens. | |
| vgi_oauth_tokens() | Show the OAuth tokens the extension has cached, one row per origin, with each token's expiry and refresh state. | |
|
Secrets
Inspect the secret providers registered for a catalog and flush their cached secrets. |
||
| vgi_secret_provider_flush() | Clear the TTL cache of one Orchard remote secret provider, or all providers when 'catalog' is omitted. | |
| vgi_secret_providers() | List the Orchard remote secret providers auto-registered by attached VGI catalogs, one row per provider with its endpoint, tie-break offset, active flag, cached-secret count, and cache TTL. | |
|
Worker access
Reach a VGI worker directly โ discover the catalogs it advertises and run its table functions or table scans โ without |
||
| vgi_catalogs() | List the catalogs advertised by a VGI worker, one row per catalog with its name and description. | |
| vgi_table_function() | Directly execute a table function exposed by a VGI worker, without ATTACHing it as a catalog. | |
| vgi_table_scan() | Internal: the physical scan operator for tables in an attached VGI catalog. | |
|
Workers & pooling
Inspect VGI's subprocess worker pool โ which workers are alive and how often pooled workers are reused. |
||
| vgi_worker_pool() |
List the VGI worker subprocesses currently pooled for reuse by this DuckDB process, one row per worker with its path, version info, pid, and idle age in seconds.
|
|
| vgi_worker_pool_stats() | Report worker-pool reuse effectiveness, one row per worker_path with how many acquisitions reused a pooled worker (hits) versus spawned a fresh one (misses). | |
No extension contents match that search.
API Reference
Function Documentation
Database Storage
Storage Extensions
Catalog implementations that attach external storage as a DuckDB database.
vgi
Description
ATTACH a VGI worker as a DuckDB catalog and query it in SQL. The database identifier you pass to ATTACH is the catalog name the worker advertises โ a single worker can serve several catalogs โ while the worker itself is selected with the LOCATION option. Once attached, the worker's schemas, tables, and table functions are queryable like any local database, joins and aggregates included. LOCATION accepts an HTTP(S) URL for a hosted worker, or an executable path / launch: spec for a worker VGI runs as a local subprocess. Any options VGI doesn't recognize are forwarded to the worker and validated against the attach-option specs that catalog declares, so each worker can accept its own attach-time options.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
location
|
VARCHAR
|
Required |
Where the worker lives: an HTTP(S) URL for a hosted worker (e.g. https://host/vgi/), or an executable path / launch: spec for a worker VGI runs as a local subprocess. May instead be embedded in the database identifier as '<catalog>?location=<worker>'.
|
secrets
|
BOOLEAN
|
Optional |
Default: true
Whether to auto-register the remote secret provider a worker advertises in its attach response, reusing this catalog's identity. Set false to opt out.
|
bearer_token
|
VARCHAR
|
Optional | Bearer token sent to the worker. Avoid inlining it in query text where you can. |
oauth_refresh_token
|
VARCHAR
|
Optional |
OAuth refresh token used to seed silent authentication for this catalog (see vgi_oauth_tokens and vgi_oauth_identity).
|
data_version_spec
|
VARCHAR
|
Optional | Pin the catalog to a specific data-version specifier the worker advertises, instead of the latest. |
pool
|
BOOLEAN
|
Optional |
Whether subprocess workers for this catalog are pooled and reused across attaches. Applies to executable / launch: LOCATIONs.
|
pool_max
|
BIGINT
|
Optional | Maximum number of pooled subprocess workers to keep for this catalog. |
pool_timeout
|
BIGINT
|
Optional | Idle time, in seconds, before a pooled subprocess worker is shut down. |
worker_debug
|
BOOLEAN
|
Optional | Enable verbose worker-side debug logging for this catalog. |
Examples
Attach a hosted worker as a catalog and query it like a local database
ATTACH 'volcanos' AS volcanos (
TYPE vgi,
LOCATION 'https://vgi-volcanos.fly.dev/'
);
SELECT name, country, primary_type
FROM volcanos.smithsonian.pleistocene_volcanoes
LIMIT 10;
Run a worker as a local subprocess and pool it across attaches
-- LOCATION can point at an executable VGI launches as a subprocess.
ATTACH 'example' AS ex (
TYPE vgi,
LOCATION './my_worker',
pool true
);
SHOW TABLES FROM ex;
Discover the catalogs a worker exposes before attaching one
SELECT catalog, source_url
FROM vgi_catalogs('https://vgi-volcanos.fly.dev/');
Configuration
Settings
Configure the vgi extension behavior using these settings.
vgi_async_prefetch
Enable async I/O prefetch for VGI table-function scans. Off by default: DuckDB's POSITIONAL JOIN operator does not handle BLOCKED sources.
false
vgi_cancel_enabled
Notify VGI workers (on both the subprocess and HTTP transports) when a stream is torn down early, so their on_cancel hook can release resources. Set to false to disable: destructors skip the cancel dispatch entirely, on_cancel is never invoked, and workers learn the stream is gone only via normal stream-close / HTTP TTL.
true
vgi_eager_load_threshold
Per-object-kind threshold keyed by VgiCatalogSet::CacheKindName (table, view, index, scalar_function, aggregate_function, table_function, macro). When a schema's estimated_object_count[kind] is โค the threshold, the first GetEntry() triggers a single bulk LoadEntries() instead of N per-name RPCs. Read at ATTACH; a mid-session SET requires re-ATTACH to take effect.
table |
1000 |
view |
1000 |
index |
1000 |
scalar_function |
1000 |
aggregate_function |
1000 |
table_function |
1000 |
macro |
1000 |
vgi_http_timeout_seconds
Timeout, in seconds, for VGI HTTP requests (catalog, init, and exchange operations).
300
vgi_join_keys_max_bytes
Maximum estimated byte size for a join-keys batch; pushdown is skipped if exceeded.
67108864
vgi_join_keys_threshold
When a join has a VGI scan on one side, raise DuckDB's dynamic_or_filter_threshold to this value so the build side's distinct join keys are pushed to the worker as an IN filter. This is a threshold, not a cap on how many keys are sent: if the distinct count exceeds it, no keys are pushed (the filter isn't built). Raise-only โ it never lowers a user-set threshold. 0 = disabled. See also vgi_join_keys_max_bytes for the byte-size cap.
100000
vgi_multi_branch_scans
Rewrite VGI multi-branch table scans into LogicalSetOperation(UNION_ALL, ...) via the optimizer extension. Set to false to disable the rewrite โ multi-branch table scans then throw at execution time (the marker placeholder's loud-fail). Emergency-rollback knob; not generally useful.
true
vgi_oauth_enabled
Enable interactive OAuth (PKCE or device-code) authentication on HTTP 401. Set to false to fail fast instead of prompting.
true
vgi_oauth_flow
OAuth flow type. auto lets VGI choose based on what the provider advertises.
auto
auto
device_code
pkce
vgi_oauth_prompt
OAuth prompt behavior sent to the authorization endpoint.
none
none
login
select_account
consent
vgi_oauth_timeout_seconds
Window, in seconds, for a human to complete interactive OAuth (device-code or browser/PKCE) authentication. Further capped by the provider's token expires_in.
120
vgi_secret_default_ttl_seconds
Default cache TTL, in seconds, for credentials fetched from an Orchard remote secret provider. Capped per-credential by the credential's own expiry. Read at ATTACH and frozen per-provider.
300
vgi_streaming_window
Route eligible OVER (...) queries against VGI aggregates with streaming_partitioned=true through the custom streaming operator. Set to false to fall back to PhysicalWindow / WindowCustomAggregator.
true
vgi_table_buffering
Rewrite calls to TableBufferingFunction subclasses through the Sink+Source PhysicalVgiTableBufferingFunction operator. Set to false to disable the rewrite โ table_buffering queries then throw a clear InternalException instead of running.
true
vgi_trust_empty_kinds
Trust worker assertions that estimated_object_count[kind] == 0 means the kind is empty (skip the catalog_schema_contents_* RPC). Set to false to force every RPC to fire even when the worker reports zero โ a debug escape hatch for diagnosing worker bugs.
true
vgi_worker_pool_idle_limit_seconds
Maximum idle time, in seconds, before pooled workers are removed.
5
vgi_worker_pool_max
Default per-path pool limit for VGI workers (0 = disabled).
256
Platform Support
Compatibility
Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.
Quick Facts
Platforms
- Linux x86_64 aarch64
- Linux (musl) Not available
- macOS Intel Apple Silicon
- Windows x86_64
- WASM eh mvp threads
Compiled binary sizes
| Platform | Architecture | Size |
|---|---|---|
| Linux | x86_64 | 20.10 MB |
| Linux | aarch64 | 18.65 MB |
| macOS | Intel | 14.67 MB |
| macOS | Apple Silicon | 13.65 MB |
| Windows | x86_64 | 12.82 MB |
| WASM | eh | 1015.6 KB |
| WASM | mvp | 865.5 KB |
| WASM | threads | 1013.1 KB |
Compressed download size from the Haybarn extension repository.
DuckDB & Haybarn
Release calendar- DuckDB v1.5.5 Haybarn 1.5.5-rc1 Supported