stellarium-ts 0.16.0 → 0.18.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.
Files changed (46) hide show
  1. package/dist/client/operatorAuth.d.ts +19 -0
  2. package/dist/client/operatorAuth.d.ts.map +1 -0
  3. package/dist/client/operatorAuth.js +21 -0
  4. package/dist/client/operatorClient.d.ts +103 -31
  5. package/dist/client/operatorClient.d.ts.map +1 -1
  6. package/dist/client/operatorClient.js +107 -23
  7. package/dist/client/runTokens.d.ts +23 -0
  8. package/dist/client/runTokens.d.ts.map +1 -0
  9. package/dist/client/runTokens.js +34 -0
  10. package/dist/db/db.d.ts.map +1 -1
  11. package/dist/db/db.js +8 -5
  12. package/dist/index.d.ts +4 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +4 -0
  15. package/dist/resource/inference.d.ts +5 -3
  16. package/dist/resource/inference.d.ts.map +1 -1
  17. package/dist/resource/inference.js +17 -7
  18. package/dist/resource/invoker.d.ts +48 -14
  19. package/dist/resource/invoker.d.ts.map +1 -1
  20. package/dist/resource/invoker.js +108 -38
  21. package/dist/resource/repository.d.ts +13 -7
  22. package/dist/resource/repository.d.ts.map +1 -1
  23. package/dist/resource/repository.js +23 -13
  24. package/dist/resource/settlement.d.ts +25 -0
  25. package/dist/resource/settlement.d.ts.map +1 -0
  26. package/dist/resource/settlement.js +59 -0
  27. package/dist/routes.d.ts +16 -0
  28. package/dist/routes.d.ts.map +1 -1
  29. package/dist/routes.js +103 -76
  30. package/dist/server.d.ts +11 -6
  31. package/dist/server.d.ts.map +1 -1
  32. package/dist/server.js +32 -7
  33. package/dist/service.d.ts +18 -6
  34. package/dist/service.d.ts.map +1 -1
  35. package/dist/service.js +8 -3
  36. package/dist/tests/client/operatorClient.test.js +94 -36
  37. package/dist/tests/db/db.test.js +4 -4
  38. package/dist/tests/helpers.d.ts +4 -3
  39. package/dist/tests/helpers.d.ts.map +1 -1
  40. package/dist/tests/helpers.js +8 -6
  41. package/dist/tests/integration/network.test.js +69 -18
  42. package/dist/tests/resource/invoker.test.js +33 -9
  43. package/dist/tests/resource/repository.test.js +9 -9
  44. package/dist/tests/routes.test.js +165 -40
  45. package/dist/tests/server.test.js +15 -15
  46. package/package.json +3 -2
package/dist/routes.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { FastifyPluginAsync } from 'fastify';
2
+ import { type RunTokenClaims } from './client/runTokens.js';
2
3
  import type { Service } from './service.js';
3
4
  export interface RateLimitConfig {
4
5
  max: number;
@@ -9,6 +10,21 @@ interface RouteOptions {
9
10
  service: Service;
10
11
  rateLimit?: RateLimitConfig;
11
12
  }
13
+ /**
14
+ * Who is calling `POST /v1/run`: the holder of this node's API key, or an API client with a run token.
15
+ * Set by the route's preValidation hook.
16
+ */
17
+ type RunCaller = {
18
+ kind: 'apiKey';
19
+ } | ({
20
+ kind: 'client';
21
+ token: string;
22
+ } & RunTokenClaims);
23
+ declare module 'fastify' {
24
+ interface FastifyRequest {
25
+ runCaller?: RunCaller;
26
+ }
27
+ }
12
28
  export declare const DEFAULT_PAGE_SIZE = 100;
13
29
  export declare const MAX_PAGE_SIZE = 500;
14
30
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAgC,MAAM,SAAS,CAAC;AAMhF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAK5C,MAAM,WAAW,eAAe;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/B;AAED,UAAU,YAAY;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,eAAe,CAAC;CAC/B;AAoDD,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,aAAa,MAAM,CAAC;AA4LjC;;;;GAIG;AACH,QAAA,MAAM,MAAM,EAAE,kBAAkB,CAAC,YAAY,CAuX5C,CAAC;eAGa,MAAM"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAgC,MAAM,SAAS,CAAC;AAEhF,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAM1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAK5C,MAAM,WAAW,eAAe;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/B;AAED,UAAU,YAAY;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,eAAe,CAAC;CAC/B;AAoCD;;;GAGG;AACH,KAAK,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAAC,CAAC;AAE3F,OAAO,QAAQ,SAAS,CAAC;IACrB,UAAU,cAAc;QACpB,SAAS,CAAC,EAAE,SAAS,CAAC;KACzB;CACJ;AAkBD,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,aAAa,MAAM,CAAC;AAwKjC;;;;GAIG;AACH,QAAA,MAAM,MAAM,EAAE,kBAAkB,CAAC,YAAY,CAma5C,CAAC;eAGa,MAAM"}
package/dist/routes.js CHANGED
@@ -1,8 +1,10 @@
1
- import { createHash, timingSafeEqual } from 'node:crypto';
1
+ import { createHash, randomUUID, timingSafeEqual } from 'node:crypto';
2
2
  import rateLimit from '@fastify/rate-limit';
3
- import { interpretParsed, parseScript } from 'requestscript';
3
+ import { interpretParsed, parseScript, RequestScriptError, TokenMeter } from 'requestscript';
4
+ import { looksLikeJwt } from './client/runTokens.js';
4
5
  import { inferenceVoucherPayload, invocationHash, isInferenceVoucher, paymentVoucherPayload } from './resource/invoker.js';
5
- import { inferenceCharge, isInferenceFunction, isInferenceManifest, parseMaxTokens } from './resource/inference.js';
6
+ import { estimateInputTokens, inferenceCharge, isInferenceFunction, isInferenceManifest, parseMaxTokens } from './resource/inference.js';
7
+ import { settlePaidCall } from './resource/settlement.js';
6
8
  import { safeVerifyData } from './crypto/utils.js';
7
9
  import { InvalidPublicKeyError } from './grant/service.js';
8
10
  // Body schemas check only the shape the handlers rely on. Signed payloads (resources) are
@@ -62,12 +64,12 @@ const resourceCallProperties = {
62
64
  };
63
65
  const fixedVoucherSchema = {
64
66
  type: 'object',
65
- required: ['requestId', 'invocationId', 'amount', 'recipientAddress', 'signature', 'expiresAt', 'invocationHash', 'publicKey'],
67
+ required: ['requestId', 'invocationId', 'amount', 'recipientOrganizationId', 'signature', 'expiresAt', 'invocationHash', 'publicKey'],
66
68
  properties: {
67
69
  requestId: { type: 'string' },
68
70
  invocationId: { type: 'string' },
69
71
  amount: { type: 'integer' },
70
- recipientAddress: { type: 'string' },
72
+ recipientOrganizationId: { type: 'string' },
71
73
  signature: { type: 'string' },
72
74
  expiresAt: { type: 'string' },
73
75
  invocationHash: { type: 'string' },
@@ -76,16 +78,16 @@ const fixedVoucherSchema = {
76
78
  };
77
79
  const inferenceVoucherSchema = {
78
80
  type: 'object',
79
- required: ['kind', 'requestId', 'invocationId', 'maxAmount', 'inputCostMicroUsdcPerMillionTokens', 'outputCostMicroUsdcPerMillionTokens', 'maxTokens', 'recipientAddress', 'signature', 'expiresAt', 'invocationHash', 'publicKey'],
81
+ required: ['kind', 'requestId', 'invocationId', 'maxAmount', 'inputCostMicroUsdPerMillionTokens', 'outputCostMicroUsdPerMillionTokens', 'maxTokens', 'recipientOrganizationId', 'signature', 'expiresAt', 'invocationHash', 'publicKey'],
80
82
  properties: {
81
83
  kind: { const: 'inference' },
82
84
  requestId: { type: 'string' },
83
85
  invocationId: { type: 'string' },
84
86
  maxAmount: { type: 'integer', minimum: 0 },
85
- inputCostMicroUsdcPerMillionTokens: { type: 'integer', minimum: 0 },
86
- outputCostMicroUsdcPerMillionTokens: { type: 'integer', minimum: 0 },
87
+ inputCostMicroUsdPerMillionTokens: { type: 'integer', minimum: 0 },
88
+ outputCostMicroUsdPerMillionTokens: { type: 'integer', minimum: 0 },
87
89
  maxTokens: { type: 'integer', minimum: 1 },
88
- recipientAddress: { type: 'string' },
90
+ recipientOrganizationId: { type: 'string' },
89
91
  signature: { type: 'string' },
90
92
  expiresAt: { type: 'string' },
91
93
  invocationHash: { type: 'string' },
@@ -131,16 +133,16 @@ function secretsEqual(a, b) {
131
133
  }
132
134
  /**
133
135
  * A voucher is valid when it is bound to this exact call, signed by its public key, unexpired,
134
- * and pays the function's recipient. A fixed voucher must pay at least the function's cost. An inference
136
+ * and pays this node's organization. A fixed voucher must pay at least the function's cost. An inference
135
137
  * voucher must be for an inference function, carry its rates, and sign the call's `maxTokens`.
136
138
  * Whether its `maxAmount` covers the call is checked separately, against the function's estimator.
137
139
  */
138
- function isValidPaymentVoucher(voucher, resourceFunction, manifest, request) {
140
+ function isValidPaymentVoucher(voucher, resourceFunction, manifest, request, organizationId) {
139
141
  const expectedHash = invocationHash(request.resourcePath, request.resourceName, request.functionName, request.parameters);
140
142
  if (voucher.invocationHash !== expectedHash) {
141
143
  return false;
142
144
  }
143
- if (voucher.recipientAddress !== manifest.recipientAddress) {
145
+ if (!organizationId || voucher.recipientOrganizationId !== organizationId) {
144
146
  return false;
145
147
  }
146
148
  const expiresAt = Date.parse(voucher.expiresAt);
@@ -151,8 +153,8 @@ function isValidPaymentVoucher(voucher, resourceFunction, manifest, request) {
151
153
  if (!isInferenceFunction(resourceFunction) || !isInferenceManifest(manifest)) {
152
154
  return false;
153
155
  }
154
- if (voucher.inputCostMicroUsdcPerMillionTokens !== manifest.inputCostMicroUsdcPerMillionTokens
155
- || voucher.outputCostMicroUsdcPerMillionTokens !== manifest.outputCostMicroUsdcPerMillionTokens) {
156
+ if (voucher.inputCostMicroUsdPerMillionTokens !== manifest.inputCostMicroUsdPerMillionTokens
157
+ || voucher.outputCostMicroUsdPerMillionTokens !== manifest.outputCostMicroUsdPerMillionTokens) {
156
158
  return false;
157
159
  }
158
160
  if (voucher.maxTokens !== parseMaxTokens(request.parameters)) {
@@ -163,31 +165,11 @@ function isValidPaymentVoucher(voucher, resourceFunction, manifest, request) {
163
165
  if (isInferenceFunction(resourceFunction) || isInferenceManifest(manifest)) {
164
166
  return false;
165
167
  }
166
- if (voucher.amount < manifest.costMicroUsdc) {
168
+ if (voucher.amount < manifest.costMicroUsd) {
167
169
  return false;
168
170
  }
169
171
  return safeVerifyData(paymentVoucherPayload(voucher), voucher.publicKey, voucher.signature);
170
172
  }
171
- /** The function's own input-token estimate, or undefined if its estimator failed or returned something other than a count. */
172
- async function estimateInputTokens(resourceFunction, request) {
173
- try {
174
- const inputTokens = await resourceFunction.estimateInputTokens?.(request.parameters);
175
- return Number.isSafeInteger(inputTokens) && inputTokens >= 0 ? inputTokens : undefined;
176
- }
177
- catch {
178
- return undefined;
179
- }
180
- }
181
- /** What the operator will accrue for `usage`: its charge at the voucher's rates, capped at `maxAmount`. */
182
- function cappedInferenceCharge(voucher, usage) {
183
- try {
184
- return Math.min(inferenceCharge(voucher, usage.inputTokens, usage.outputTokens), voucher.maxAmount);
185
- }
186
- catch {
187
- // The charge isn't a safe integer, so it is over any maxAmount.
188
- return voucher.maxAmount;
189
- }
190
- }
191
173
  /**
192
174
  * Encapsulates the routes
193
175
  * @param fastify Encapsulated Fastify Instance
@@ -210,6 +192,32 @@ const routes = async (fastify, options) => {
210
192
  return reply.status(401).send({ error: 'Invalid API Key' });
211
193
  }
212
194
  };
195
+ /**
196
+ * Requires `Authorization: Bearer <API_KEY>` or `Bearer <run token>`, a JWT from the operator addressed to
197
+ * this node. Records the caller on `request.runCaller`.
198
+ */
199
+ const requireRunAuthorization = async (request, reply) => {
200
+ const authorizationHeader = request.headers['authorization'];
201
+ if (!authorizationHeader) {
202
+ return reply.status(401).send({ error: 'Authorization header is required' });
203
+ }
204
+ const token = authorizationHeader.split(' ')[1];
205
+ const expectedApiKey = options.service.config.apiKey;
206
+ if (token && expectedApiKey && secretsEqual(token, expectedApiKey)) {
207
+ request.runCaller = { kind: 'apiKey' };
208
+ return;
209
+ }
210
+ const verifier = options.service.runTokenVerifier;
211
+ if (token && verifier && looksLikeJwt(token)) {
212
+ const claims = await verifier.verify(token);
213
+ if (!claims) {
214
+ return reply.status(401).send({ error: 'Invalid run token' });
215
+ }
216
+ request.runCaller = { kind: 'client', token, ...claims };
217
+ return;
218
+ }
219
+ return reply.status(401).send({ error: 'Invalid API Key' });
220
+ };
213
221
  // Start Meta Routes
214
222
  fastify.get('/', async (request, reply) => {
215
223
  return {
@@ -255,12 +263,12 @@ const routes = async (fastify, options) => {
255
263
  description: parameter.description,
256
264
  })),
257
265
  paymentManifest: !fn.paymentManifest ? undefined : isInferenceManifest(fn.paymentManifest) ? {
258
- inputCostMicroUsdcPerMillionTokens: fn.paymentManifest.inputCostMicroUsdcPerMillionTokens,
259
- outputCostMicroUsdcPerMillionTokens: fn.paymentManifest.outputCostMicroUsdcPerMillionTokens,
260
- recipientAddress: fn.paymentManifest.recipientAddress,
266
+ inputCostMicroUsdPerMillionTokens: fn.paymentManifest.inputCostMicroUsdPerMillionTokens,
267
+ outputCostMicroUsdPerMillionTokens: fn.paymentManifest.outputCostMicroUsdPerMillionTokens,
268
+ recipientOrganizationId: fn.paymentManifest.recipientOrganizationId,
261
269
  } : {
262
- costMicroUsdc: fn.paymentManifest.costMicroUsdc,
263
- recipientAddress: fn.paymentManifest.recipientAddress,
270
+ costMicroUsd: fn.paymentManifest.costMicroUsd,
271
+ recipientOrganizationId: fn.paymentManifest.recipientOrganizationId,
264
272
  },
265
273
  restricted: fn.restricted || undefined,
266
274
  kind: isInferenceFunction(fn) ? 'inference' : undefined,
@@ -286,8 +294,14 @@ const routes = async (fastify, options) => {
286
294
  });
287
295
  // End Meta Routes
288
296
  // Start Run Routes
297
+ /**
298
+ * Runs a request script. A run token from another organization's API client is billed: the operator
299
+ * holds the run's cost first, the script's paid calls draw on that hold, and afterwards the run is
300
+ * charged for the tokens the script executed at this node's listed price. Runs by the API key holder or
301
+ * by this node's own organization are free, and their paid calls are paid by this node's organization.
302
+ */
289
303
  // Authenticate before body validation, so unauthenticated callers learn nothing about the schema.
290
- fastify.post('/run', { schema: { body: runRequestSchema }, preValidation: requireApiKey }, async (request, reply) => {
304
+ fastify.post('/run', { schema: { body: runRequestSchema }, preValidation: requireRunAuthorization }, async (request, reply) => {
291
305
  const runRequest = request.body;
292
306
  let script;
293
307
  try {
@@ -300,16 +314,50 @@ const routes = async (fastify, options) => {
300
314
  if (script.declaration.kind !== 'request') {
301
315
  return reply.status(400).send({ error: 'Invalid script' });
302
316
  }
317
+ const caller = request.runCaller;
318
+ const billed = caller.kind === 'client' && caller.organizationId !== options.service.config.organizationId;
319
+ const runId = billed ? randomUUID() : undefined;
320
+ let resourceInvoker = options.service.resourceInvoker;
321
+ if (billed) {
322
+ const authorization = await options.service.operatorClient.authorizeRun({ token: caller.token, runId: runId, source: runRequest.source });
323
+ if (!authorization.ok) {
324
+ return reply.status(authorization.status).send({ error: authorization.error });
325
+ }
326
+ resourceInvoker = options.service.resourceInvoker.forRun({ runId: runId });
327
+ }
328
+ // Nothing waits on settlement, so it doesn't delay the response.
329
+ const settle = (outcome, tokens) => runId && setTimeout(async () => {
330
+ try {
331
+ if (outcome === 'capture') {
332
+ await options.service.operatorClient.captureRun({ runId, tokens });
333
+ }
334
+ else {
335
+ await options.service.operatorClient.releaseRun({ runId });
336
+ }
337
+ }
338
+ catch (error) {
339
+ fastify.log.error(error, `Failed to ${outcome} run ${runId}`);
340
+ }
341
+ }, 0);
342
+ const meter = new TokenMeter();
303
343
  try {
304
344
  const result = await interpretParsed(script, {
305
345
  resourceResolver: options.service.resourceResolver,
306
- resourceInvoker: options.service.resourceInvoker
346
+ resourceInvoker,
347
+ meter,
307
348
  });
308
- reply.status(200).send({ returnValue: result.returnValue ?? null });
349
+ settle('capture', meter.tokens);
350
+ return reply.status(200).send({ returnValue: result.returnValue ?? null, usage: { tokens: meter.tokens } });
309
351
  }
310
352
  catch (error) {
353
+ // A script that fails at runtime is the caller's mistake, and is charged for what it executed.
354
+ if (error instanceof RequestScriptError) {
355
+ settle('capture', meter.tokens);
356
+ return reply.status(422).send({ error: 'Script failed', details: error.message, usage: { tokens: meter.tokens } });
357
+ }
358
+ settle('release', meter.tokens);
311
359
  fastify.log.error(error);
312
- reply.status(500).send({ error: 'Internal server error' });
360
+ return reply.status(500).send({ error: 'Internal server error' });
313
361
  }
314
362
  });
315
363
  /**
@@ -375,7 +423,7 @@ const routes = async (fastify, options) => {
375
423
  if (maxTokens === undefined) {
376
424
  return reply.status(400).send({ error: 'maxTokens must be a positive int32' });
377
425
  }
378
- const inputTokens = await estimateInputTokens(resourceFunction, resourceRequest);
426
+ const inputTokens = await estimateInputTokens(resourceFunction, resourceRequest.parameters);
379
427
  if (inputTokens === undefined) {
380
428
  return reply.status(500).send({ error: 'Failed to estimate input tokens' });
381
429
  }
@@ -398,7 +446,7 @@ const routes = async (fastify, options) => {
398
446
  if (!paymentVoucher) {
399
447
  return reply.status(402).send({ error: 'Payment voucher is required' });
400
448
  }
401
- if (!isValidPaymentVoucher(paymentVoucher, resourceFunction, paymentManifest, resourceRequest)) {
449
+ if (!isValidPaymentVoucher(paymentVoucher, resourceFunction, paymentManifest, resourceRequest, options.service.config.organizationId)) {
402
450
  return reply.status(400).send({ error: 'Invalid payment voucher' });
403
451
  }
404
452
  if (await options.service.grantService.isBlocked(paymentVoucher.publicKey)) {
@@ -406,45 +454,24 @@ const routes = async (fastify, options) => {
406
454
  }
407
455
  // Re-estimated here, so inference never runs on a voucher that can't cover the expected cost.
408
456
  if (isInferenceVoucher(paymentVoucher)) {
409
- const inputTokens = await estimateInputTokens(resourceFunction, resourceRequest);
457
+ const inputTokens = await estimateInputTokens(resourceFunction, resourceRequest.parameters);
410
458
  if (inputTokens === undefined) {
411
459
  return reply.status(500).send({ error: 'Failed to estimate input tokens' });
412
460
  }
413
461
  const requiredMaxAmount = inferenceCharge(paymentVoucher, inputTokens, paymentVoucher.maxTokens);
414
462
  if (paymentVoucher.maxAmount < requiredMaxAmount) {
415
- return reply.status(402).send({ error: `Payment voucher maxAmount ${paymentVoucher.maxAmount} is below the estimated ${requiredMaxAmount} micro-USDC`, requiredMaxAmount });
463
+ return reply.status(402).send({ error: `Payment voucher maxAmount ${paymentVoucher.maxAmount} is below the estimated ${requiredMaxAmount} micro-USD`, requiredMaxAmount });
416
464
  }
417
465
  }
418
466
  }
419
- const result = await options.service.resourceInvoker.invoke(resource, resourceRequest.functionName, resourceRequest.parameters);
420
- let settlement;
421
- if (paymentManifest && paymentVoucher && !result.error) {
422
- if (!isInferenceVoucher(paymentVoucher)) {
423
- settlement = { voucher: paymentVoucher };
424
- }
425
- else if (result.usage) {
426
- // The function may report more output than it was allowed; the caller only signed for maxTokens.
427
- const usage = { inputTokens: result.usage.inputTokens, outputTokens: Math.min(result.usage.outputTokens, paymentVoucher.maxTokens) };
428
- result.usage = usage;
429
- // A free call has nothing to settle, and the operator refuses an empty receipt.
430
- if (cappedInferenceCharge(paymentVoucher, usage) > 0) {
431
- settlement = { voucher: paymentVoucher, usage };
432
- }
433
- }
467
+ const invoke = () => options.service.resourceInvoker.invoke(resource, resourceRequest.functionName, resourceRequest.parameters);
468
+ // A free function ignores any voucher sent with it.
469
+ if (!paymentManifest || !paymentVoucher) {
470
+ return invoke();
434
471
  }
435
- // Settle only verified vouchers, and only after a successful invoke. A voucher sent to a free function is ignored.
436
- // Run this on a separate thread to avoid blocking the main thread.
437
- if (settlement) {
438
- const payment = settlement;
439
- setTimeout(async () => {
440
- // Nothing awaits this callback, so an uncaught error here would be an unhandled rejection.
441
- try {
442
- await options.service.operatorClient.settlePayment(payment);
443
- }
444
- catch (error) {
445
- fastify.log.error(error, 'Failed to settle payment');
446
- }
447
- }, 0);
472
+ const result = await settlePaidCall(options.service.operatorClient, paymentVoucher, invoke, (error, message) => fastify.log.error(error, message));
473
+ if ('refused' in result) {
474
+ return reply.status(result.refused.status).send({ error: result.refused.error });
448
475
  }
449
476
  return result;
450
477
  });
package/dist/server.d.ts CHANGED
@@ -6,7 +6,7 @@ import type { ApproveGrant, GrantOptions } from './grant/service.js';
6
6
  import { StellariumResource } from './resource/repository.js';
7
7
  import { RateLimitConfig } from './routes.js';
8
8
  import { type NodeConfig } from './service.js';
9
- export declare const DEFAULT_COST_LIMIT_MICRO_USDC = 10000;
9
+ export declare const DEFAULT_COST_LIMIT_MICRO_USD = 10000;
10
10
  export declare const DEFAULT_GRANT_REFRESH_INTERVAL_MS: number;
11
11
  export interface StellariumNodeOptions {
12
12
  port?: number;
@@ -20,17 +20,22 @@ export interface StellariumNodeOptions {
20
20
  publicKey?: string;
21
21
  /** Defaults to `SIGNING_PRIVATE_KEY`. */
22
22
  privateKey?: string;
23
- /** Bearer token for `POST /v1/run`. Defaults to `API_KEY`. */
23
+ /** Bearer token for `POST /v1/run` and the admin routes. Defaults to `API_KEY`. */
24
24
  apiKey?: string;
25
25
  /** Defaults to `OPERATOR_URL`. */
26
26
  operatorUrl?: string;
27
- /** Most this node pays for one remote call. Defaults to `COST_LIMIT_MICRO_USDC`, then 10,000 ($0.01). */
28
- costLimitMicroUsdc?: number;
27
+ /**
28
+ * One-time token from the dashboard that enrolls this node in an organization. Defaults to
29
+ * `ENROLLMENT_TOKEN`. Only needed on a node's first start; an enrolled node ignores it.
30
+ */
31
+ enrollmentToken?: string;
32
+ /** Most this node pays for one remote call. Defaults to `COST_LIMIT_MICRO_USD`, then 10,000 ($0.01). */
33
+ costLimitMicroUsd?: number;
29
34
  /**
30
35
  * Most this node signs for in one remote inference call (the voucher's `maxAmount`).
31
- * Defaults to `INFERENCE_COST_LIMIT_MICRO_USDC`, then to the resolved `costLimitMicroUsdc`.
36
+ * Defaults to `INFERENCE_COST_LIMIT_MICRO_USD`, then to the resolved `costLimitMicroUsd`.
32
37
  */
33
- inferenceCostLimitMicroUsdc?: number;
38
+ inferenceCostLimitMicroUsd?: number;
34
39
  /** SQLite file. Defaults to `./requestscript.db`; use ':memory:' for tests. */
35
40
  databasePath?: string;
36
41
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -4,7 +4,8 @@ import { validateInferenceFunction } from './resource/inference.js';
4
4
  import routes from './routes.js';
5
5
  import { createService } from './service.js';
6
6
  import { StartupService } from './startup.js';
7
- export const DEFAULT_COST_LIMIT_MICRO_USDC = 10_000;
7
+ import { OperatorClient } from './client/operatorClient.js';
8
+ export const DEFAULT_COST_LIMIT_MICRO_USD = 10_000;
8
9
  export const DEFAULT_GRANT_REFRESH_INTERVAL_MS = 1000 * 60 * 5;
9
10
  function required(name, value) {
10
11
  if (!value) {
@@ -16,13 +17,13 @@ function hostsPaidFunctions(resources) {
16
17
  return resources.some(resource => resource.functions.some(fn => fn.paymentManifest));
17
18
  }
18
19
  export function resolveConfig(options, resources = []) {
19
- const costLimit = options.costLimitMicroUsdc ?? (process.env.COST_LIMIT_MICRO_USDC ? Number(process.env.COST_LIMIT_MICRO_USDC) : DEFAULT_COST_LIMIT_MICRO_USDC);
20
+ const costLimit = options.costLimitMicroUsd ?? (process.env.COST_LIMIT_MICRO_USD ? Number(process.env.COST_LIMIT_MICRO_USD) : DEFAULT_COST_LIMIT_MICRO_USD);
20
21
  if (!Number.isSafeInteger(costLimit) || costLimit < 0) {
21
- throw new Error('costLimitMicroUsdc must be a nonnegative integer');
22
+ throw new Error('costLimitMicroUsd must be a nonnegative integer');
22
23
  }
23
- const inferenceCostLimit = options.inferenceCostLimitMicroUsdc ?? (process.env.INFERENCE_COST_LIMIT_MICRO_USDC ? Number(process.env.INFERENCE_COST_LIMIT_MICRO_USDC) : costLimit);
24
+ const inferenceCostLimit = options.inferenceCostLimitMicroUsd ?? (process.env.INFERENCE_COST_LIMIT_MICRO_USD ? Number(process.env.INFERENCE_COST_LIMIT_MICRO_USD) : costLimit);
24
25
  if (!Number.isSafeInteger(inferenceCostLimit) || inferenceCostLimit < 0) {
25
- throw new Error('inferenceCostLimitMicroUsdc must be a nonnegative integer');
26
+ throw new Error('inferenceCostLimitMicroUsd must be a nonnegative integer');
26
27
  }
27
28
  for (const resource of resources) {
28
29
  resource.functions.forEach(validateInferenceFunction);
@@ -36,8 +37,8 @@ export function resolveConfig(options, resources = []) {
36
37
  operatorUrl: hostsPaidFunctions(resources)
37
38
  ? required('OPERATOR_URL', options.operatorUrl ?? process.env.OPERATOR_URL)
38
39
  : options.operatorUrl ?? process.env.OPERATOR_URL,
39
- costLimitMicroUsdc: costLimit,
40
- inferenceCostLimitMicroUsdc: inferenceCostLimit,
40
+ costLimitMicroUsd: costLimit,
41
+ inferenceCostLimitMicroUsd: inferenceCostLimit,
41
42
  };
42
43
  }
43
44
  export class StellariumNode {
@@ -52,6 +53,30 @@ export class StellariumNode {
52
53
  */
53
54
  async start(fastify, options) {
54
55
  const config = resolveConfig(options, this.resources);
56
+ const enrollmentToken = options.enrollmentToken ?? process.env.ENROLLMENT_TOKEN;
57
+ // Paid functions are paid to the node's organization, so a provider must be enrolled before it serves them.
58
+ // A node that only calls paid functions enrolls the same way, so its vouchers draw on its organization's balance.
59
+ // Any other node with an operator still reports its URL, for listing, and learns its organization, so its
60
+ // own organization's run tokens run free.
61
+ const session = () => new OperatorClient(config.operatorUrl, config.publicKey, config.privateKey).session(enrollmentToken, config.baseUrl);
62
+ if (hostsPaidFunctions(this.resources) || enrollmentToken) {
63
+ try {
64
+ ({ organizationId: config.organizationId } = await session());
65
+ }
66
+ catch (error) {
67
+ const hint = enrollmentToken ? '' : '. Create an enrollment token for it at https://platform.stellarium.sh and set ENROLLMENT_TOKEN';
68
+ throw new Error(`${error instanceof Error ? error.message : String(error)}${hint}`, { cause: error });
69
+ }
70
+ }
71
+ else if (config.operatorUrl) {
72
+ // Serving doesn't depend on it, so it doesn't hold up the start. Until it answers, run tokens from
73
+ // this node's own organization are refused as if billed.
74
+ session().then(({ organizationId }) => {
75
+ config.organizationId = organizationId;
76
+ }, (error) => {
77
+ fastify.log.warn(`No session with the operator, so this node can't be listed: ${error instanceof Error ? error.message : String(error)}`);
78
+ });
79
+ }
55
80
  const db = await openDatabase(options.databasePath ?? DEFAULT_DATABASE_PATH);
56
81
  fastify.addHook('onClose', async () => db.close());
57
82
  const service = createService(db, this.resources, config, { approveGrant: options.approveGrant });
package/dist/service.d.ts CHANGED
@@ -1,8 +1,11 @@
1
- import { ResourceInvoker, ResourceResolver } from "requestscript";
1
+ import type { JWTVerifyGetKey } from "jose";
2
+ import { ResourceResolver } from "requestscript";
2
3
  import type { Database } from "sqlite";
3
4
  import { PeerRepository } from "./peer/repository.js";
4
5
  import { ResourceRepository, type StellariumResource } from "./resource/repository.js";
6
+ import { CombinationResourceInvoker } from "./resource/invoker.js";
5
7
  import { OperatorClient } from "./client/operatorClient.js";
8
+ import { RunTokenVerifier } from "./client/runTokens.js";
6
9
  import { type ApproveGrant, GrantService } from "./grant/service.js";
7
10
  /** Settings a node runs with. `StellariumNode.start` fills these from its options, then the environment. */
8
11
  export interface NodeConfig {
@@ -12,27 +15,36 @@ export interface NodeConfig {
12
15
  publicKey: string;
13
16
  /** Ed25519 PKCS#8 DER, base64url. */
14
17
  privateKey: string;
15
- /** Bearer token for `POST /v1/run`. When unset, `/v1/run` rejects every request. */
18
+ /** Bearer token for `POST /v1/run` and the admin routes. When unset, only run tokens are accepted, and the admin routes reject every request. */
16
19
  apiKey?: string;
17
- /** Operator base URL, including `/v1`. Required when the node hosts paid functions. */
20
+ /** Operator base URL, including `/v1`. Required when the node hosts paid functions, and to accept run tokens. */
18
21
  operatorUrl?: string;
22
+ /**
23
+ * The operator organization this node is enrolled in, which is paid for its functions.
24
+ * `StellariumNode.start` asks the operator for it when the node hosts paid functions.
25
+ */
26
+ organizationId?: string;
19
27
  /** Most this node will pay for one remote call. */
20
- costLimitMicroUsdc: number;
28
+ costLimitMicroUsd: number;
21
29
  /** Most this node will sign for in one remote inference call. */
22
- inferenceCostLimitMicroUsdc: number;
30
+ inferenceCostLimitMicroUsd: number;
23
31
  }
24
32
  export interface Service {
25
33
  config: NodeConfig;
26
34
  peerRepository: PeerRepository;
27
35
  resourceRepository: ResourceRepository;
28
36
  resourceResolver: ResourceResolver;
29
- resourceInvoker: ResourceInvoker;
37
+ resourceInvoker: CombinationResourceInvoker;
30
38
  operatorClient: OperatorClient;
31
39
  grantService: GrantService;
40
+ /** Verifies the run tokens `POST /v1/run` accepts. Set when the node has an operator URL. */
41
+ runTokenVerifier?: RunTokenVerifier;
32
42
  }
33
43
  export interface ServiceOptions {
34
44
  /** Decides `POST /v1/grants/request`. Without it, every request is refused. */
35
45
  approveGrant?: ApproveGrant;
46
+ /** Replaces the operator's published keys for verifying run tokens, in tests. */
47
+ runTokenKeys?: JWTVerifyGetKey;
36
48
  }
37
49
  export declare function createService(db: Database, resources: StellariumResource[], config: NodeConfig, options?: ServiceOptions): Service;
38
50
  //# sourceMappingURL=service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEvF,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,EAAE,KAAK,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAErE,4GAA4G;AAC5G,MAAM,WAAW,UAAU;IACvB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,2BAA2B,EAAE,MAAM,CAAC;CACvC;AAED,MAAM,WAAW,OAAO;IACpB,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,YAAY,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC3B,+EAA+E;IAC/E,YAAY,CAAC,EAAE,YAAY,CAAC;CAC/B;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,kBAAkB,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAoBtI"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,KAAK,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAErE,4GAA4G;AAC5G,MAAM,WAAW,UAAU;IACvB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,iJAAiJ;IACjJ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iHAAiH;IACjH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,0BAA0B,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,OAAO;IACpB,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,eAAe,EAAE,0BAA0B,CAAC;IAC5C,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC3B,+EAA+E;IAC/E,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iFAAiF;IACjF,YAAY,CAAC,EAAE,eAAe,CAAC;CAClC;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,kBAAkB,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAwBtI"}
package/dist/service.js CHANGED
@@ -3,14 +3,18 @@ import { CombinationResourceResolver } from "./resource/resolver.js";
3
3
  import { ResourceRepository } from "./resource/repository.js";
4
4
  import { CombinationResourceInvoker } from "./resource/invoker.js";
5
5
  import { OperatorClient } from "./client/operatorClient.js";
6
+ import { RunTokenVerifier } from "./client/runTokens.js";
6
7
  import { GrantRepository } from "./grant/repository.js";
7
8
  import { GrantService } from "./grant/service.js";
8
9
  export function createService(db, resources, config, options = {}) {
9
- const resourceRepository = new ResourceRepository(db, resources, config.baseUrl);
10
+ const resourceRepository = new ResourceRepository(db, resources, config.baseUrl, config.organizationId);
10
11
  const resourceResolver = new CombinationResourceResolver(resourceRepository);
11
12
  const grantService = new GrantService(new GrantRepository(db), config, options.approveGrant);
12
- const resourceInvoker = new CombinationResourceInvoker(resourceRepository, config.costLimitMicroUsdc, config.publicKey, config.privateKey, grantService, config.inferenceCostLimitMicroUsdc);
13
- const operatorClient = new OperatorClient(config.operatorUrl);
13
+ const operatorClient = new OperatorClient(config.operatorUrl, config.publicKey, config.privateKey);
14
+ const resourceInvoker = new CombinationResourceInvoker(resourceRepository, config.costLimitMicroUsd, config.publicKey, config.privateKey, grantService, config.inferenceCostLimitMicroUsd, {
15
+ operatorClient,
16
+ organizationId: config.organizationId,
17
+ });
14
18
  return {
15
19
  config,
16
20
  peerRepository: new PeerRepository(db),
@@ -19,5 +23,6 @@ export function createService(db, resources, config, options = {}) {
19
23
  resourceInvoker,
20
24
  operatorClient,
21
25
  grantService,
26
+ runTokenVerifier: config.operatorUrl ? new RunTokenVerifier(config.operatorUrl, config.publicKey, options.runTokenKeys) : undefined,
22
27
  };
23
28
  }