@query-farm/vgi 0.39.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/index.core.d.ts +2 -2
- package/dist/index.js +30 -3
- package/dist/index.js.map +4 -4
- package/dist/rpc-server.d.ts +23 -1
- package/dist/serve-entry.d.ts +13 -1
- package/dist/serve-entry.js +31 -5
- package/dist/serve-entry.js.map +4 -4
- package/dist/worker-cf-entry.d.ts +1 -1
- package/dist/worker-cf-entry.js +13 -3
- package/dist/worker-cf-entry.js.map +5 -5
- package/package.json +3 -3
- package/src/index.core.ts +2 -0
- package/src/rpc-server.ts +55 -1
- package/src/serve-entry.ts +19 -1
- package/src/worker-cf-entry.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@query-farm/vgi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "TypeScript framework for extending DuckDB with user-defined functions via the Vector Gateway Interface (VGI) over Arrow IPC.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Query Farm LLC (https://query.farm)",
|
|
@@ -91,12 +91,12 @@
|
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|
|
93
93
|
"@query-farm/apache-arrow": "^21.1.1",
|
|
94
|
-
"@query-farm/vgi-rpc": "^0.
|
|
94
|
+
"@query-farm/vgi-rpc": "^0.27.0"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
97
|
"@duckdb/node-api": "^1.5.5-r.4",
|
|
98
98
|
"@query-farm/apache-arrow": "^21.1.1",
|
|
99
|
-
"@query-farm/vgi-rpc": "^0.
|
|
99
|
+
"@query-farm/vgi-rpc": "^0.27.0",
|
|
100
100
|
"@types/bun": "latest",
|
|
101
101
|
"typescript": "^7.0.2"
|
|
102
102
|
},
|
package/src/index.core.ts
CHANGED
|
@@ -318,6 +318,7 @@ export type { RpcClient, StreamSession, LogMessage } from "@query-farm/vgi-rpc";
|
|
|
318
318
|
// GrantRefusedError to decline a grant.
|
|
319
319
|
export {
|
|
320
320
|
buildRpcServer,
|
|
321
|
+
grantKeysFromArgv,
|
|
321
322
|
INTROSPECT_PRINCIPALS_ENV,
|
|
322
323
|
type BuildRpcServerOptions,
|
|
323
324
|
type HostingOptions,
|
|
@@ -325,6 +326,7 @@ export {
|
|
|
325
326
|
} from "./rpc-server.js";
|
|
326
327
|
export {
|
|
327
328
|
AuthUnavailableError,
|
|
329
|
+
GrantKeys,
|
|
328
330
|
GrantRefusedError,
|
|
329
331
|
IdentityUnavailableError,
|
|
330
332
|
StatusError,
|
package/src/rpc-server.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
// exactly as it would against a single-protocol server.
|
|
23
23
|
|
|
24
24
|
import {
|
|
25
|
+
GrantKeys,
|
|
25
26
|
type GrantMinter,
|
|
26
27
|
IdentityImpl,
|
|
27
28
|
type Protocol,
|
|
@@ -117,6 +118,20 @@ export interface HostingOptions {
|
|
|
117
118
|
/** Maximum age of the caller's authentication for `issue_grant`, in
|
|
118
119
|
* seconds. vgi-rpc's default when omitted. */
|
|
119
120
|
maxAuthAge?: number;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Sealed-grant keys (vgi-rpc `GrantKeys`), turning on the framework's own
|
|
124
|
+
* grants on HTTP: `issue_grant` mints `vgig1.` tokens (unless
|
|
125
|
+
* {@link mintGrant} is supplied) and the worker's HTTP authentication accepts
|
|
126
|
+
* them back as bearer credentials, as the grant's owner. When omitted,
|
|
127
|
+
* `VGI_RPC_GRANT_KEYS` (comma-separated base64, exactly 32 bytes each, first
|
|
128
|
+
* mints, all verify), `VGI_RPC_GRANT_AUDIENCE` and
|
|
129
|
+
* `VGI_RPC_GRANT_MAX_TTL_SECONDS` are read; `serveVgiWorker` also accepts
|
|
130
|
+
* `--grant-key KEY` (repeatable). Unset everywhere, nothing changes. A
|
|
131
|
+
* malformed key refuses to start. `null` turns grants off regardless of the
|
|
132
|
+
* environment.
|
|
133
|
+
*/
|
|
134
|
+
grantKeys?: GrantKeys | null;
|
|
120
135
|
}
|
|
121
136
|
|
|
122
137
|
/** Server-level options passed through to `VgiRpcServer`. */
|
|
@@ -156,6 +171,9 @@ export function buildRpcServer(
|
|
|
156
171
|
serverId: options.serverId,
|
|
157
172
|
protocols,
|
|
158
173
|
identity,
|
|
174
|
+
// Resolved here, per transport, rather than by vgi-rpc's own environment
|
|
175
|
+
// default: identity (and so grants) is hosted on HTTP only.
|
|
176
|
+
grantKeys: null,
|
|
159
177
|
});
|
|
160
178
|
}
|
|
161
179
|
|
|
@@ -224,10 +242,15 @@ function buildIdentity(
|
|
|
224
242
|
env: Record<string, string | undefined> | undefined,
|
|
225
243
|
): IdentityImpl | undefined {
|
|
226
244
|
const { resolveToken, mintGrant } = hosting;
|
|
227
|
-
|
|
245
|
+
// Explicit option, else the environment. Read at server build, so a
|
|
246
|
+
// malformed key stops the worker before it serves anything.
|
|
247
|
+
const grantKeys = hosting.grantKeys === undefined ? GrantKeys.fromEnv(env ?? processEnv()) : hosting.grantKeys;
|
|
248
|
+
if (!resolveToken && !mintGrant && !grantKeys) return undefined;
|
|
228
249
|
return new IdentityImpl({
|
|
229
250
|
resolveToken,
|
|
251
|
+
// Absent with grant keys configured, vgi-rpc mints sealed grants itself.
|
|
230
252
|
mintGrant,
|
|
253
|
+
grantKeys: grantKeys ?? undefined,
|
|
231
254
|
// Only meaningful for `introspect_token`; a worker that mints but resolves
|
|
232
255
|
// nothing is not an oracle and needs no allowlist.
|
|
233
256
|
introspectPrincipals: resolveToken ? resolveIntrospectPrincipals(hosting.introspectPrincipals, env) : undefined,
|
|
@@ -271,3 +294,34 @@ function resolveIntrospectPrincipals(
|
|
|
271
294
|
}
|
|
272
295
|
return principals;
|
|
273
296
|
}
|
|
297
|
+
|
|
298
|
+
function processEnv(): Record<string, string | undefined> | undefined {
|
|
299
|
+
return (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Collect `--grant-key KEY` / `--grant-key=KEY` (repeatable, first mints) from
|
|
304
|
+
* an argv, as a `GrantKeys` -- or `undefined` when none was passed, so the
|
|
305
|
+
* environment still applies. Audience and lifetime come from
|
|
306
|
+
* `VGI_RPC_GRANT_AUDIENCE` / `VGI_RPC_GRANT_MAX_TTL_SECONDS`.
|
|
307
|
+
*
|
|
308
|
+
* @throws Error A malformed key: a worker refuses to start.
|
|
309
|
+
*/
|
|
310
|
+
export function grantKeysFromArgv(
|
|
311
|
+
argv: readonly string[],
|
|
312
|
+
env: Record<string, string | undefined> | undefined = processEnv(),
|
|
313
|
+
): GrantKeys | undefined {
|
|
314
|
+
const keys: string[] = [];
|
|
315
|
+
for (let i = 0; i < argv.length; i++) {
|
|
316
|
+
const a = argv[i];
|
|
317
|
+
if (a === "--grant-key") {
|
|
318
|
+
if (i + 1 >= argv.length) throw new Error("--grant-key requires a value");
|
|
319
|
+
keys.push(argv[++i]);
|
|
320
|
+
} else if (a.startsWith("--grant-key=")) {
|
|
321
|
+
keys.push(a.slice("--grant-key=".length));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (keys.length === 0) return undefined;
|
|
325
|
+
const fromEnv = GrantKeys.fromEnv({ ...(env ?? {}), VGI_RPC_GRANT_KEYS: keys.join(",") });
|
|
326
|
+
return fromEnv ?? undefined;
|
|
327
|
+
}
|
package/src/serve-entry.ts
CHANGED
|
@@ -29,10 +29,11 @@ import {
|
|
|
29
29
|
observePeerIdentity,
|
|
30
30
|
peerIdentityPrimary,
|
|
31
31
|
type AuthenticateFn,
|
|
32
|
+
GrantKeys,
|
|
32
33
|
type PeerResolutionOptions,
|
|
33
34
|
} from "@query-farm/vgi-rpc";
|
|
34
35
|
import { createVgiFetch } from "./http/fetch.js";
|
|
35
|
-
import type
|
|
36
|
+
import { grantKeysFromArgv, type HostingOptions } from "./rpc-server.js";
|
|
36
37
|
|
|
37
38
|
/** Environment variables `serveVgiWorker` reads. Injectable for testing. */
|
|
38
39
|
export interface ServeEnv {
|
|
@@ -40,6 +41,14 @@ export interface ServeEnv {
|
|
|
40
41
|
VGI_SIGNING_KEY?: string;
|
|
41
42
|
VGI_TOKEN_TTL?: string;
|
|
42
43
|
CORS_ORIGINS?: string;
|
|
44
|
+
/** Sealed-grant keys: comma-separated base64, exactly 32 bytes each, first mints. */
|
|
45
|
+
VGI_RPC_GRANT_KEYS?: string;
|
|
46
|
+
/** Audience bound into sealed grants. */
|
|
47
|
+
VGI_RPC_GRANT_AUDIENCE?: string;
|
|
48
|
+
/** Sealed-grant lifetime ceiling, seconds (default 7 days). */
|
|
49
|
+
VGI_RPC_GRANT_MAX_TTL_SECONDS?: string;
|
|
50
|
+
/** Principals allowed to call `introspect_token`, comma-separated. */
|
|
51
|
+
VGI_INTROSPECT_PRINCIPALS?: string;
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
export const DEFAULT_PORT = 8787;
|
|
@@ -94,6 +103,10 @@ export interface ServeVgiWorkerOptions extends HostingOptions {
|
|
|
94
103
|
quiet?: boolean;
|
|
95
104
|
/** Environment source (default `process.env`). */
|
|
96
105
|
env?: ServeEnv;
|
|
106
|
+
/** Command-line arguments to read `--grant-key KEY` (repeatable, first mints)
|
|
107
|
+
* from. Default `process.argv.slice(2)`. A key here takes precedence over
|
|
108
|
+
* `VGI_RPC_GRANT_KEYS`; an explicit {@link grantKeys} over both. */
|
|
109
|
+
argv?: readonly string[];
|
|
97
110
|
/** Authenticates each request, returning the caller's `AuthContext`.
|
|
98
111
|
* Omit for an anonymous worker (the default). */
|
|
99
112
|
authenticate?: AuthenticateFn;
|
|
@@ -247,6 +260,11 @@ export function createVgiWorkerFetch(
|
|
|
247
260
|
mintGrant: opts.mintGrant,
|
|
248
261
|
introspectPrincipals: opts.introspectPrincipals,
|
|
249
262
|
maxAuthAge: opts.maxAuthAge,
|
|
263
|
+
grantKeys:
|
|
264
|
+
opts.grantKeys !== undefined
|
|
265
|
+
? opts.grantKeys
|
|
266
|
+
: (grantKeysFromArgv(opts.argv ?? process.argv.slice(2), env as Record<string, string | undefined>) ??
|
|
267
|
+
GrantKeys.fromEnv(env as Record<string, string | undefined>)),
|
|
250
268
|
});
|
|
251
269
|
}
|
|
252
270
|
|
package/src/worker-cf-entry.ts
CHANGED