rivetkit 2.3.20 → 2.3.21

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.
Files changed (130) hide show
  1. package/dist/browser/client.d.ts +43 -5
  2. package/dist/browser/client.js +302 -60
  3. package/dist/browser/client.js.map +1 -1
  4. package/dist/browser/inspector/client.js +2 -1
  5. package/dist/browser/inspector/client.js.map +1 -1
  6. package/dist/tsup/actor/errors.cjs.map +1 -1
  7. package/dist/tsup/agent-os/index.cjs +2 -1
  8. package/dist/tsup/agent-os/index.cjs.map +1 -1
  9. package/dist/tsup/agent-os/index.d.cts +32 -4
  10. package/dist/tsup/agent-os/index.d.ts +32 -4
  11. package/dist/tsup/agent-os/index.js +2 -1
  12. package/dist/tsup/agent-os/index.js.map +1 -1
  13. package/dist/tsup/{chunk-553CIQJY.js → chunk-2CI2IEVN.js} +2 -2
  14. package/dist/tsup/{chunk-W2PXGWMR.js → chunk-5EGLHQJJ.js} +2 -2
  15. package/dist/tsup/{chunk-DMQUERRN.js → chunk-5IBUZFLP.js} +2 -2
  16. package/dist/tsup/{chunk-76YTS32R.cjs → chunk-6LRPYB4Q.cjs} +4 -4
  17. package/dist/tsup/chunk-6LRPYB4Q.cjs.map +1 -0
  18. package/dist/tsup/{chunk-WAISW7KR.cjs → chunk-APOANSDG.cjs} +10 -10
  19. package/dist/tsup/chunk-APOANSDG.cjs.map +1 -0
  20. package/dist/tsup/{chunk-IHKDFKM4.cjs → chunk-ARGNQGYG.cjs} +3 -3
  21. package/dist/tsup/chunk-ARGNQGYG.cjs.map +1 -0
  22. package/dist/tsup/{chunk-A36FMKOV.cjs → chunk-B5SNXPSS.cjs} +448 -199
  23. package/dist/tsup/chunk-B5SNXPSS.cjs.map +1 -0
  24. package/dist/tsup/{chunk-SNNVRHLX.js → chunk-CJXBGTVT.js} +2 -2
  25. package/dist/tsup/{chunk-CVLNRT7O.cjs → chunk-GHKQC4PD.cjs} +3 -3
  26. package/dist/tsup/chunk-GHKQC4PD.cjs.map +1 -0
  27. package/dist/tsup/chunk-JX2ITWAV.cjs.map +1 -1
  28. package/dist/tsup/chunk-KVFIONKQ.cjs.map +1 -1
  29. package/dist/tsup/{chunk-2CE5UNWK.cjs → chunk-KWGN2ZBJ.cjs} +3 -2
  30. package/dist/tsup/chunk-KWGN2ZBJ.cjs.map +1 -0
  31. package/dist/tsup/{chunk-3WS7Z64U.cjs → chunk-LWSLKKZ5.cjs} +3 -3
  32. package/dist/tsup/chunk-LWSLKKZ5.cjs.map +1 -0
  33. package/dist/tsup/chunk-MYWZCDML.cjs.map +1 -1
  34. package/dist/tsup/{chunk-RSIOGT2U.cjs → chunk-NL2EIKK6.cjs} +7 -7
  35. package/dist/tsup/chunk-NL2EIKK6.cjs.map +1 -0
  36. package/dist/tsup/chunk-NWCAIWHL.cjs.map +1 -1
  37. package/dist/tsup/{chunk-5G2J26VM.js → chunk-PYRI75DL.js} +314 -65
  38. package/dist/tsup/chunk-PYRI75DL.js.map +1 -0
  39. package/dist/tsup/{chunk-2NXLTFWI.js → chunk-RFN3BG6D.js} +3 -3
  40. package/dist/tsup/chunk-RFN3BG6D.js.map +1 -0
  41. package/dist/tsup/{chunk-DHWZTUKA.js → chunk-RGIZNZYO.js} +3 -2
  42. package/dist/tsup/chunk-RGIZNZYO.js.map +1 -0
  43. package/dist/tsup/{chunk-TZVAVPGJ.js → chunk-VMMXXVDO.js} +4 -4
  44. package/dist/tsup/client/mod.cjs +5 -5
  45. package/dist/tsup/client/mod.cjs.map +1 -1
  46. package/dist/tsup/client/mod.d.cts +12 -3
  47. package/dist/tsup/client/mod.d.ts +12 -3
  48. package/dist/tsup/client/mod.js +4 -4
  49. package/dist/tsup/common/log.cjs +2 -2
  50. package/dist/tsup/common/log.cjs.map +1 -1
  51. package/dist/tsup/common/log.js +1 -1
  52. package/dist/tsup/common/websocket.cjs +3 -3
  53. package/dist/tsup/common/websocket.cjs.map +1 -1
  54. package/dist/tsup/common/websocket.js +2 -2
  55. package/dist/tsup/{config-Be-VOHaX.d.ts → config-Bc1nnvUl.d.ts} +35 -5
  56. package/dist/tsup/{config-Ct5uXxXk.d.cts → config-Do_0guSS.d.cts} +35 -5
  57. package/dist/tsup/{context-Cw-11Q-B.d.cts → context-CgNkhjdY.d.cts} +1 -1
  58. package/dist/tsup/{context-CXLa0BIM.d.ts → context-oGQxZDPz.d.ts} +1 -1
  59. package/dist/tsup/db/drizzle.cjs +2 -2
  60. package/dist/tsup/db/drizzle.cjs.map +1 -1
  61. package/dist/tsup/db/drizzle.js +1 -1
  62. package/dist/tsup/db/mod.cjs +3 -3
  63. package/dist/tsup/db/mod.cjs.map +1 -1
  64. package/dist/tsup/db/mod.js +2 -2
  65. package/dist/tsup/dynamic/mod.cjs.map +1 -1
  66. package/dist/tsup/dynamic/mod.d.cts +2 -1
  67. package/dist/tsup/dynamic/mod.d.ts +2 -1
  68. package/dist/tsup/inspector/mod.cjs +5 -5
  69. package/dist/tsup/inspector/mod.cjs.map +1 -1
  70. package/dist/tsup/inspector/mod.js +4 -4
  71. package/dist/tsup/inspector/workflow.cjs +4 -4
  72. package/dist/tsup/inspector/workflow.cjs.map +1 -1
  73. package/dist/tsup/inspector/workflow.js +3 -3
  74. package/dist/tsup/inspector-tab/mod.cjs.map +1 -1
  75. package/dist/tsup/inspector-tab/mod.d.cts +3 -2
  76. package/dist/tsup/inspector-tab/mod.d.ts +3 -2
  77. package/dist/tsup/mod.cjs +105 -115
  78. package/dist/tsup/mod.cjs.map +1 -1
  79. package/dist/tsup/mod.d.cts +4 -3
  80. package/dist/tsup/mod.d.ts +4 -3
  81. package/dist/tsup/mod.js +29 -39
  82. package/dist/tsup/mod.js.map +1 -1
  83. package/dist/tsup/test/mod.cjs +6 -6
  84. package/dist/tsup/test/mod.cjs.map +1 -1
  85. package/dist/tsup/test/mod.d.cts +2 -1
  86. package/dist/tsup/test/mod.d.ts +2 -1
  87. package/dist/tsup/test/mod.js +4 -4
  88. package/dist/tsup/unstable/migrations.cjs.map +1 -1
  89. package/dist/tsup/utils.cjs +2 -2
  90. package/dist/tsup/utils.cjs.map +1 -1
  91. package/dist/tsup/utils.js +1 -1
  92. package/dist/tsup/workflow/mod.cjs +7 -7
  93. package/dist/tsup/workflow/mod.cjs.map +1 -1
  94. package/dist/tsup/workflow/mod.d.cts +4 -3
  95. package/dist/tsup/workflow/mod.d.ts +4 -3
  96. package/dist/tsup/workflow/mod.js +4 -4
  97. package/package.json +10 -9
  98. package/src/client/actor-conn.ts +34 -1
  99. package/src/client/actor-handle.ts +29 -1
  100. package/src/client/auth.ts +37 -0
  101. package/src/client/client.ts +10 -2
  102. package/src/client/config.ts +12 -1
  103. package/src/client/mod.ts +9 -0
  104. package/src/client/token-provider.test.ts +86 -0
  105. package/src/client/token-provider.ts +93 -0
  106. package/src/client/utils.ts +1 -0
  107. package/src/engine-client/actor-websocket-client.ts +2 -1
  108. package/src/engine-client/api-endpoints.ts +28 -0
  109. package/src/engine-client/driver.ts +4 -0
  110. package/src/engine-client/mod.ts +201 -64
  111. package/src/registry/config/envoy.ts +1 -17
  112. package/src/registry/config/index.test.ts +2 -2
  113. package/src/registry/config/index.ts +7 -2
  114. package/src/registry/index.ts +28 -0
  115. package/dist/tsup/chunk-2CE5UNWK.cjs.map +0 -1
  116. package/dist/tsup/chunk-2NXLTFWI.js.map +0 -1
  117. package/dist/tsup/chunk-3WS7Z64U.cjs.map +0 -1
  118. package/dist/tsup/chunk-5G2J26VM.js.map +0 -1
  119. package/dist/tsup/chunk-76YTS32R.cjs.map +0 -1
  120. package/dist/tsup/chunk-A36FMKOV.cjs.map +0 -1
  121. package/dist/tsup/chunk-CVLNRT7O.cjs.map +0 -1
  122. package/dist/tsup/chunk-DHWZTUKA.js.map +0 -1
  123. package/dist/tsup/chunk-IHKDFKM4.cjs.map +0 -1
  124. package/dist/tsup/chunk-RSIOGT2U.cjs.map +0 -1
  125. package/dist/tsup/chunk-WAISW7KR.cjs.map +0 -1
  126. /package/dist/tsup/{chunk-553CIQJY.js.map → chunk-2CI2IEVN.js.map} +0 -0
  127. /package/dist/tsup/{chunk-W2PXGWMR.js.map → chunk-5EGLHQJJ.js.map} +0 -0
  128. /package/dist/tsup/{chunk-DMQUERRN.js.map → chunk-5IBUZFLP.js.map} +0 -0
  129. /package/dist/tsup/{chunk-SNNVRHLX.js.map → chunk-CJXBGTVT.js.map} +0 -0
  130. /package/dist/tsup/{chunk-TZVAVPGJ.js.map → chunk-VMMXXVDO.js.map} +0 -0
@@ -220,7 +220,7 @@ function noopNext() {
220
220
  // package.json
221
221
  var package_default = {
222
222
  name: "rivetkit",
223
- version: "2.3.20",
223
+ version: "2.3.21",
224
224
  description: "Lightweight libraries for building stateful actors on edge platforms",
225
225
  license: "Apache-2.0",
226
226
  keywords: [
@@ -433,6 +433,7 @@ var package_default = {
433
433
  "@rivet-dev/agent-os-core": "^0.1.1",
434
434
  "@rivet-dev/services": "^0.1.5",
435
435
  "@rivetkit/bare-ts": "^0.6.2",
436
+ "@rivetkit/engine-api-full": "workspace:*",
436
437
  "@rivetkit/engine-cli": "workspace:*",
437
438
  "@rivetkit/engine-envoy-protocol": "workspace:*",
438
439
  "@rivetkit/on-change": "6.0.1",
@@ -1145,6 +1146,73 @@ function deserializeActorKey(keyString) {
1145
1146
  return parts;
1146
1147
  }
1147
1148
 
1149
+ // src/client/token-provider.ts
1150
+ function isInvalidToken(group, code) {
1151
+ return group === "auth" && (code === "invalid_token" || code === "token_expired");
1152
+ }
1153
+ function isInvalidTokenResponse(response) {
1154
+ if (response.status !== 401) return false;
1155
+ const error = response.headers.get("x-rivet-error");
1156
+ if (!error) return false;
1157
+ const separator = error.indexOf(".");
1158
+ return separator > 0 && isInvalidToken(error.slice(0, separator), error.slice(separator + 1));
1159
+ }
1160
+ var TokenProvider = class {
1161
+ constructor(getToken) {
1162
+ this.getToken = getToken;
1163
+ }
1164
+ #cached;
1165
+ #pending;
1166
+ #pendingForce = false;
1167
+ async current() {
1168
+ if (this.#cached && (this.#cached.expiresAt === void 0 || Date.now() < this.#cached.expiresAt - 5e3)) {
1169
+ return this.#cached.token;
1170
+ }
1171
+ return this.#load(false);
1172
+ }
1173
+ /** A late rejection for an older token must never erase a newer credential. */
1174
+ async refreshIfCurrent(failedToken) {
1175
+ if (this.#pending !== void 0) {
1176
+ const wasForced = this.#pendingForce;
1177
+ const refreshed = await this.#pending;
1178
+ if (wasForced || refreshed !== failedToken) return refreshed;
1179
+ }
1180
+ if (this.#cached?.token !== failedToken) {
1181
+ return this.current();
1182
+ }
1183
+ return this.#load(true);
1184
+ }
1185
+ #load(forceRefresh) {
1186
+ if (this.#pending !== void 0) return this.#pending;
1187
+ const pending = Promise.resolve().then(() => this.getToken({ forceRefresh })).then((token) => {
1188
+ if (typeof token !== "string" || token.length === 0) {
1189
+ throw new Error(
1190
+ "getToken must resolve to a nonempty string"
1191
+ );
1192
+ }
1193
+ this.#cached = { token, expiresAt: jwtExpiration(token) };
1194
+ return token;
1195
+ }).finally(() => {
1196
+ if (this.#pending === pending) this.#pending = void 0;
1197
+ });
1198
+ this.#pending = pending;
1199
+ this.#pendingForce = forceRefresh;
1200
+ return pending;
1201
+ }
1202
+ };
1203
+ function jwtExpiration(token) {
1204
+ try {
1205
+ const payload = token.split(".")[1];
1206
+ if (!payload) return void 0;
1207
+ const claims = JSON.parse(
1208
+ atob(payload.replace(/-/g, "+").replace(/_/g, "/"))
1209
+ );
1210
+ return typeof claims.exp === "number" && Number.isSafeInteger(claims.exp) ? claims.exp * 1e3 : void 0;
1211
+ } catch {
1212
+ return void 0;
1213
+ }
1214
+ }
1215
+
1148
1216
  // src/common/actor-router-consts.ts
1149
1217
  var PATH_CONNECT = "/connect";
1150
1218
  var PATH_WEBSOCKET_BASE = "/websocket";
@@ -1230,6 +1298,25 @@ function buildGuardHeaders(runConfig, actorRequest, options) {
1230
1298
  // src/client/config.ts
1231
1299
  import z3 from "zod/v4";
1232
1300
 
1301
+ // src/common/engine.ts
1302
+ var ENGINE_HOST = "127.0.0.1";
1303
+ var ENGINE_PORT = 6420;
1304
+ var ENGINE_ENDPOINT = buildEngineEndpoint(ENGINE_HOST, ENGINE_PORT);
1305
+ function buildEngineEndpoint(host, port) {
1306
+ const urlHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
1307
+ return `http://${urlHost}:${port}`;
1308
+ }
1309
+ function isLocalEngineEndpoint(endpoint) {
1310
+ let url;
1311
+ try {
1312
+ url = new URL(endpoint);
1313
+ } catch {
1314
+ return false;
1315
+ }
1316
+ const hostname = url.hostname.toLowerCase();
1317
+ return hostname === "localhost" || hostname === "0.0.0.0" || hostname === "::" || hostname === "[::]" || hostname === "::1" || hostname === "[::1]" || /^127(?:\.\d{1,3}){0,3}$/.test(hostname);
1318
+ }
1319
+
1233
1320
  // src/utils/endpoint-parser.ts
1234
1321
  function tryParseEndpoint(ctx, options) {
1235
1322
  const {
@@ -1327,6 +1414,8 @@ var ClientConfigSchemaBase = z3.object({
1327
1414
  }),
1328
1415
  /** Token to use to authenticate with the API. */
1329
1416
  token: z3.string().optional().transform((val) => val ?? getRivetToken()),
1417
+ /** Issue and renew a client token on demand. Overrides the static token when provided. */
1418
+ getToken: z3.custom().optional(),
1330
1419
  /** Namespace to connect to. */
1331
1420
  namespace: z3.string().optional().transform((val) => val ?? getRivetNamespace()),
1332
1421
  /** Name of the envoy pool. This is used to group together envoys in to different pools. */
@@ -1368,7 +1457,7 @@ function transformClientConfig(config, ctx) {
1368
1457
  ...config,
1369
1458
  endpoint: parsedEndpoint?.endpoint,
1370
1459
  namespace: parsedEndpoint?.namespace ?? config.namespace ?? "default",
1371
- token: parsedEndpoint?.token ?? config.token
1460
+ token: parsedEndpoint?.token ?? config.token ?? (config.endpoint && isLocalEngineEndpoint(config.endpoint) ? "default" : void 0)
1372
1461
  };
1373
1462
  }
1374
1463
 
@@ -1679,7 +1768,8 @@ async function openWebSocketToGateway(runConfig, gatewayUrl, encoding, params, o
1679
1768
  const ackHookToken = typeof process !== "undefined" && process.env.VITEST ? crypto.randomUUID() : void 0;
1680
1769
  logger3().debug({
1681
1770
  msg: "opening websocket to actor via guard",
1682
- gatewayUrl
1771
+ // Gateway URLs can embed credentials; never include the path or query in logs.
1772
+ origin: new URL(gatewayUrl).origin
1683
1773
  });
1684
1774
  const ws = new WebSocket2(
1685
1775
  gatewayUrl,
@@ -3865,7 +3955,8 @@ async function sendHttpRequest(opts) {
3865
3955
  {
3866
3956
  metadata: responseData.metadata,
3867
3957
  rayId: rayId ?? void 0,
3868
- actor: responseData.actor
3958
+ actor: responseData.actor,
3959
+ statusCode: response.status
3869
3960
  }
3870
3961
  );
3871
3962
  } catch (error) {
@@ -3946,6 +4037,14 @@ async function apiCall(config, method, path, body) {
3946
4037
  }
3947
4038
 
3948
4039
  // src/engine-client/api-endpoints.ts
4040
+ async function issueToken(config, options) {
4041
+ return apiCall(config, "POST", "/auth/tokens", {
4042
+ namespace: config.namespace,
4043
+ subject: options.subject,
4044
+ duration: options.expiresIn,
4045
+ grants: options.grants
4046
+ });
4047
+ }
3949
4048
  async function getActor(config, _, actorId) {
3950
4049
  return apiCall(
3951
4050
  config,
@@ -4183,6 +4282,8 @@ function closeWebSocketIfOpen(ws, code, reason) {
4183
4282
  var RemoteEngineControlClient = class {
4184
4283
  #config;
4185
4284
  #metadataPromise;
4285
+ #tokenProvider;
4286
+ #webSocketTokens = /* @__PURE__ */ new WeakMap();
4186
4287
  constructor(runConfig) {
4187
4288
  if (getNextPhase() === "phase-production-build") {
4188
4289
  logger3().info(
@@ -4191,6 +4292,8 @@ var RemoteEngineControlClient = class {
4191
4292
  runConfig.disableMetadataLookup = true;
4192
4293
  }
4193
4294
  this.#config = { ...runConfig };
4295
+ if (runConfig.getToken)
4296
+ this.#tokenProvider = new TokenProvider(runConfig.getToken);
4194
4297
  if (!runConfig.disableMetadataLookup) {
4195
4298
  this.#metadataPromise = lookupMetadataCached(this.#config).then(
4196
4299
  (metadataData) => {
@@ -4199,14 +4302,13 @@ var RemoteEngineControlClient = class {
4199
4302
  if (metadataData.clientNamespace) {
4200
4303
  this.#config.namespace = metadataData.clientNamespace;
4201
4304
  }
4202
- if (metadataData.clientToken) {
4305
+ if (metadataData.clientToken && !this.#tokenProvider) {
4203
4306
  this.#config.token = metadataData.clientToken;
4204
4307
  }
4205
4308
  logger3().info({
4206
4309
  msg: "overriding client endpoint",
4207
4310
  endpoint: metadataData.clientEndpoint,
4208
- namespace: metadataData.clientNamespace,
4209
- token: metadataData.clientToken
4311
+ namespace: metadataData.clientNamespace
4210
4312
  });
4211
4313
  }
4212
4314
  logger3().info({
@@ -4219,12 +4321,47 @@ var RemoteEngineControlClient = class {
4219
4321
  );
4220
4322
  }
4221
4323
  }
4324
+ async #authorizedConfig() {
4325
+ if (!this.#tokenProvider) return this.#config;
4326
+ return { ...this.#config, token: await this.#tokenProvider.current() };
4327
+ }
4328
+ async #withCredential(request, readOnly = false) {
4329
+ const config = await this.#authorizedConfig();
4330
+ try {
4331
+ return await request(config);
4332
+ } catch (error) {
4333
+ if (this.#tokenProvider && config.token && error instanceof RivetError && error.statusCode === 401 && isInvalidToken(error.group, error.code)) {
4334
+ if (readOnly) {
4335
+ const token = await this.#tokenProvider.refreshIfCurrent(
4336
+ config.token
4337
+ );
4338
+ return request({ ...this.#config, token });
4339
+ }
4340
+ await this.#tokenProvider.refreshIfCurrent(config.token);
4341
+ }
4342
+ throw error;
4343
+ }
4344
+ }
4345
+ async issueToken(options) {
4346
+ await this.#metadataPromise;
4347
+ const response = await this.#withCredential(
4348
+ (config) => issueToken(config, options)
4349
+ );
4350
+ return {
4351
+ token: response.token,
4352
+ issuedAt: response.issued_ts,
4353
+ expiresAt: response.expires_ts
4354
+ };
4355
+ }
4222
4356
  async getForId({
4223
4357
  name,
4224
4358
  actorId
4225
4359
  }) {
4226
4360
  await this.#metadataPromise;
4227
- const response = await getActor(this.#config, name, actorId);
4361
+ const response = await this.#withCredential(
4362
+ (config) => getActor(config, name, actorId),
4363
+ true
4364
+ );
4228
4365
  const actor = response.actors[0];
4229
4366
  if (!actor) return void 0;
4230
4367
  if (actor.name !== name) {
@@ -4245,7 +4382,10 @@ var RemoteEngineControlClient = class {
4245
4382
  await this.#metadataPromise;
4246
4383
  logger3().debug({ msg: "getWithKey: searching for actor", name, key });
4247
4384
  try {
4248
- const response = await getActorByKey(this.#config, name, key);
4385
+ const response = await this.#withCredential(
4386
+ (config) => getActorByKey(config, name, key),
4387
+ true
4388
+ );
4249
4389
  const actor = response.actors[0];
4250
4390
  if (!actor) return void 0;
4251
4391
  logger3().debug({
@@ -4278,16 +4418,18 @@ var RemoteEngineControlClient = class {
4278
4418
  key
4279
4419
  });
4280
4420
  try {
4281
- const { actor, created } = await getOrCreateActor(this.#config, {
4282
- datacenter: region,
4283
- name,
4284
- key: serializeActorKey(key),
4285
- runner_name_selector: poolName ?? this.#config.poolName,
4286
- input: actorInput ? uint8ArrayToBase64(
4287
- encodeCborCompat(actorInput)
4288
- ) : void 0,
4289
- crash_policy: crashPolicy ?? "sleep"
4290
- });
4421
+ const { actor, created } = await this.#withCredential(
4422
+ (config) => getOrCreateActor(config, {
4423
+ datacenter: region,
4424
+ name,
4425
+ key: serializeActorKey(key),
4426
+ runner_name_selector: poolName ?? this.#config.poolName,
4427
+ input: actorInput ? uint8ArrayToBase64(
4428
+ encodeCborCompat(actorInput)
4429
+ ) : void 0,
4430
+ crash_policy: crashPolicy ?? "sleep"
4431
+ })
4432
+ );
4291
4433
  logger3().info({
4292
4434
  msg: "getOrCreateWithKey: actor ready",
4293
4435
  actorId: actor.actor_id,
@@ -4303,7 +4445,10 @@ var RemoteEngineControlClient = class {
4303
4445
  name,
4304
4446
  key
4305
4447
  });
4306
- const response = await getActorByKey(this.#config, name, key);
4448
+ const response = await this.#withCredential(
4449
+ (config) => getActorByKey(config, name, key),
4450
+ true
4451
+ );
4307
4452
  const existing = response.actors[0];
4308
4453
  if (!existing) throw error;
4309
4454
  logger3().info({
@@ -4327,14 +4472,18 @@ var RemoteEngineControlClient = class {
4327
4472
  }) {
4328
4473
  await this.#metadataPromise;
4329
4474
  logger3().info({ msg: "creating actor via engine api", name, key });
4330
- const result = await createActor(this.#config, {
4331
- datacenter: region,
4332
- name,
4333
- runner_name_selector: poolName ?? this.#config.poolName,
4334
- key: serializeActorKey(key),
4335
- input: input ? uint8ArrayToBase64(encodeCborCompat(input)) : void 0,
4336
- crash_policy: crashPolicy ?? "sleep"
4337
- });
4475
+ const result = await this.#withCredential(
4476
+ (config) => createActor(config, {
4477
+ datacenter: region,
4478
+ name,
4479
+ runner_name_selector: poolName ?? this.#config.poolName,
4480
+ key: serializeActorKey(key),
4481
+ input: input ? uint8ArrayToBase64(
4482
+ encodeCborCompat(input)
4483
+ ) : void 0,
4484
+ crash_policy: crashPolicy ?? "sleep"
4485
+ })
4486
+ );
4338
4487
  logger3().info({
4339
4488
  msg: "actor created",
4340
4489
  actorId: result.actor.actor_id,
@@ -4346,43 +4495,60 @@ var RemoteEngineControlClient = class {
4346
4495
  async listActors({ name }) {
4347
4496
  await this.#metadataPromise;
4348
4497
  logger3().debug({ msg: "listing actors via engine api", name });
4349
- const response = await listActorsByName(this.#config, name);
4498
+ const response = await this.#withCredential(
4499
+ (config) => listActorsByName(config, name),
4500
+ true
4501
+ );
4350
4502
  return response.actors.map(apiActorToOutput);
4351
4503
  }
4352
4504
  async destroyActor(actorId) {
4353
4505
  await this.#metadataPromise;
4354
4506
  logger3().info({ msg: "destroying actor via engine api", actorId });
4355
- await destroyActor(this.#config, actorId);
4507
+ await this.#withCredential((config) => destroyActor(config, actorId));
4356
4508
  logger3().info({ msg: "actor destroyed", actorId });
4357
4509
  }
4358
4510
  async sendRequest(target, actorRequest, options = {}) {
4359
4511
  await this.#metadataPromise;
4360
- const path = requestPath(actorRequest);
4361
- const gatewayUrl = this.#buildGatewayUrlForTarget(
4362
- target,
4363
- path,
4364
- options
4365
- );
4512
+ const config = await this.#authorizedConfig();
4366
4513
  const httpOptions = {
4367
4514
  ...options,
4368
4515
  directActorId: shouldSkipReadyWait(options) ? directActorIdFromTarget(target) : void 0
4369
4516
  };
4370
- return sendHttpRequestToGateway(
4371
- this.#config,
4372
- gatewayUrl,
4517
+ const send = (current) => sendHttpRequestToGateway(
4518
+ current,
4519
+ this.#buildGatewayUrlForTarget(
4520
+ current,
4521
+ target,
4522
+ requestPath(actorRequest),
4523
+ options
4524
+ ),
4373
4525
  actorRequest,
4374
4526
  httpOptions
4375
4527
  );
4528
+ const response = await send(config);
4529
+ if (!this.#tokenProvider || !config.token || !isInvalidTokenResponse(response))
4530
+ return response;
4531
+ if ((actorRequest.method === "GET" || actorRequest.method === "HEAD") && !("getOrCreateForKey" in target)) {
4532
+ const token = await this.#tokenProvider.refreshIfCurrent(
4533
+ config.token
4534
+ );
4535
+ await response.body?.cancel();
4536
+ return send({ ...this.#config, token });
4537
+ }
4538
+ await this.#tokenProvider.refreshIfCurrent(config.token);
4539
+ return response;
4376
4540
  }
4377
4541
  async openWebSocket(path, target, encoding, params, options = {}) {
4378
4542
  await this.#metadataPromise;
4543
+ const config = await this.#authorizedConfig();
4379
4544
  const gatewayUrl = this.#buildGatewayUrlForTarget(
4545
+ config,
4380
4546
  target,
4381
4547
  path,
4382
4548
  options
4383
4549
  );
4384
- return openWebSocketToGateway(
4385
- this.#config,
4550
+ const ws = await openWebSocketToGateway(
4551
+ config,
4386
4552
  gatewayUrl,
4387
4553
  encoding,
4388
4554
  params,
@@ -4391,24 +4557,58 @@ var RemoteEngineControlClient = class {
4391
4557
  directActorId: shouldSkipReadyWait(options) ? directActorIdFromTarget(target) : void 0
4392
4558
  }
4393
4559
  );
4560
+ this.#watchWebSocketAuth(ws, config.token);
4561
+ return ws;
4562
+ }
4563
+ async refreshAuthToken(ws) {
4564
+ const context2 = this.#webSocketTokens.get(ws);
4565
+ if (!context2?.refresh) return false;
4566
+ await context2.refresh;
4567
+ return true;
4568
+ }
4569
+ #watchWebSocketAuth(ws, token) {
4570
+ const tokenProvider = this.#tokenProvider;
4571
+ if (!tokenProvider || !token) return;
4572
+ const context2 = {};
4573
+ this.#webSocketTokens.set(ws, context2);
4574
+ ws.addEventListener(
4575
+ "close",
4576
+ (event) => {
4577
+ if (event.code !== 1008 || !event.reason) return;
4578
+ const separator = event.reason.indexOf(".");
4579
+ if (separator < 0) return;
4580
+ const code = event.reason.slice(separator + 1).split(/[\s:#]/, 1)[0];
4581
+ if (isInvalidToken(event.reason.slice(0, separator), code)) {
4582
+ context2.refresh = tokenProvider.refreshIfCurrent(token).catch((error) => {
4583
+ logger3().warn(
4584
+ "failed to renew authentication token"
4585
+ );
4586
+ throw error;
4587
+ });
4588
+ context2.refresh.catch(() => {
4589
+ });
4590
+ }
4591
+ }
4592
+ );
4394
4593
  }
4395
4594
  async buildGatewayUrl(target, options = {}) {
4396
4595
  await this.#metadataPromise;
4397
- return this.#buildGatewayUrlForTarget(target, "", options);
4596
+ return this.#buildGatewayUrlForTarget(
4597
+ await this.#authorizedConfig(),
4598
+ target,
4599
+ "",
4600
+ options
4601
+ );
4398
4602
  }
4399
4603
  async proxyRequest(_c, actorRequest, actorId) {
4400
- await this.#metadataPromise;
4401
- const gatewayUrl = this.#buildGatewayUrlForTarget(
4402
- { directId: actorId },
4403
- requestPath(actorRequest)
4404
- );
4405
- return sendHttpRequestToGateway(this.#config, gatewayUrl, actorRequest);
4604
+ return this.sendRequest({ directId: actorId }, actorRequest);
4406
4605
  }
4407
4606
  async proxyWebSocket(c, path, actorId, encoding, params) {
4408
4607
  await this.#metadataPromise;
4409
- const upgradeWebSocket = this.#config.getUpgradeWebSocket?.();
4608
+ const config = await this.#authorizedConfig();
4609
+ const upgradeWebSocket = config.getUpgradeWebSocket?.();
4410
4610
  invariant3(upgradeWebSocket, "missing getUpgradeWebSocket");
4411
- const endpoint = getEndpoint(this.#config);
4611
+ const endpoint = getEndpoint(config);
4412
4612
  const guardUrl = combineUrlPath(endpoint, path);
4413
4613
  const wsGuardUrl = guardUrl.replace("http://", "ws://");
4414
4614
  logger3().debug({
@@ -4418,7 +4618,7 @@ var RemoteEngineControlClient = class {
4418
4618
  guardUrl
4419
4619
  });
4420
4620
  const protocols = buildWebSocketProtocols(
4421
- this.#config,
4621
+ config,
4422
4622
  encoding,
4423
4623
  params,
4424
4624
  void 0,
@@ -4436,8 +4636,8 @@ var RemoteEngineControlClient = class {
4436
4636
  setGetUpgradeWebSocket(getUpgradeWebSocket) {
4437
4637
  this.#config.getUpgradeWebSocket = getUpgradeWebSocket;
4438
4638
  }
4439
- #buildGatewayUrlForTarget(target, path, options = {}) {
4440
- const endpoint = getEndpoint(this.#config);
4639
+ #buildGatewayUrlForTarget(config, target, path, options = {}) {
4640
+ const endpoint = getEndpoint(config);
4441
4641
  if (shouldSkipReadyWait(options) && directActorIdFromTarget(target) && canUseDirectSkipReadyWaitPath(path)) {
4442
4642
  return combineUrlPath(endpoint, path);
4443
4643
  }
@@ -4445,7 +4645,7 @@ var RemoteEngineControlClient = class {
4445
4645
  return buildActorGatewayUrl(
4446
4646
  endpoint,
4447
4647
  target.directId,
4448
- this.#config.token,
4648
+ config.token,
4449
4649
  path
4450
4650
  );
4451
4651
  }
@@ -4453,20 +4653,20 @@ var RemoteEngineControlClient = class {
4453
4653
  return buildActorGatewayUrl(
4454
4654
  endpoint,
4455
4655
  target.getForId.actorId,
4456
- this.#config.token,
4656
+ config.token,
4457
4657
  path
4458
4658
  );
4459
4659
  }
4460
4660
  if ("getForKey" in target || "getOrCreateForKey" in target) {
4461
4661
  return buildActorQueryGatewayUrl(
4462
4662
  endpoint,
4463
- this.#config.namespace,
4663
+ config.namespace,
4464
4664
  target,
4465
- this.#config.token,
4665
+ config.token,
4466
4666
  path,
4467
- this.#config.maxInputSize,
4667
+ config.maxInputSize,
4468
4668
  void 0,
4469
- "getOrCreateForKey" in target ? target.getOrCreateForKey.poolName ?? this.#config.poolName : void 0,
4669
+ "getOrCreateForKey" in target ? target.getOrCreateForKey.poolName ?? config.poolName : void 0,
4470
4670
  options
4471
4671
  );
4472
4672
  }
@@ -4949,6 +5149,7 @@ var ActorConnRaw = class {
4949
5149
  /** Promise used to indicate the socket has connected successfully. This will be rejected if the connection fails. */
4950
5150
  #onOpenPromise;
4951
5151
  #websocket;
5152
+ #authRetryAvailable = true;
4952
5153
  #client;
4953
5154
  #driver;
4954
5155
  #currentActorInvocation;
@@ -5191,6 +5392,10 @@ var ActorConnRaw = class {
5191
5392
  if (error instanceof ActorConnDisposed) {
5192
5393
  return false;
5193
5394
  }
5395
+ if (error instanceof RivetError && isInvalidToken(error.group, error.code) && this.#driver.refreshAuthToken && this.#authRetryAvailable) {
5396
+ this.#authRetryAvailable = false;
5397
+ return true;
5398
+ }
5194
5399
  if (error instanceof RivetError && this.#shouldReconnectForStaleActor(error.group, error.code)) {
5195
5400
  return true;
5196
5401
  }
@@ -5315,6 +5520,7 @@ var ActorConnRaw = class {
5315
5520
  messageQueueLength: this.#messageQueue.length,
5316
5521
  connId: this.#connId
5317
5522
  });
5523
+ this.#authRetryAvailable = true;
5318
5524
  this.#setConnStatus("connected");
5319
5525
  if (this.#onOpenPromise) {
5320
5526
  this.#onOpenPromise.resolve(void 0);
@@ -5456,6 +5662,7 @@ var ActorConnRaw = class {
5456
5662
  }
5457
5663
  /** Called by the onclose event from drivers. */
5458
5664
  async #handleOnClose(event) {
5665
+ const failedSocket = this.#websocket;
5459
5666
  const closeEvent = event;
5460
5667
  const wasClean = closeEvent.wasClean;
5461
5668
  const wasConnected = this.#connStatus === "connected";
@@ -5477,6 +5684,20 @@ var ActorConnRaw = class {
5477
5684
  const parsed = parseWebSocketCloseReason(reason);
5478
5685
  if (parsed) {
5479
5686
  const { group, code, rayId } = parsed;
5687
+ if (isInvalidToken(group, code) && closeEvent.code === 1008 && failedSocket) {
5688
+ try {
5689
+ if (!await this.#driver.refreshAuthToken?.(
5690
+ failedSocket
5691
+ )) {
5692
+ this.#authRetryAvailable = false;
5693
+ }
5694
+ } catch {
5695
+ logger2().warn("token renewal failed");
5696
+ this.#authRetryAvailable = false;
5697
+ }
5698
+ } else if (isInvalidToken(group, code)) {
5699
+ this.#authRetryAvailable = false;
5700
+ }
5480
5701
  if (this.#shouldReconnectForStaleActor(group, code)) {
5481
5702
  this.#clearResolvedActorIdentity();
5482
5703
  this.#onOpenPromise?.reject(
@@ -5526,7 +5747,7 @@ var ActorConnRaw = class {
5526
5747
  if (error instanceof RivetError) {
5527
5748
  this.#dispatchActorError(error);
5528
5749
  }
5529
- if (wasConnected) {
5750
+ if (wasConnected && this.#authRetryAvailable) {
5530
5751
  logger2().debug({
5531
5752
  msg: "triggering reconnect",
5532
5753
  connId: this.#connId
@@ -6832,6 +7053,22 @@ var ActorHandleRaw = class {
6832
7053
  "dynamic actor resolution did not produce a direct actor id"
6833
7054
  );
6834
7055
  }
7056
+ /** Issue a token scoped to this actor. Defaults to actor gateway read access. */
7057
+ async issueToken(options = {}) {
7058
+ const actorId = await this.resolve();
7059
+ const permissions = options.permissions ?? {
7060
+ actor_gateway: ["read"]
7061
+ };
7062
+ const grants = Object.keys(permissions).flatMap((resource) => {
7063
+ const operations = permissions[resource];
7064
+ return operations ? [{ resource, target: { id: actorId }, operations }] : [];
7065
+ });
7066
+ return this.#driver.issueToken({
7067
+ subject: options.subject,
7068
+ expiresIn: options.expiresIn,
7069
+ grants
7070
+ });
7071
+ }
6835
7072
  /**
6836
7073
  * Returns the raw URL for routing traffic to the actor.
6837
7074
  */
@@ -6866,6 +7103,8 @@ var ACTOR_CONNS_SYMBOL = /* @__PURE__ */ Symbol("actorConns");
6866
7103
  var CREATE_ACTOR_CONN_PROXY = /* @__PURE__ */ Symbol("createActorConnProxy");
6867
7104
  var ClientRaw = class {
6868
7105
  #disposed = false;
7106
+ /** Issue namespace-scoped tokens with an explicit grant list. */
7107
+ auth;
6869
7108
  [ACTOR_CONNS_SYMBOL] = /* @__PURE__ */ new Set();
6870
7109
  #driver;
6871
7110
  #encodingKind;
@@ -6877,6 +7116,9 @@ var ClientRaw = class {
6877
7116
  */
6878
7117
  constructor(driver, options = {}) {
6879
7118
  this.#driver = driver;
7119
+ this.auth = {
7120
+ issueToken: (issueOptions) => this.#driver.issueToken(issueOptions)
7121
+ };
6880
7122
  this.#encodingKind = options.encoding ?? "bare";
6881
7123
  this.#gatewayOptions = options.gateway ?? {};
6882
7124
  this.#currentActorInvocation = options.currentActorInvocation;