duckdb + quack
A duckdb process is the client; the attached catalog is the principal's virtual world.
LOAD httpfs; LOAD quack;
ATTACH 'quack:<host>:<port>' AS remote (TYPE quack, TOKEN '<access token>');
SELECT count(*) FROM remote.main.orders; -- your slice
INSERT INTO remote.main.orders SELECT * FROM payload; -- bulk SEND_DATA drain, enforced row by row
- Instead of an inline TOKEN, a secret works today:
CREATE SECRET s (TYPE quack, TOKEN '...', SCOPE 'quack:<host>:<port>')and ATTACH without TOKEN. - Bulk staging is a granted schema (spec 056):
CREATE TABLE remote.stage.load(...), drain into it, promote with ordinary SQL,DROPit. A bulk insert into a predicate-confined table needs the grant to carry aCOLUMNSlist (the drain is positional on the wire). - The client's own
CREATE TEMP TABLEis local to the client and never reaches the server.
OIDC without hand-carried tokens (spec 061)
The oidc provider runs a real flow at CREATE SECRET and stores the minted token where quack
already looks - ATTACH then needs no TOKEN at all:
CREATE SECRET kc (TYPE quack, PROVIDER oidc, SCOPE 'quack:<host>:<port>',
ISSUER 'https://kc/realms/x', CLIENT_ID 'cli',
FLOW 'device'); -- prints a URL + code, waits for approval
-- FLOW 'password' + USERNAME/PASSWORD (where the IdP allows it)
-- FLOW 'client_credentials' + CLIENT_SECRET (machine identity)
-- FLOW 'token' + TOKEN (a token you already have)
ATTACH 'quack:<host>:<port>' AS remote (TYPE quack); -- rides the secret
CREATE OR REPLACE SECRET re-mints - silently, off the cached refresh token, so a device flow does
not re-prompt. Passwords and client secrets are consumed by the flow and never stored; the secret
carries only the minted token (redacted) and the visible issuer/client/flow. OAUTH_SCOPE passes an
OAuth scope through (plain SCOPE is the secret's own matching clause).
Worth knowing:
- When the minted token expires, the door refuses the next connection with its ordinary
verification error - the fix is
CREATE OR REPLACE SECRET(the re-mint lever), not a new ATTACH. CREATE PERSISTENT SECRETwrites the minted token to disk (duckdb's standard secret storage,redact_keysgoverns display only), and after a restart it revives with whatever staleness it has while the refresh chain lived only in process memory - prefer the default temporary secret and re-mint per session.- The
CREATE SECRETstatement itself carries the password/client secret as ordinary SQL text - run it locally, never through a gateway or shell that logs statements.
Discovery: ISSUER is optional (spec 062)
A served door advertises the issuers the node trusts on GET /.well-known/quack-auth, so a secret
whose SCOPE names a concrete door needs no IdP configuration at all:
CREATE SECRET kc (TYPE quack, PROVIDER oidc, SCOPE 'quack:<host>:<port>',
CLIENT_ID 'acl-cli', FLOW 'device'); -- ISSUER discovered from the door
A door advertising several issuers refuses the omission by count - name ISSUER then. The door
itself serves TLS when given a cert/key (acl_quack_serve(uri, token, cert, key)); a remote quack
client speaks https by default, a loopback one stays plain.