| description | How to serve a VGI worker over HTTP and authenticate callers with bearer tokens or JWT/OAuth. |
|---|
What this is: how to run a worker over HTTP instead of a subprocess, and gate it with
authentication.
Who it's for: developers deploying a worker as a network service.
Requires: pip install vgi-python[http] (and [oauth] for JWT).
- A working worker (see the tutorial).
pip install vgi-python[http]; for JWT/OAuth alsovgi-python[oauth].
The same worker that runs over a subprocess also runs over HTTP — add --http:
vgi-serve my_worker.py --httpDuckDB still attaches the worker over HTTP the usual way (ATTACH ... (TYPE vgi, LOCATION 'http://...')). You only need the Python Client when you're calling the worker from Python
rather than from SQL — for tests, scripts, or another service. It connects with transport="http"
instead of spawning a subprocess, and exposes the same call methods:
# illustrative — calling the worker from Python over HTTP
from vgi.client import Client
with Client(transport="http", base_url="http://localhost:8080", bearer_token="token1") as client:
... # same .scalar_function() / .table_function() calls as the subprocess transportThe native client advertises a 256 MiB decoded-response limit on every RPC, including the short-lived connections used for catalog operations. Override it when the application has a smaller memory budget:
with Client.from_http(
"http://localhost:8080",
accepted_max_response_bytes=32 * 1024 * 1024,
) as client:
...The value must be an integer from 65536 through 2^53 - 1. Configured clients
discover server support before the first RPC and fail closed if it is absent.
Passing None omits negotiation for compatibility with a legacy unbounded server;
doing so is not recommended for externally reachable workers. Capability
discovery reports the server's own effective response cap and whether it
honors the client limit.
The quickest setup is static bearer tokens via an environment variable — comma-separated
token=principal pairs:
VGI_BEARER_TOKENS="token1=alice,token2=bob" vgi-serve my_worker.py --httpUnauthenticated requests get HTTP 401. Authenticated requests carry the principal in an
AuthContext. Your function reads it through an optional ctx parameter: declare ctx in the
signature and the framework injects a per-call CallContext (auth, logging, transport info) — you
don't pass it from SQL.
# illustrative — reading the injected ctx in a function
class Secret(ScalarFunction):
@classmethod
def compute(cls, value, *, ctx) -> ...:
ctx.auth.require_authenticated() # raises if anonymous
principal = ctx.auth.principal # "alice"
...For JWT/JWKS and RFC 9728 OAuth resource metadata, set VGI_JWT_ISSUER / VGI_JWT_AUDIENCE
(requires vgi-python[oauth]) — see the Authentication reference for the
full variable list and programmatic make_wsgi_app(authenticate=...) API.
- Full auth options (JWT, OAuth metadata, custom callbacks) → Authentication reference.
- Request context (
ctx.auth, logging) → API Reference: Auth & Secrets.