@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 CHANGED
@@ -615,6 +615,37 @@ never return `null` for one: both read as a definitive answer. Return `null` onl
615
615
  when the store answered and the credential is unknown; throw `GrantRefusedError` to
616
616
  decline a grant.
617
617
 
618
+ ### Sealed grants: minted credentials that log their owner in
619
+
620
+ `issue_grant` mints a credential meant for unattended automation to present later
621
+ as an ordinary bearer. Configure **grant keys** and the worker both mints and
622
+ accepts them, with no storage and no code:
623
+
624
+ ```console
625
+ $ VGI_RPC_GRANT_KEYS="$(openssl rand -base64 32)" bun run scripts/serve.ts
626
+ # or: bun run scripts/serve.ts --grant-key "$(openssl rand -base64 32)"
627
+ ```
628
+
629
+ - `VGI_RPC_GRANT_KEYS`: comma-separated standard base64, exactly 32 bytes each. The
630
+ first mints; every key verifies. Rotate by adding the new key first and removing
631
+ the old one once its grants have expired. `--grant-key KEY` (repeatable) on
632
+ `serveVgiWorker`, or the `grantKeys` option (`GrantKeys.parse([...])`, required on
633
+ Cloudflare Workers, where there is no process environment) work too.
634
+ - Optional `VGI_RPC_GRANT_AUDIENCE` (bind grants to this deployment) and
635
+ `VGI_RPC_GRANT_MAX_TTL_SECONDS` (default 7 days).
636
+ - With keys set, HTTP hosts `issue_grant` (minted by the framework unless you supply
637
+ `mintGrant`) and the worker's HTTP authentication accepts `vgig1.` grants after
638
+ your own `authenticate`, as the grant's owner (domain `grant`). A caller
639
+ authenticated by a grant has no `auth_time`, so it cannot mint another grant.
640
+ - A worker that supplies `resolveToken` also has bearers your `authenticate` did not
641
+ accept resolved through it (domain `token`); an `AuthUnavailableError` from the hook
642
+ is a 503 with `Retry-After`, never a 401.
643
+ - Your `authenticate` must throw (not return anonymous) for a bearer it does not
644
+ recognise, or the chain ends before grants are tried.
645
+ - No key set, nothing changes. A malformed key stops the worker at startup.
646
+ - Sealed grants are not individually revocable: keep the lifetime short; removing a
647
+ key revokes every grant it minted.
648
+
618
649
  ## Runtimes & entry points
619
650
 
620
651
  The package ships a backend-agnostic Arrow facade and selects an implementation at
@@ -43,7 +43,7 @@ export type { VgiClientOptions, TableFunctionOptions, ScalarFunctionOptions, Tab
43
43
  export { str, bytes, int, int32, float, float32, bool, toSchema, OutputCollector, AuthContext } from "@query-farm/vgi-rpc";
44
44
  export { subprocessConnect, httpConnect, tcpConnect } from "@query-farm/vgi-rpc";
45
45
  export type { RpcClient, StreamSession, LogMessage } from "@query-farm/vgi-rpc";
46
- export { buildRpcServer, INTROSPECT_PRINCIPALS_ENV, type BuildRpcServerOptions, type HostingOptions, type ServerTransport, } from "./rpc-server.js";
47
- export { AuthUnavailableError, GrantRefusedError, IdentityUnavailableError, StatusError, } from "@query-farm/vgi-rpc";
46
+ export { buildRpcServer, grantKeysFromArgv, INTROSPECT_PRINCIPALS_ENV, type BuildRpcServerOptions, type HostingOptions, type ServerTransport, } from "./rpc-server.js";
47
+ export { AuthUnavailableError, GrantKeys, GrantRefusedError, IdentityUnavailableError, StatusError, } from "@query-farm/vgi-rpc";
48
48
  export type { GrantMinter, IssuedGrant, TokenIdentity, TokenResolver } from "@query-farm/vgi-rpc";
49
49
  export { normalizeSchemaPath, schemaPathDisplay, schemaPathKey, schemaPathsEqual, type SchemaPath } from "./schema-path.js";
package/dist/index.js CHANGED
@@ -13722,6 +13722,7 @@ import { subprocessConnect, httpConnect, tcpConnect } from "@query-farm/vgi-rpc"
13722
13722
 
13723
13723
  // src/rpc-server.ts
13724
13724
  import {
13725
+ GrantKeys,
13725
13726
  IdentityImpl,
13726
13727
  VgiRpcServer
13727
13728
  } from "@query-farm/vgi-rpc";
@@ -13738,7 +13739,8 @@ function buildRpcServer(primary, hosting, options) {
13738
13739
  return new VgiRpcServer(primary, {
13739
13740
  serverId: options.serverId,
13740
13741
  protocols,
13741
- identity
13742
+ identity,
13743
+ grantKeys: null
13742
13744
  });
13743
13745
  }
13744
13746
  function validatedHostedProtocols(hosting, primary) {
@@ -13781,11 +13783,13 @@ function validatedHostedProtocols(hosting, primary) {
13781
13783
  }
13782
13784
  function buildIdentity(hosting, env) {
13783
13785
  const { resolveToken, mintGrant } = hosting;
13784
- if (!resolveToken && !mintGrant)
13786
+ const grantKeys = hosting.grantKeys === undefined ? GrantKeys.fromEnv(env ?? processEnv()) : hosting.grantKeys;
13787
+ if (!resolveToken && !mintGrant && !grantKeys)
13785
13788
  return;
13786
13789
  return new IdentityImpl({
13787
13790
  resolveToken,
13788
13791
  mintGrant,
13792
+ grantKeys: grantKeys ?? undefined,
13789
13793
  introspectPrincipals: resolveToken ? resolveIntrospectPrincipals(hosting.introspectPrincipals, env) : undefined,
13790
13794
  maxAuthAge: hosting.maxAuthAge
13791
13795
  });
@@ -13806,10 +13810,31 @@ function resolveIntrospectPrincipals(explicit, env) {
13806
13810
  }
13807
13811
  return principals;
13808
13812
  }
13813
+ function processEnv() {
13814
+ return globalThis.process?.env;
13815
+ }
13816
+ function grantKeysFromArgv(argv, env = processEnv()) {
13817
+ const keys = [];
13818
+ for (let i = 0;i < argv.length; i++) {
13819
+ const a = argv[i];
13820
+ if (a === "--grant-key") {
13821
+ if (i + 1 >= argv.length)
13822
+ throw new Error("--grant-key requires a value");
13823
+ keys.push(argv[++i]);
13824
+ } else if (a.startsWith("--grant-key=")) {
13825
+ keys.push(a.slice("--grant-key=".length));
13826
+ }
13827
+ }
13828
+ if (keys.length === 0)
13829
+ return;
13830
+ const fromEnv = GrantKeys.fromEnv({ ...env ?? {}, VGI_RPC_GRANT_KEYS: keys.join(",") });
13831
+ return fromEnv ?? undefined;
13832
+ }
13809
13833
 
13810
13834
  // src/index.core.ts
13811
13835
  import {
13812
13836
  AuthUnavailableError,
13837
+ GrantKeys as GrantKeys2,
13813
13838
  GrantRefusedError,
13814
13839
  IdentityUnavailableError,
13815
13840
  StatusError
@@ -14042,6 +14067,7 @@ export {
14042
14067
  FunctionStorageSqlite,
14043
14068
  FunctionType,
14044
14069
  GROUP_COLUMN_NAME,
14070
+ GrantKeys2 as GrantKeys,
14045
14071
  GrantRefusedError,
14046
14072
  INTROSPECT_PRINCIPALS_ENV,
14047
14073
  IdentityUnavailableError,
@@ -14163,6 +14189,7 @@ export {
14163
14189
  fromBrandedBigInt,
14164
14190
  fromBrandedNumber,
14165
14191
  storage as functionStorage,
14192
+ grantKeysFromArgv,
14166
14193
  httpConnect,
14167
14194
  int3 as int,
14168
14195
  int162 as int16,
@@ -14250,4 +14277,4 @@ export {
14250
14277
  utf82 as utf8
14251
14278
  };
14252
14279
 
14253
- //# debugId=03676B1C4ED9A21664756E2164756E21
14280
+ //# debugId=E43EFCB3DEBB959264756E2164756E21