Security and data flow
The ai extension is a network-calling DuckDB extension. Treat every provider call as
an explicit data egress path from DuckDB to the configured provider or gateway.
Defaults
- No telemetry is sent by default.
- Usage events stay in the current DuckDB database instance unless
duckdb_ai_log_endpointorDUCKDB_AI_LOG_ENDPOINTis configured. - Usage logs omit prompt, input, and response text by default.
- API keys are read from environment variables or
TYPE duckdb_aisecrets. - An API key is only sent to the origin (scheme, host and port) it is
configured for: the
BASE_URLof the secret that holds it, otherwise the provider's built-in endpoint or base URL environment variable, plus any host listed inDUCKDB_AI_ALLOWED_HOSTS. A query-levelbase_url,duckdb_ai_base_urlor external modellocationcannot redirect it. - External model objects contain only safe routing metadata and the name of a
TYPE duckdb_aicredential secret; they never contain credential material. TYPE duckdb_aisecrets redactAPI_KEYvalues when listed.- Provider error messages redact the active API key before surfacing errors.
- Provider redirects are not followed for API calls.
- Provider and log URLs are restricted to HTTP(S), and URLs with embedded credentials or raw control characters are rejected.
- HTTP response bodies are bounded to 64 MiB by default; use
DUCKDB_AI_MAX_RESPONSE_BYTESto configure a different bound up to 1 GiB. - Caller-controlled HTTP header values reject CR, LF, and NUL characters.
- Provider response parsing uses DuckDB's vendored
yyjsonparser.
Egress controls
Use duckdb_ai_allowed_hosts or per-call allowed_hosts := ... to restrict
provider and usage-log destinations:
SET duckdb_ai_allowed_hosts = 'ai-gateway.internal,api.openai.com';
Allowlist entries match URL hosts. Entries may be hostnames, host:port, full
URLs, *.example.com, or *.
The allowlist check runs before the HTTP request, including for usage-log endpoints.
The DUCKDB_AI_ALLOWED_HOSTS environment variable is a ceiling set by whoever
starts the process. Every request must match it as well as any session
allowlist, so duckdb_ai_allowed_hosts and allowed_hosts := ... can narrow it
but not widen it.
The URL validation still runs when no host allowlist is configured. An allowlist controls which HTTP(S) hosts are reachable; it does not enable other libcurl protocols.
SET enable_external_access = false blocks every provider and usage-log
request, including requests to a local model server, with a Permission Error.
Endpoints listed in DuckDB's allowed_directories or allowed_paths stay
reachable, for example after
SET allowed_directories = ['https://api.openai.com/'] before external access
is disabled.
Functions that only build or inspect requests, such as
ai_completion_request_json, keep working. Under on_error := 'null' a
blocked row returns NULL and ai_usage() records the reason. DuckDB also
stops reading the persistent secret directory once external access is off, so
set allow_persistent_secrets = false first or use environment variables and
temporary secrets.
For a shared server, such as one serving remote clients over Quack, see Run on a Quack server.
Data sent by function family
| Function family | Data sent |
|---|---|
ai_complete, ai_try_complete, ai_complete_json, ai_rerank, task wrappers, aggregates, and SQL assistant functions | Prompt text, configured system prompt, model name, response-format hints, and provider options needed by the selected provider API. |
ai_complete_record, ai_extract_record, ai_complete_json | Prompt or row text plus the supplied JSON Schema. |
ai_decide, ai_jev, and ai_classify/ai_filter with typesafe | Row text as state, plus question labels, descriptions and instructions. TypeSafe batches up to 32 rows per request; other decision providers receive one row per request. |
ai_provider_call | The full request body you supply. |
ai_embed, ai_similarity, and optimized classification | Input text to embed and model name. Inputs with the same options are packed by count, estimated tokens, and bytes. ai_similarity uses a bounded query-local cache to send each distinct value once across vector chunks. |
ai_redact with openai_privacy_filter | Raw input text and model name to POST /redact. |
ai_generate_chunks, ai_prep_search(..., enrich := false), ai_completion_request_json, ai_embedding_request_json, ai_schema_prompt, ai_is_read_only_sql, ai_validate_read_only_sql, ai_count_tokens, ai_recommended_batch_size, metadata functions, and catalog table functions | No provider network call. |
ai_sql, ai_query_data, ai_explain_sql, ai_fix_sql with sample_rows := N | Up to N real rows per included table are added to the schema context. Omit sample_rows for sensitive tables, or build a table without the sensitive columns, for example CREATE TABLE tickets_for_sql AS SELECT * EXCLUDE (internal_note) FROM support_tickets;. Views are not described by ai_schema_prompt. |
ai_query_data | The natural-language question and generated schema context are sent during bind; generated SQL is validated as one read-only DuckDB SELECT before execution. SQL assistant schema context is sent in the provider system message when the provider protocol supports it. Successful generated SQL is cached in memory for repeated binds with the same question, schema context, and output-affecting options. |
ai_parse_document | Binary document content encoded as base64, MIME type, parser profile, and optional page selection are sent to DUCKDB_AI_CONTROL_PLANE_URL. |
| Endpoint provisioning functions | Safe external-model metadata, operation IDs, and explicit spend ceilings are sent to DUCKDB_AI_CONTROL_PLANE_URL. Cloud credentials are not read or sent by the extension. |
Optional control plane
Endpoint provisioning and remote document parsing are disabled until
DUCKDB_AI_CONTROL_PLANE_URL is configured. The optional
DUCKDB_AI_CONTROL_PLANE_TOKEN is sent as a bearer token and should be scoped to
the companion service, not to a cloud provider. Applied provisioning requires
an explicit dry_run := false and positive hourly spend ceiling. Registration,
query binding, and first inference never provision resources.
Logging
Outbound usage logs are disabled unless a log endpoint is configured. Default
payloads contain function name, query id, provider, protocol, model, character
counts, token counts, elapsed time, HTTP status, optional tags, and optional
estimated cost. Text fields are included only when duckdb_ai_log_include_text or
DUCKDB_AI_LOG_INCLUDE_TEXT=1 is enabled.
When strict logging is disabled, outbound usage logs are queued in memory and
sent asynchronously on a best-effort basis. Enabling duckdb_ai_log_strict or
DUCKDB_AI_LOG_STRICT=1 posts logs synchronously and fails the SQL query if the
collector request fails.
Proxy and TLS
Provider HTTP calls use libcurl. Standard environment variables such as
HTTPS_PROXY, HTTP_PROXY, NO_PROXY, SSL_CERT_FILE, and CURL_CA_BUNDLE
can be used where supported by the libcurl build.
Vulnerabilities
Report suspected vulnerabilities using the process in the repository
SECURITY.md. Do not include API keys, provider credentials, private data, or
full model prompts in public issues.