@tdxvolt/volt-client-grpc 0.18.9 → 0.19.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/lib/index.cjs CHANGED
@@ -2,35 +2,36 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ var voltClientWeb = require('@tdxvolt/volt-client-web');
6
+ var uuid = require('uuid');
5
7
  var lodash = require('lodash');
6
8
  var ip = require('ip');
7
9
  var js = require('@tdxvolt/volt-client-web/js');
8
10
  var debug = require('debug');
9
- var EventEmitter = require('events');
10
- var fs = require('fs');
11
- var path = require('path');
12
- var bent = require('bent');
13
- var uuid = require('uuid');
14
11
  var Protobuf = require('protobufjs');
15
12
  var camelCase = require('lodash.camelcase');
16
13
  var descriptor = require('protobufjs/ext/descriptor/index.js');
14
+ var EventEmitter = require('events');
15
+ var fs = require('fs');
16
+ var path = require('path');
17
17
  var jwt = require('jsonwebtoken');
18
+ var crypto = require('crypto');
18
19
 
19
20
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
20
21
 
21
22
  var lodash__default = /*#__PURE__*/_interopDefaultLegacy(lodash);
22
23
  var ip__default = /*#__PURE__*/_interopDefaultLegacy(ip);
23
24
  var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug);
24
- var EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
25
- var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
26
- var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
27
- var bent__default = /*#__PURE__*/_interopDefaultLegacy(bent);
28
25
  var Protobuf__default = /*#__PURE__*/_interopDefaultLegacy(Protobuf);
29
26
  var camelCase__default = /*#__PURE__*/_interopDefaultLegacy(camelCase);
30
27
  var descriptor__default = /*#__PURE__*/_interopDefaultLegacy(descriptor);
28
+ var EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
29
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
30
+ var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
31
31
  var jwt__default = /*#__PURE__*/_interopDefaultLegacy(jwt);
32
+ var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
32
33
 
33
- const constants = {
34
+ const _constants = {
34
35
  authTokenName: "volt-token",
35
36
  defaultDIDHostName: "coreid.com",
36
37
  didServiceType: {
@@ -51,9 +52,18 @@ const constants = {
51
52
  terminalAPI: "tdx.volt_api.volt.v1.TerminalAPI",
52
53
  voltAPI: "tdx.volt_api.volt.v1.VoltAPI",
53
54
  wireAPI: "tdx.volt_api.volt.v1.WireAPI"
54
- }
55
+ },
56
+ voltDIDPrefix: "did:volt:"
55
57
  };
56
58
 
59
+ _constants.defaultRegistryList = [
60
+ _constants.defaultDIDHostName,
61
+ "https://tdxvolt.com",
62
+ "https://tdxid.com"
63
+ ];
64
+
65
+ const constants = _constants;
66
+
57
67
  const createSecureContextOptions = (cryptoOptions) => {
58
68
  const tlsOptions = {
59
69
  cert: Buffer.from(cryptoOptions.cert),
@@ -353,842 +363,336 @@ var grpcUtils = /*#__PURE__*/Object.freeze({
353
363
  createServer: createServer
354
364
  });
355
365
 
356
- /* eslint-disable no-underscore-dangle */
357
-
358
- const {
359
- aesCreateKey,
360
- aesEncrypt: aesEncrypt$1,
361
- aesDecrypt: aesDecrypt$1,
362
- toBase64,
363
- formatPEM: formatPEM$1,
364
- keyFromPem: keyFromPem$2,
365
- verify,
366
- deriveSharedKey: deriveSharedKey$1,
367
- x25519: x25519$1
368
- } = js.voltUtils;
369
-
370
- const invokeMethod = "Invoke";
371
-
372
- const log$5 = debug__default["default"]("volt-client-grpc:grpc-call");
373
-
374
- function _prepareInvokeRequest(
375
- request,
376
- method,
377
- methodType = "METHOD_TYPE_UNARY"
378
- ) {
379
- const isServiceRelayed =
380
- this._service?.service_description.host_type ===
381
- "SERVICE_HOST_TYPE_RELAYED";
366
+ /**
367
+ *
368
+ * This is copied from the proto-loader package file @grpc/proto-loader/build/src/index.js,
369
+ * simply in order to export the createPackageDefinition function, which we need to be
370
+ * able to dynamically load protobuf from strings stored in the Volt database rather than
371
+ * from files.
372
+ *
373
+ * There doesn't seem to be any plans to export this function from the package itself,
374
+ * see https://github.com/grpc/grpc-node/issues/550.
375
+ *
376
+ * This is deemed to be low-risk as the protobuf code is not expected to change often.
377
+ *
378
+ * Used by `getServiceDescriptors` in `proto-utils.js`.
379
+ *
380
+ */
382
381
 
383
- const relaying = this._voltClient.isRelayed || isServiceRelayed;
382
+ const IDEMPOTENCY_UNKNOWN = "IDEMPOTENCY_UNKNOWN";
384
383
 
385
- let targetDID = [];
386
- if (this._voltClient.isRelayed) {
387
- targetDID.push(this._voltClient.voltConfig.id);
384
+ const descriptorOptions = {
385
+ longs: String,
386
+ enums: String,
387
+ bytes: String,
388
+ defaults: true,
389
+ oneofs: true,
390
+ json: true
391
+ };
392
+ function joinName(baseName, name) {
393
+ if (baseName === "") {
394
+ return name;
395
+ } else {
396
+ return baseName + "." + name;
388
397
  }
389
-
390
- if (isServiceRelayed) {
391
- // Add another relay hop if the target service is relayed.
392
- log$5("target service is relayed");
393
- targetDID.push(this._service.service_description.host_client_id);
398
+ }
399
+ function isHandledReflectionObject(obj) {
400
+ return (
401
+ obj instanceof Protobuf__default["default"].Service ||
402
+ obj instanceof Protobuf__default["default"].Type ||
403
+ obj instanceof Protobuf__default["default"].Enum
404
+ );
405
+ }
406
+ function isNamespaceBase(obj) {
407
+ return obj instanceof Protobuf__default["default"].Namespace || obj instanceof Protobuf__default["default"].Root;
408
+ }
409
+ function getAllHandledReflectionObjects(obj, parentName) {
410
+ const objName = joinName(parentName, obj.name);
411
+ if (isHandledReflectionObject(obj)) {
412
+ return [[objName, obj]];
413
+ } else {
414
+ if (isNamespaceBase(obj) && typeof obj.nested !== "undefined") {
415
+ return Object.keys(obj.nested)
416
+ .map((name) => {
417
+ return getAllHandledReflectionObjects(obj.nested[name], objName);
418
+ })
419
+ .reduce(
420
+ (accumulator, currentValue) => accumulator.concat(currentValue),
421
+ []
422
+ );
423
+ }
394
424
  }
395
-
396
- log$5("target is %j", targetDID);
397
-
398
- // Use the service's host client id and public key if the service is relayed.
399
- const methodToken = this._voltClient.credential.getIdentityMetadata(
400
- this._voltClient.grpc,
401
- this._service?.service_description.host_client_id ||
402
- this._voltClient.voltConfig.id,
403
- this._service?.service_description.host_public_key ||
404
- this._voltClient.credential.voltPublicKey,
405
- relaying
425
+ return [];
426
+ }
427
+ function createDeserializer(cls, options) {
428
+ return function deserialize(argBuf) {
429
+ return cls.toObject(cls.decode(argBuf), options);
430
+ };
431
+ }
432
+ function createSerializer(cls) {
433
+ return function serialize(arg) {
434
+ if (Array.isArray(arg)) {
435
+ throw new Error(
436
+ `Failed to serialize message: expected object with ${cls.name} structure, got array instead`
437
+ );
438
+ }
439
+ const message = cls.fromObject(arg);
440
+ return cls.encode(message).finish();
441
+ };
442
+ }
443
+ function mapMethodOptions(options) {
444
+ return (options || []).reduce(
445
+ (obj, item) => {
446
+ for (const [key, value] of Object.entries(item)) {
447
+ switch (key) {
448
+ case "uninterpreted_option":
449
+ obj.uninterpreted_option.push(item.uninterpreted_option);
450
+ break;
451
+ default:
452
+ obj[key] = value;
453
+ }
454
+ }
455
+ return obj;
456
+ },
457
+ {
458
+ deprecated: false,
459
+ idempotency_level: IDEMPOTENCY_UNKNOWN,
460
+ uninterpreted_option: []
461
+ }
406
462
  );
463
+ }
464
+ function createMethodDefinition(method, serviceName, options, fileDescriptors) {
465
+ /* This is only ever called after the corresponding root.resolveAll(), so we
466
+ * can assume that the resolved request and response types are non-null */
467
+ const requestType = method.resolvedRequestType;
468
+ const responseType = method.resolvedResponseType;
469
+ return {
470
+ path: "/" + serviceName + "/" + method.name,
471
+ requestStream: !!method.requestStream,
472
+ responseStream: !!method.responseStream,
473
+ requestSerialize: createSerializer(requestType),
474
+ requestDeserialize: createDeserializer(requestType, options),
475
+ responseSerialize: createSerializer(responseType),
476
+ responseDeserialize: createDeserializer(responseType, options),
477
+ // TODO(murgatroid99): Find a better way to handle this
478
+ originalName: camelCase__default["default"](method.name),
479
+ requestType: createMessageDefinition(requestType, fileDescriptors),
480
+ responseType: createMessageDefinition(responseType, fileDescriptors),
481
+ options: mapMethodOptions(method.parsedOptions)
482
+ };
483
+ }
484
+ function createServiceDefinition(service, name, options, fileDescriptors) {
485
+ const def = {};
486
+ for (const method of service.methodsArray) {
487
+ def[method.name] = createMethodDefinition(
488
+ method,
489
+ name,
490
+ options,
491
+ fileDescriptors
492
+ );
493
+ }
494
+ return def;
495
+ }
496
+ function createMessageDefinition(message, fileDescriptors) {
497
+ const messageDescriptor = message.toDescriptor("proto3");
498
+ return {
499
+ format: "Protocol Buffer 3 DescriptorProto",
500
+ type: messageDescriptor.$type.toObject(
501
+ messageDescriptor,
502
+ descriptorOptions
503
+ ),
504
+ fileDescriptorProtos: fileDescriptors
505
+ };
506
+ }
507
+ function createEnumDefinition(enumType, fileDescriptors) {
508
+ const enumDescriptor = enumType.toDescriptor("proto3");
509
+ return {
510
+ format: "Protocol Buffer 3 EnumDescriptorProto",
511
+ type: enumDescriptor.$type.toObject(enumDescriptor, descriptorOptions),
512
+ fileDescriptorProtos: fileDescriptors
513
+ };
514
+ }
515
+ /**
516
+ * function createDefinition(obj: Protobuf.Service, name: string, options:
517
+ * Options): ServiceDefinition; function createDefinition(obj: Protobuf.Type,
518
+ * name: string, options: Options): MessageTypeDefinition; function
519
+ * createDefinition(obj: Protobuf.Enum, name: string, options: Options):
520
+ * EnumTypeDefinition;
521
+ */
522
+ function createDefinition(obj, name, options, fileDescriptors) {
523
+ if (obj instanceof Protobuf__default["default"].Service) {
524
+ return createServiceDefinition(obj, name, options, fileDescriptors);
525
+ } else if (obj instanceof Protobuf__default["default"].Type) {
526
+ return createMessageDefinition(obj, fileDescriptors);
527
+ } else if (obj instanceof Protobuf__default["default"].Enum) {
528
+ return createEnumDefinition(obj, fileDescriptors);
529
+ } else {
530
+ throw new Error("Type mismatch in reflection object handling");
531
+ }
532
+ }
407
533
 
408
- let invokeRequest;
409
- let callMethod = method;
534
+ function createPackageDefinition(root, options) {
535
+ const def = {};
536
+ root.resolveAll();
537
+ const descriptorList = root.toDescriptor("proto3").file;
538
+ const bufferList = descriptorList.map((value) =>
539
+ Buffer.from(descriptor__default["default"].FileDescriptorProto.encode(value).finish())
540
+ );
541
+ for (const [name, obj] of getAllHandledReflectionObjects(root, "")) {
542
+ def[name] = createDefinition(obj, name, options, bufferList);
543
+ }
544
+ return def;
545
+ }
410
546
 
411
- const voltGrpcClient = this._voltClient.getVoltAPIClient();
547
+ /**
548
+ * This file is generated by the 'generate-proto-assets.js' script.
549
+ * Do not modify this file directly.
550
+ */
412
551
 
413
- if (relaying) {
414
- //
415
- // If the volt connection is via a relay (or the target service is relayed),
416
- // we wrap the rpc in a RemoteRequest message and send it via a call to Invoke().
417
- //
552
+ const sqlite = `syntax = "proto3";
418
553
 
419
- // Replace the target method with a call to Invoke on the relay Volt.
420
- callMethod = invokeMethod;
554
+ package tdx.volt_api.data.v1;
421
555
 
422
- // But first we need to perform a key exchange with the target service.
423
- if (!this._encryptionKey) {
424
- // We haven't exchanged keys yet, so queue the payload.
425
- if (request) {
426
- this._pendingRequests.push(request);
427
- }
556
+ // Just reflect SQLite types for now.
557
+ enum DataType {
558
+ DATA_TYPE_UNKNOWN = 0;
559
+ DATA_TYPE_TEXT = 1;
560
+ DATA_TYPE_INTEGER = 2;
561
+ DATA_TYPE_REAL = 3;
562
+ DATA_TYPE_BLOB = 4;
563
+ DATA_TYPE_NULL = 5;
564
+ }
428
565
 
429
- if (this._keyExchangePending) {
430
- // We've already sent the key exchange request, so do nothing.
431
- log$5("key exchange pending, not sending payload");
432
- return;
433
- }
566
+ message Column {
567
+ string name = 1;
568
+ string description = 2;
569
+ DataType type = 3;
570
+ }
434
571
 
435
- this._keyExchangePending = true;
572
+ message Schema {
573
+ string name = 1;
574
+ string description = 2;
575
+ repeated Column column = 3;
576
+ }
436
577
 
437
- // Send the key exchange request.
438
- invokeRequest = {
439
- token: methodToken.token,
440
- target_did: targetDID,
441
- target_service_id: isServiceRelayed
442
- ? this._service.service_description.host_service_id
443
- : undefined
444
- };
578
+ message Variant {
579
+ oneof data {
580
+ string text = 1;
581
+ int64 integer = 2;
582
+ double real = 3;
583
+ bytes blob = 4;
584
+ bool null = 5;
585
+ }
586
+ }
445
587
 
446
- if (this._voltClient.isRelayed) {
447
- // When tunnelling, we serialise the request using the original serialisation
448
- // method for Invoke.
449
- invokeRequest =
450
- voltGrpcClient.Invoke.requestSerializeOriginal(invokeRequest);
451
- }
588
+ message RowHeader {
589
+ repeated Column column = 1;
590
+ }
452
591
 
453
- return {
454
- request: invokeRequest,
455
- method: callMethod,
456
- methodName: method,
457
- meta: methodToken
458
- };
459
- }
592
+ message VariantRow {
593
+ repeated Variant column = 1;
594
+ }
595
+ `;
596
+ const sqlite_database_api = `syntax = "proto3";
460
597
 
461
- let requestPayload;
598
+ package tdx.volt_api.data.v1;
462
599
 
463
- // Serialise the rpc request using the original serialisation method.
464
- if (request) {
465
- if (this._voltClient.isRelayed) {
466
- requestPayload =
467
- this._grpcClient[method].requestSerializeOriginal(request);
468
- } else {
469
- requestPayload = this._grpcClient[method].requestSerialize(request);
470
- }
471
- } else if (request !== null) {
472
- log$5("****************LOOKOUT****************** - empty request");
473
- } else {
474
- log$5("explicit empty request payload");
475
- }
600
+ import "tdx/volt_api/volt/v1/status.proto";
601
+ import "tdx/volt_api/data/v1/sqlite.proto";
602
+ import "tdx/volt_api/volt/v1/volt.proto";
476
603
 
477
- // Create a tunnel message containing the method call metadata and request payload.
478
- const tunnelResp = voltGrpcClient.Tunnel.responseSerialize({
479
- method_invoke: {
480
- method_name: this._grpcClient[method].path,
481
- method_type: methodType,
482
- request: requestPayload
483
- }
484
- });
604
+ // The Sqlite Database API exposes functions that enable clients to manipulate data in a given Volt database.
605
+ // Use the Sqlite Server API to create the database resource.
606
+ service SqliteDatabaseAPI {
607
+ // Execute multiple SQL statements in a single transaction via a single RPC.
608
+ rpc BulkUpdate(SqlBulkUpdateRequest) returns (SqlBulkUpdateResponse);
485
609
 
486
- // When sending via a relay we need to encrypt the payload.
487
- const iv = aesCreateKey().iv;
488
- const invokePayload = aesEncrypt$1(this._encryptionKey, iv, tunnelResp);
610
+ // Execute a single SQL statement. Any valid SQL is accepted. In order to execute non-SELECT statements, the caller must have the "write" permission.
611
+ rpc Execute(stream SqlExecuteRequest) returns (stream SqlExecuteResponse);
489
612
 
490
- // Construct the Invoke request.
491
- invokeRequest = {
492
- iv,
493
- payload: invokePayload
494
- };
613
+ // Import CSV data into a table.
614
+ // The import handler will inspect the incoming CSV data, infer the columns and data types required, and create and populate the SQL table.
615
+ // The CSV must contain a header row containing the column names and at least one row of data so that the types can be inferred.
616
+ // The importer assumes all data in any given CSV file relates to a single table.
617
+ // The data can either be streamed in chunks or retrieved from an existing resource.
618
+ // If the data is streamed in chunks, the client must call the Close() method on the stream to indicate that it has finished.
619
+ rpc ImportCSV(stream SqlImportCSVRequest) returns (stream SqlImportCSVResponse);
620
+ }
495
621
 
496
- if (this._voltClient.isRelayed) {
497
- // When tunnelling, we serialise the request using the original serialisation
498
- // method for Invoke.
499
- invokeRequest =
500
- voltGrpcClient.Invoke.requestSerializeOriginal(invokeRequest);
501
- }
502
- } else {
503
- invokeRequest = request;
504
- }
622
+ message SqlBulkUpdateRequest {
623
+ // The id of the database to update.
624
+ string database_id = 1;
505
625
 
506
- return {
507
- meta: methodToken,
508
- method: callMethod,
509
- methodName: method,
510
- request: invokeRequest
511
- };
626
+ // The SQL statements to execute. The statements will be executed within a transaction and will be committed if all statements succeed.
627
+ // Bear in mind the maximum size limit of a single message is 64MB, and around 1MB seems to be optimal in terms of performance.
628
+ repeated string statement = 2;
512
629
  }
513
630
 
514
- function _preparePayloadRequest(request, invokeInfo) {
515
- let payloadRequest;
631
+ message SqlBulkUpdateResponse {
632
+ // Details of any error that occurred on the call.
633
+ tdx.volt_api.volt.v1.Status status = 1;
634
+ }
516
635
 
517
- const isServiceRelayed =
518
- this._service?.service_description.host_type ===
519
- "SERVICE_HOST_TYPE_RELAYED";
636
+ message SqlExecuteStart {
637
+ // The id of the database to execute on.
638
+ string database_id = 1;
520
639
 
521
- if (this._voltClient.isRelayed || isServiceRelayed) {
522
- let requestPayload;
523
- if (this._voltClient.isRelayed) {
524
- // Serialise the rpc request using the original serialisation method.
525
- requestPayload =
526
- this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(
527
- request
528
- );
529
- } else {
530
- requestPayload =
531
- this._grpcClient[invokeInfo.methodName].requestSerialize(request);
532
- }
640
+ // Set to start a transaction. If not set, each statement will be executed in its own transaction.
641
+ bool transaction = 2;
533
642
 
534
- // Use the Tunnel method to serialise the relay request wrapper, this
535
- const voltGrpcClient = this._voltClient.getVoltAPIClient();
643
+ // The SQL statement to execute.
644
+ string statement = 3;
536
645
 
537
- // Create a tunnel message containing the method call metadata and request payload.
538
- const tunnelResp = voltGrpcClient.Tunnel.responseSerialize({
539
- method_payload: {
540
- payload: requestPayload
541
- }
542
- });
646
+ // This can be used to limit the number of rows returned by \`SELECT\` statements to avoid overloading a client. It is similar to using 'LIMIT/OFFSET' clauses on the 'SELECT' statement, but this method is easier to manage and the entire result set will be prepared on the Volt, and the client can then page through it by sending successive messages.
647
+ uint32 page_size = 4;
543
648
 
544
- // Encrypt the payload using the shared key.
545
- const iv = aesCreateKey().iv;
546
- const invokePayload = aesEncrypt$1(this._encryptionKey, iv, tunnelResp);
649
+ // Set to indicate that the query will be cancelled if the client disconnects.
650
+ // This is useful for long running queries, where it might be desirable for the client to be able to cancel the request before all the data is received. However this requires a worker thread to be allocated to the query until it completes, which may affect performance and limit the number of concurrent queries that can be executed due to file handle limitations.
651
+ // Only applicable to \`SELECT\` statements, ignored otherwise.
652
+ bool can_cancel = 5;
547
653
 
548
- // We then serialise the entire request using the original serialisation method for Invoke.
549
- payloadRequest = {
550
- iv,
551
- payload: invokePayload
552
- };
654
+ //The values to set for each parameter defined in a database view.
655
+ //This field is only relevant to 'query' databases, i.e. those with kind \`tdx:sqlite-view\`.
656
+ //The values in the map should be keyed by parameter name.
657
+ // A query may have no parameters defined, in which case leave this field empty.
658
+ map<string, tdx.volt_api.volt.v1.AttributeValue> parameter = 6;
659
+ }
553
660
 
554
- if (this._voltClient.isRelayed) {
555
- // When tunnelling, we serialise the request using the original serialisation
556
- // method for Invoke.
557
- payloadRequest =
558
- voltGrpcClient.Invoke.requestSerializeOriginal(payloadRequest);
559
- }
560
- } else {
561
- payloadRequest = request;
562
- }
661
+ // Intentionally empty.
662
+ message SqlExecuteNext {
663
+ }
563
664
 
564
- return payloadRequest;
665
+ // Ends the call.
666
+ message SqlExecuteEnd {
667
+ // Set to commit the transaction. If not set, the transaction will be rolled back.
668
+ bool commit_transaction = 1;
565
669
  }
566
670
 
567
- function _parseResponse(method, meta, response) {
568
- // log("parseResponse for %s", this._methodName);
569
- let invokeResponse = {};
671
+ message SqlExecuteRequest {
672
+ oneof payload {
673
+ // Initialise the request with the database id and whether to execute within a transaction.
674
+ // Also includes the SQL statement to execute.
675
+ SqlExecuteStart start = 2;
570
676
 
571
- const isServiceRelayed =
572
- this._service?.service_description.host_type ===
573
- "SERVICE_HOST_TYPE_RELAYED";
677
+ // To retrieve subsequent pages, send a \`next\` request.
678
+ SqlExecuteNext next = 3;
574
679
 
575
- const voltGrpcClient = this._voltClient.getVoltAPIClient();
680
+ // To end the call, send an \`end\` request. Only really necessary for transaction-based calls, otherwise clients can just close the stream.
681
+ SqlExecuteEnd end = 4;
682
+ }
683
+ }
576
684
 
577
- if (this._voltClient.isRelayed || isServiceRelayed) {
578
- if (this._voltClient.isRelayed) {
579
- // Deserialise the incoming response using the original deserialisation method for Invoke.
580
- response = voltGrpcClient.Invoke.responseDeserializeOriginal(response);
581
- }
685
+ // One of the following fields will be present in any given message.
686
+ message SqlExecuteResponse {
687
+ oneof payload {
688
+ // A status will be sent on error.
689
+ tdx.volt_api.volt.v1.Status status = 1;
582
690
 
583
- if (response.key_exchange) {
584
- const encryptionKey = response.key_exchange.encryption_key;
585
- const nonce = response.key_exchange.nonce;
586
- const signature = response.key_exchange.signature;
691
+ // The initial response for SELECT statements will be a header containing the column names and types.
692
+ RowHeader header = 2;
587
693
 
588
- // The signed message is the encryption key and the nonce.
589
- const message = new Uint8Array(encryptionKey.length + nonce.length);
590
- message.set(encryptionKey);
591
- message.set(nonce, encryptionKey.length);
592
-
593
- // Check the signature using the target service public key.
594
- const targetKey = keyFromPem$2(
595
- this._service?.service_description?.host_public_key ||
596
- this._voltClient.credential.voltPublicKey
597
- ).publicKey;
598
-
599
- if (!verify(targetKey, message, signature)) {
600
- throw new Error("signature verification failed");
601
- }
602
-
603
- invokeResponse = {
604
- key_exchange: {
605
- encryption_key: formatPEM$1(toBase64(encryptionKey), "PUBLIC KEY")
606
- }
607
- };
608
- } else if (response.payload) {
609
- let decryptedPayload;
610
- if (this._encryptionKey) {
611
- decryptedPayload = aesDecrypt$1(
612
- this._encryptionKey,
613
- response.iv,
614
- response.payload
615
- );
616
- } else {
617
- decryptedPayload = response.payload;
618
- }
619
-
620
- // Deserialise the decrypted payload using the Tunnel deserialisation method.
621
- const responsePayload =
622
- voltGrpcClient.Tunnel.requestDeserialize(decryptedPayload);
623
- if (responsePayload.payload === "method_payload") {
624
- try {
625
- // Deserialise the method payload.
626
- if (this._voltClient.isRelayed) {
627
- // Use the original deserialisation method for the target method.
628
- invokeResponse.payload = this._grpcClient[
629
- method
630
- ].responseDeserializeOriginal(
631
- responsePayload.method_payload.payload
632
- );
633
- } else {
634
- invokeResponse.payload = this._grpcClient[
635
- method
636
- ].responseDeserialize(responsePayload.method_payload.payload);
637
- }
638
- } catch (err) {
639
- log$5("failure deserialising payload for %s", this._methodName);
640
- throw new Error(
641
- "failure deserialising payload - check protobuf definition matches with data being sent"
642
- );
643
- }
644
- } else if (responsePayload.payload === "method_end") {
645
- invokeResponse = responsePayload.method_end;
646
- } else {
647
- log$5("unexpected tunnel payload type: %s", responsePayload.payload);
648
- }
649
- } else if (response.status?.message) {
650
- // There's been an error in the tunnel.
651
- log$5(
652
- "Tunnel status received: %s, code %d, description: %s",
653
- response.status.message,
654
- response.status.code,
655
- response.status.description || "n/a"
656
- );
657
- invokeResponse = {
658
- ended: true,
659
- error: `Error in tunnel: ${response.status.message}`
660
- };
661
- } else {
662
- invokeResponse.methodId = response.invoke_id;
663
- log$5("started method %d for %s", invokeResponse.methodId, method);
664
- }
665
- } else {
666
- invokeResponse = { payload: response };
667
- }
668
-
669
- return invokeResponse;
670
- }
671
-
672
- class GRPCCall extends EventEmitter__default["default"] {
673
- constructor(voltClient, methodName, methodType, service = undefined) {
674
- super();
675
- this._methodName = methodName;
676
- this._methodType = methodType;
677
- this._call = null;
678
- this._voltClient = voltClient;
679
- this._service = service;
680
- this._keyExchangePending = false;
681
- this._pendingRequests = [];
682
- this._encryptionKey = null;
683
- this._ended = false;
684
- }
685
-
686
- start(grpcClient, request, unary = false) {
687
- this._grpcClient = grpcClient;
688
-
689
- if (typeof this._grpcClient[this._methodName] !== "function") {
690
- throw new Error(`method not found: '${this._methodName}'`);
691
- }
692
-
693
- this._initialRequest = _prepareInvokeRequest.call(
694
- this,
695
- request,
696
- this._methodName,
697
- this._methodType
698
- );
699
-
700
- const isServiceRelayed =
701
- this._service?.service_description.host_type ===
702
- "SERVICE_HOST_TYPE_RELAYED";
703
-
704
- let callClient;
705
- if (this._voltClient.isRelayed || isServiceRelayed) {
706
- callClient = this._voltClient.getVoltAPIClient();
707
- } else {
708
- callClient = this._grpcClient;
709
- }
710
-
711
- this._call = callClient[this._initialRequest.method](
712
- this._initialRequest?.meta?.metadata
713
- );
714
-
715
- this._call.on("data", (streamResponse) => {
716
- if (this._ended) {
717
- log$5("ignoring data after end on %s", this._methodName);
718
- return;
719
- }
720
-
721
- try {
722
- const parsedResponse = _parseResponse.call(
723
- this,
724
- this._methodName,
725
- this._initialRequest.meta,
726
- streamResponse
727
- );
728
- if (parsedResponse.key_exchange) {
729
- const peerKey = keyFromPem$2(
730
- parsedResponse.key_exchange.encryption_key
731
- ).publicKey;
732
-
733
- this._encryptionKey = deriveSharedKey$1(
734
- this._initialRequest.meta.sharedKey.key,
735
- peerKey,
736
- x25519$1
737
- );
738
-
739
- // We've exchanged keys, so send any queued payloads on the next tick.
740
- process.nextTick(() => {
741
- this._pendingRequests.forEach((pendingRequest) => {
742
- this.write(pendingRequest);
743
- });
744
- this._pendingRequests = [];
745
- });
746
- } else if (parsedResponse.payload) {
747
- // Interpret a non-empty status message as an error.
748
- if (parsedResponse.payload?.status?.message) {
749
- this.emit(
750
- "error",
751
- new Error(parsedResponse.payload.status.message)
752
- );
753
- } else {
754
- this.emit("data", parsedResponse.payload);
755
- }
756
- } else if (parsedResponse.ended) {
757
- if (parsedResponse.error) {
758
- this.emit("error", new Error(parsedResponse.error));
759
- } else {
760
- // Not sure this is necessary - we should receive "end" from grpc soon...
761
- this.emit("end");
762
- }
763
- } else {
764
- log$5("method id is %s", parsedResponse.methodId);
765
- }
766
- } catch (err) {
767
- log$5(
768
- "failure processing %s call response: %s",
769
- this._methodName,
770
- err.message
771
- );
772
- this.emit("error", err);
773
- }
774
- });
775
-
776
- this._call.on("error", (err) => {
777
- log$5(
778
- "ERROR - intercepted stream error on %s: %s",
779
- this._methodName,
780
- err.message
781
- );
782
- this.emit("error", err);
783
- });
784
-
785
- this._call.on("finish", () => {
786
- // The read side has ended (i.e. we called end()).
787
- log$5("finished call %s", this._methodName);
788
- this.emit("finish");
789
- });
790
-
791
- this._call.on("end", () => {
792
- // The remote peer ended the stream.
793
- log$5(
794
- "ended call %s, method id: %s",
795
- this._methodName,
796
- this._initialRequest.methodId || "n/a - not tunnelling"
797
- );
798
- this.emit("end");
799
- });
800
-
801
- if (this._initialRequest.request) {
802
- if (unary) {
803
- this._call.end(this._initialRequest.request);
804
- } else {
805
- // Allow callee to attach event handlers before we actually send the initial payload.
806
- process.nextTick(() => this._call.write(this._initialRequest.request));
807
- }
808
- }
809
-
810
- return this;
811
- }
812
-
813
- end() {
814
- if (this._call) {
815
- this._ended = true;
816
- return this._call.end();
817
- } else {
818
- throw new Error("call not initialised");
819
- }
820
- }
821
-
822
- pause() {
823
- log$5("pausing call %s", this._methodName);
824
- return this._call?.pause();
825
- }
826
-
827
- resume() {
828
- log$5("resuming call %s", this._methodName);
829
- return this._call?.resume();
830
- }
831
-
832
- get writable() {
833
- return this._call?.writable;
834
- }
835
-
836
- write(request) {
837
- if (!this._call) {
838
- log$5("ERROR - call not initialised");
839
- throw new Error("call not initialised");
840
- } else if (this._initialRequest && !this._keyExchangePending) {
841
- const payloadRequest = _preparePayloadRequest.call(
842
- this,
843
- request,
844
- this._initialRequest
845
- );
846
- return this._call.write(payloadRequest);
847
- } else {
848
- this._keyExchangePending = false;
849
-
850
- this._initialRequest = _prepareInvokeRequest.call(
851
- this,
852
- request,
853
- this._methodName,
854
- this._methodType
855
- );
856
-
857
- return this._call.write(this._initialRequest.request);
858
- }
859
- }
860
- }
861
-
862
- /**
863
- *
864
- * This is copied from the proto-loader package file @grpc/proto-loader/build/src/index.js,
865
- * simply in order to export the createPackageDefinition function, which we need to be
866
- * able to dynamically load protobuf from strings stored in the Volt database rather than
867
- * from files.
868
- *
869
- * There doesn't seem to be any plans to export this function from the package itself,
870
- * see https://github.com/grpc/grpc-node/issues/550.
871
- *
872
- * This is deemed to be low-risk as the protobuf code is not expected to change often.
873
- *
874
- * Used by `getServiceDescriptors` in `proto-utils.js`.
875
- *
876
- */
877
-
878
- const IDEMPOTENCY_UNKNOWN = "IDEMPOTENCY_UNKNOWN";
879
-
880
- const descriptorOptions = {
881
- longs: String,
882
- enums: String,
883
- bytes: String,
884
- defaults: true,
885
- oneofs: true,
886
- json: true
887
- };
888
- function joinName(baseName, name) {
889
- if (baseName === "") {
890
- return name;
891
- } else {
892
- return baseName + "." + name;
893
- }
894
- }
895
- function isHandledReflectionObject(obj) {
896
- return (
897
- obj instanceof Protobuf__default["default"].Service ||
898
- obj instanceof Protobuf__default["default"].Type ||
899
- obj instanceof Protobuf__default["default"].Enum
900
- );
901
- }
902
- function isNamespaceBase(obj) {
903
- return obj instanceof Protobuf__default["default"].Namespace || obj instanceof Protobuf__default["default"].Root;
904
- }
905
- function getAllHandledReflectionObjects(obj, parentName) {
906
- const objName = joinName(parentName, obj.name);
907
- if (isHandledReflectionObject(obj)) {
908
- return [[objName, obj]];
909
- } else {
910
- if (isNamespaceBase(obj) && typeof obj.nested !== "undefined") {
911
- return Object.keys(obj.nested)
912
- .map((name) => {
913
- return getAllHandledReflectionObjects(obj.nested[name], objName);
914
- })
915
- .reduce(
916
- (accumulator, currentValue) => accumulator.concat(currentValue),
917
- []
918
- );
919
- }
920
- }
921
- return [];
922
- }
923
- function createDeserializer(cls, options) {
924
- return function deserialize(argBuf) {
925
- return cls.toObject(cls.decode(argBuf), options);
926
- };
927
- }
928
- function createSerializer(cls) {
929
- return function serialize(arg) {
930
- if (Array.isArray(arg)) {
931
- throw new Error(
932
- `Failed to serialize message: expected object with ${cls.name} structure, got array instead`
933
- );
934
- }
935
- const message = cls.fromObject(arg);
936
- return cls.encode(message).finish();
937
- };
938
- }
939
- function mapMethodOptions(options) {
940
- return (options || []).reduce(
941
- (obj, item) => {
942
- for (const [key, value] of Object.entries(item)) {
943
- switch (key) {
944
- case "uninterpreted_option":
945
- obj.uninterpreted_option.push(item.uninterpreted_option);
946
- break;
947
- default:
948
- obj[key] = value;
949
- }
950
- }
951
- return obj;
952
- },
953
- {
954
- deprecated: false,
955
- idempotency_level: IDEMPOTENCY_UNKNOWN,
956
- uninterpreted_option: []
957
- }
958
- );
959
- }
960
- function createMethodDefinition(method, serviceName, options, fileDescriptors) {
961
- /* This is only ever called after the corresponding root.resolveAll(), so we
962
- * can assume that the resolved request and response types are non-null */
963
- const requestType = method.resolvedRequestType;
964
- const responseType = method.resolvedResponseType;
965
- return {
966
- path: "/" + serviceName + "/" + method.name,
967
- requestStream: !!method.requestStream,
968
- responseStream: !!method.responseStream,
969
- requestSerialize: createSerializer(requestType),
970
- requestDeserialize: createDeserializer(requestType, options),
971
- responseSerialize: createSerializer(responseType),
972
- responseDeserialize: createDeserializer(responseType, options),
973
- // TODO(murgatroid99): Find a better way to handle this
974
- originalName: camelCase__default["default"](method.name),
975
- requestType: createMessageDefinition(requestType, fileDescriptors),
976
- responseType: createMessageDefinition(responseType, fileDescriptors),
977
- options: mapMethodOptions(method.parsedOptions)
978
- };
979
- }
980
- function createServiceDefinition(service, name, options, fileDescriptors) {
981
- const def = {};
982
- for (const method of service.methodsArray) {
983
- def[method.name] = createMethodDefinition(
984
- method,
985
- name,
986
- options,
987
- fileDescriptors
988
- );
989
- }
990
- return def;
991
- }
992
- function createMessageDefinition(message, fileDescriptors) {
993
- const messageDescriptor = message.toDescriptor("proto3");
994
- return {
995
- format: "Protocol Buffer 3 DescriptorProto",
996
- type: messageDescriptor.$type.toObject(
997
- messageDescriptor,
998
- descriptorOptions
999
- ),
1000
- fileDescriptorProtos: fileDescriptors
1001
- };
1002
- }
1003
- function createEnumDefinition(enumType, fileDescriptors) {
1004
- const enumDescriptor = enumType.toDescriptor("proto3");
1005
- return {
1006
- format: "Protocol Buffer 3 EnumDescriptorProto",
1007
- type: enumDescriptor.$type.toObject(enumDescriptor, descriptorOptions),
1008
- fileDescriptorProtos: fileDescriptors
1009
- };
1010
- }
1011
- /**
1012
- * function createDefinition(obj: Protobuf.Service, name: string, options:
1013
- * Options): ServiceDefinition; function createDefinition(obj: Protobuf.Type,
1014
- * name: string, options: Options): MessageTypeDefinition; function
1015
- * createDefinition(obj: Protobuf.Enum, name: string, options: Options):
1016
- * EnumTypeDefinition;
1017
- */
1018
- function createDefinition(obj, name, options, fileDescriptors) {
1019
- if (obj instanceof Protobuf__default["default"].Service) {
1020
- return createServiceDefinition(obj, name, options, fileDescriptors);
1021
- } else if (obj instanceof Protobuf__default["default"].Type) {
1022
- return createMessageDefinition(obj, fileDescriptors);
1023
- } else if (obj instanceof Protobuf__default["default"].Enum) {
1024
- return createEnumDefinition(obj, fileDescriptors);
1025
- } else {
1026
- throw new Error("Type mismatch in reflection object handling");
1027
- }
1028
- }
1029
-
1030
- function createPackageDefinition(root, options) {
1031
- const def = {};
1032
- root.resolveAll();
1033
- const descriptorList = root.toDescriptor("proto3").file;
1034
- const bufferList = descriptorList.map((value) =>
1035
- Buffer.from(descriptor__default["default"].FileDescriptorProto.encode(value).finish())
1036
- );
1037
- for (const [name, obj] of getAllHandledReflectionObjects(root, "")) {
1038
- def[name] = createDefinition(obj, name, options, bufferList);
1039
- }
1040
- return def;
1041
- }
1042
-
1043
- /**
1044
- * This file is generated by the 'generate-proto-assets.js' script.
1045
- * Do not modify this file directly.
1046
- */
1047
-
1048
- const sqlite = `syntax = "proto3";
1049
-
1050
- package tdx.volt_api.data.v1;
1051
-
1052
- // Just reflect SQLite types for now.
1053
- enum DataType {
1054
- DATA_TYPE_UNKNOWN = 0;
1055
- DATA_TYPE_TEXT = 1;
1056
- DATA_TYPE_INTEGER = 2;
1057
- DATA_TYPE_REAL = 3;
1058
- DATA_TYPE_BLOB = 4;
1059
- DATA_TYPE_NULL = 5;
1060
- }
1061
-
1062
- message Column {
1063
- string name = 1;
1064
- string description = 2;
1065
- DataType type = 3;
1066
- }
1067
-
1068
- message Schema {
1069
- string name = 1;
1070
- string description = 2;
1071
- repeated Column column = 3;
1072
- }
1073
-
1074
- message Variant {
1075
- oneof data {
1076
- string text = 1;
1077
- int64 integer = 2;
1078
- double real = 3;
1079
- bytes blob = 4;
1080
- bool null = 5;
1081
- }
1082
- }
1083
-
1084
- message RowHeader {
1085
- repeated Column column = 1;
1086
- }
1087
-
1088
- message VariantRow {
1089
- repeated Variant column = 1;
1090
- }
1091
- `;
1092
- const sqlite_database_api = `syntax = "proto3";
1093
-
1094
- package tdx.volt_api.data.v1;
1095
-
1096
- import "tdx/volt_api/volt/v1/status.proto";
1097
- import "tdx/volt_api/data/v1/sqlite.proto";
1098
- import "tdx/volt_api/volt/v1/volt.proto";
1099
-
1100
- // The Sqlite Database API exposes functions that enable clients to manipulate data in a given Volt database.
1101
- // Use the Sqlite Server API to create the database resource.
1102
- service SqliteDatabaseAPI {
1103
- // Execute multiple SQL statements in a single transaction via a single RPC.
1104
- rpc BulkUpdate(SqlBulkUpdateRequest) returns (SqlBulkUpdateResponse);
1105
-
1106
- // Execute a single SQL statement. Any valid SQL is accepted. In order to execute non-SELECT statements, the caller must have the "write" permission.
1107
- rpc Execute(stream SqlExecuteRequest) returns (stream SqlExecuteResponse);
1108
-
1109
- // Import CSV data into a table.
1110
- // The import handler will inspect the incoming CSV data, infer the columns and data types required, and create and populate the SQL table.
1111
- // The CSV must contain a header row containing the column names and at least one row of data so that the types can be inferred.
1112
- // The importer assumes all data in any given CSV file relates to a single table.
1113
- // The data can either be streamed in chunks or retrieved from an existing resource.
1114
- // If the data is streamed in chunks, the client must call the Close() method on the stream to indicate that it has finished.
1115
- rpc ImportCSV(stream SqlImportCSVRequest) returns (stream SqlImportCSVResponse);
1116
- }
1117
-
1118
- message SqlBulkUpdateRequest {
1119
- // The id of the database to update.
1120
- string database_id = 1;
1121
-
1122
- // The SQL statements to execute. The statements will be executed within a transaction and will be committed if all statements succeed.
1123
- // Bear in mind the maximum size limit of a single message is 64MB, and around 1MB seems to be optimal in terms of performance.
1124
- repeated string statement = 2;
1125
- }
1126
-
1127
- message SqlBulkUpdateResponse {
1128
- // Details of any error that occurred on the call.
1129
- tdx.volt_api.volt.v1.Status status = 1;
1130
- }
1131
-
1132
- message SqlExecuteStart {
1133
- // The id of the database to execute on.
1134
- string database_id = 1;
1135
-
1136
- // Set to start a transaction. If not set, each statement will be executed in its own transaction.
1137
- bool transaction = 2;
1138
-
1139
- // The SQL statement to execute.
1140
- string statement = 3;
1141
-
1142
- // This can be used to limit the number of rows returned by \`SELECT\` statements to avoid overloading a client. It is similar to using 'LIMIT/OFFSET' clauses on the 'SELECT' statement, but this method is easier to manage and the entire result set will be prepared on the Volt, and the client can then page through it by sending successive messages.
1143
- uint32 page_size = 4;
1144
-
1145
- // Set to indicate that the query will be cancelled if the client disconnects.
1146
- // This is useful for long running queries, where it might be desirable for the client to be able to cancel the request before all the data is received. However this requires a worker thread to be allocated to the query until it completes, which may affect performance and limit the number of concurrent queries that can be executed due to file handle limitations.
1147
- // Only applicable to \`SELECT\` statements, ignored otherwise.
1148
- bool can_cancel = 5;
1149
-
1150
- //The values to set for each parameter defined in a database view.
1151
- //This field is only relevant to 'query' databases, i.e. those with kind \`tdx:sqlite-view\`.
1152
- //The values in the map should be keyed by parameter name.
1153
- // A query may have no parameters defined, in which case leave this field empty.
1154
- map<string, tdx.volt_api.volt.v1.AttributeValue> parameter = 6;
1155
- }
1156
-
1157
- // Intentionally empty.
1158
- message SqlExecuteNext {
1159
- }
1160
-
1161
- // Ends the call.
1162
- message SqlExecuteEnd {
1163
- // Set to commit the transaction. If not set, the transaction will be rolled back.
1164
- bool commit_transaction = 1;
1165
- }
1166
-
1167
- message SqlExecuteRequest {
1168
- oneof payload {
1169
- // Initialise the request with the database id and whether to execute within a transaction.
1170
- // Also includes the SQL statement to execute.
1171
- SqlExecuteStart start = 2;
1172
-
1173
- // To retrieve subsequent pages, send a \`next\` request.
1174
- SqlExecuteNext next = 3;
1175
-
1176
- // To end the call, send an \`end\` request. Only really necessary for transaction-based calls, otherwise clients can just close the stream.
1177
- SqlExecuteEnd end = 4;
1178
- }
1179
- }
1180
-
1181
- // One of the following fields will be present in any given message.
1182
- message SqlExecuteResponse {
1183
- oneof payload {
1184
- // A status will be sent on error.
1185
- tdx.volt_api.volt.v1.Status status = 1;
1186
-
1187
- // The initial response for SELECT statements will be a header containing the column names and types.
1188
- RowHeader header = 2;
1189
-
1190
- // Each row in the result set will be sent as a VariantRow.
1191
- VariantRow row = 3;
694
+ // Each row in the result set will be sent as a VariantRow.
695
+ VariantRow row = 3;
1192
696
 
1193
697
  // NYI - For INSERT/UPDATE statements the number of affected rows will be sent.
1194
698
  uint32 affected_rows = 4;
@@ -3318,860 +2822,1367 @@ enum ConnectResourceEvent {
3318
2822
  CONNECT_RESOURCE_EVENT_DATABASE_WRITE = 6;
3319
2823
  }
3320
2824
 
3321
- message ConnectResource {
3322
- // The type of resource event that has occurred.
3323
- ConnectResourceEvent event = 1;
2825
+ message ConnectResource {
2826
+ // The type of resource event that has occurred.
2827
+ ConnectResourceEvent event = 1;
2828
+
2829
+ // Details of the resource.
2830
+ Resource resource = 2;
2831
+ }
2832
+
2833
+ message ConnectAuthRequest {
2834
+ Session session = 1;
2835
+
2836
+ string context = 5;
2837
+
2838
+ string challenge = 6;
2839
+
2840
+ uint64 timestamp = 7;
2841
+ }
2842
+
2843
+ message ConnectDIDRegistryUpdate {
2844
+ DIDRegistryUpdate update = 1;
2845
+ }
2846
+
2847
+ message ConnectEvent {
2848
+ oneof event {
2849
+ // An authentication request event.
2850
+ ConnectAuthRequest connect_auth_request = 1;
2851
+
2852
+ // A resource event notification, such as updated or deleted.
2853
+ ConnectResource connect_resource = 2;
2854
+
2855
+ // A DID registry entry update.
2856
+ ConnectDIDRegistryUpdate did_registry_update = 3;
2857
+ }
2858
+ }
2859
+
2860
+ // One of the following payloads will be present in a given ConnectRequest message.
2861
+ message ConnectRequest {
2862
+ oneof payload {
2863
+ // A ConnectHello message is the first message a client sends to the target upon successfully starting the call.
2864
+ ConnectHello hello = 1;
2865
+
2866
+ // Indicates the client is closing the connection.
2867
+ ConnectGoodbye goodbye = 2;
2868
+
2869
+ // Clients should periodically send ping requests to the target to confirm the stream is still live.
2870
+ ConnectPing ping = 4;
2871
+
2872
+ // Send invocation responses back to the caller.
2873
+ // n.b. The 'request' and 'response' semantics are inverted with remote invocations because the Relayed connection is established by a request **from** the 'target' Volt to the Relay. Hence any invocation on behalf of a client of the Relay involves sending a **response** back down the Relay connection to the 'target' Volt.
2874
+ InvokeResponse invoke_response = 5;
2875
+
2876
+ // Send HTTP response back to the originating request.
2877
+ // n.b. The 'request' and 'response' semantics are inverted with HTTP proxying for the same reason as \`invoke_response\` above.
2878
+ HttpResponse http_response = 6;
2879
+ }
2880
+ }
2881
+
2882
+ // One of the following payloads will be present in a given ConnectResponse message.
2883
+ message ConnectResponse {
2884
+ // Details of any error that occurred on the call.
2885
+ tdx.volt_api.volt.v1.Status status = 1;
2886
+
2887
+ oneof payload {
2888
+ // Server response to initial handshake.
2889
+ ConnectAcknowledge acknowledge = 2;
2890
+
2891
+ // Indicates the server is ending the connection.
2892
+ ConnectGoodbye goodbye = 3;
2893
+
2894
+ // Notifies clients of various events on the Volt.
2895
+ ConnectEvent evt = 4;
2896
+
2897
+ // Periodic ping response.
2898
+ ConnectPing ping = 6;
2899
+
2900
+ // Send an invocation request to a remote target.
2901
+ // n.b. The 'request' and 'response' semantics are inverted for remote invocations because the Relayed connection is established by a request **from** the 'target' Volt to the Relay. Hence any invocation on behalf of a client of the Relay involves sending a **response** back down the Relay connection to the 'target' Volt.
2902
+ InvokeRequest invoke_request = 7;
2903
+
2904
+ // Send an HTTP request to a remote target.
2905
+ // n.b. The 'request' and 'response' semantics are inverted for HTTP requests for the same reason as \`invoke_request\` above.
2906
+ HttpRequest http_request = 8;
2907
+ }
2908
+ }
2909
+
2910
+ enum CopyResourceMode {
2911
+ COPY_RESOURCE_MODE_UNKNOWN = 0;
2912
+ COPY_RESOURCE_MODE_COPY = 1;
2913
+ COPY_RESOURCE_MODE_LINK = 2;
2914
+ }
2915
+
2916
+ message CopyResourceRequest {
2917
+ // The id of the resource to copy.
2918
+ string resource_id = 1;
2919
+
2920
+ // The id of the resource to receive the new copy.
2921
+ string to_resource_id = 2;
2922
+
2923
+ // The copy mode to use.
2924
+ CopyResourceMode mode = 3;
2925
+
2926
+ // Indicates if all descendants of the resource should be copied too. Only relevant for COPY_RESOURCE_MODE_COPY mode.
2927
+ bool recursive = 4;
2928
+
2929
+ // The parent resource to link from, only relevant for COPY_RESOURCE_MODE_LINK mode.
2930
+ string from_resource_id = 5;
2931
+ }
2932
+
2933
+ message CopyResourceResponse {
2934
+ // Details of any error that occurred on the call.
2935
+ tdx.volt_api.volt.v1.Status status = 1;
2936
+ }
2937
+
2938
+ message DeleteAccessRequest {
2939
+ // The id of the access rule to delete.
2940
+ string id = 1;
2941
+ }
2942
+
2943
+ message DeleteAccessResponse {
2944
+ // Details of any error that occurred on the call.
2945
+ tdx.volt_api.volt.v1.Status status = 1;
2946
+ }
2947
+
2948
+ message DeleteResourceRequest {
2949
+ // The resource to delete.
2950
+ string resource_id = 1;
2951
+
2952
+ // Set to indicate all descendant resources should also be deleted.
2953
+ // If this is not set and the resource has descendants, the call will fail.
2954
+ bool recursive = 2;
2955
+
2956
+ // Set to attempt to unlink the resource from this parent resource, rather than completely delete it.
2957
+ // The resource will be removed as a descendant from the \`parent_id\` resource. If the resource is has more than one parent, it will not be removed from those other parents.
2958
+ // Note that if the resource's only parent is \`parent_id\` it will be removed from that parent and deleted as normal.
2959
+ string parent_id = 3;
2960
+ }
2961
+
2962
+ message DeleteResourceResponse {
2963
+ // Details of any error that occurred on the call.
2964
+ tdx.volt_api.volt.v1.Status status = 1;
2965
+ }
2966
+
2967
+ message DiscoverServicesRequest {
2968
+ // List the service APIs that should be discovered.
2969
+ // The response will include services that match **any of** the terms given.
2970
+ // Use of '*' to indicate wildcards is supported.
2971
+ repeated string service_api = 1;
2972
+
2973
+ // Set to indicate that offline services should be included in the response.
2974
+ bool include_offline = 2;
2975
+
2976
+ // Set to include the service attributes in the response.
2977
+ bool include_attributes = 3;
2978
+
2979
+ // Set to include the service protobuf in the response.
2980
+ bool include_protobuf = 4;
2981
+ }
2982
+
2983
+ message DiscoverServicesResponse {
2984
+ // Details of any error that occurred on the call.
2985
+ tdx.volt_api.volt.v1.Status status = 1;
2986
+
2987
+ // The services that were discovered.
2988
+ repeated Resource resource = 2;
2989
+ }
2990
+
2991
+ message GetAccessRequest {
2992
+ // The resource id that is the target of the access rule. If omitted, all resources will be considered.
2993
+ string resource_id = 1;
2994
+
2995
+ // The identity id that is the subject of the access rule. If omitted, all identities will be considered.
2996
+ string identity_did = 2;
2997
+
2998
+ // The type of access to retrieve, if omitted all access will be considered.
2999
+ string access = 4;
3000
+
3001
+ // The type of decision, if omitted all decisions will be considered.
3002
+ PolicyDecision decision = 5;
3003
+ }
3004
+
3005
+ message GetAccessResponse {
3006
+ // Details of any error that occurred on the call.
3007
+ tdx.volt_api.volt.v1.Status status = 1;
3008
+
3009
+ // The access rules that match the criteria.
3010
+ repeated Access access = 2;
3011
+ }
3012
+
3013
+ message GetIdentitiesRequest {
3014
+ // Optional name of the identity.
3015
+ // Use '*' to perform a wildcard search.
3016
+ // If omitted all identities will be retrieved.
3017
+ string name = 1;
3018
+
3019
+ // The identity alias criteria, if omitted all identities will be considered.
3020
+ IdentityAlias alias = 2;
3021
+ }
3022
+
3023
+ message GetIdentitiesResponse {
3024
+ // Details of any error that occurred on the call.
3025
+ tdx.volt_api.volt.v1.Status status = 1;
3026
+
3027
+ // The identity list that matched the criteria.
3028
+ repeated Identity identity = 2;
3029
+ }
3030
+
3031
+ // One of \`identity_did\` or \`fingerprint\` must be populated.
3032
+ message GetIdentityRequest {
3033
+ oneof lookup {
3034
+ // The id of the identity to retrieve.
3035
+ string identity_did = 1;
3036
+ // The public key fingerprint of the identity to retrieve.
3037
+ string fingerprint = 2;
3038
+ }
3039
+ }
3040
+
3041
+ message GetIdentityResponse {
3042
+ // Details of any error that occurred on the call.
3043
+ tdx.volt_api.volt.v1.Status status = 1;
3044
+
3045
+ // The identity details.
3046
+ Identity identity = 2;
3047
+ }
3048
+
3049
+ message GetOneTimeTokenRequest {
3050
+ // Optional token TTL, in seconds. Default is 10 seconds.
3051
+ int32 ttl = 1;
3052
+ }
3053
+
3054
+ message GetOneTimeTokenResponse {
3055
+ // Details of any error that occurred on the call.
3056
+ tdx.volt_api.volt.v1.Status status = 1;
3057
+
3058
+ // The one-time token.
3059
+ string token = 2;
3060
+ }
3061
+
3062
+ // This an empty message.
3063
+ message GetParametersRequest {
3064
+ }
3065
+
3066
+ message GetParametersResponse {
3067
+ // Details of any error that occurred on the call.
3068
+ tdx.volt_api.volt.v1.Status status = 1;
3069
+
3070
+ // The retrieved Volt parameters.
3071
+ VoltParameters parameters = 2;
3072
+ }
3073
+
3074
+ // This an empty message.
3075
+ message GetPolicyRequest {
3076
+ // Set to only retrieve the custom policy document.
3077
+ bool custom_policy = 1;
3078
+ }
3079
+
3080
+ message GetPolicyResponse {
3081
+ // Details of any error that occurred on the call.
3082
+ tdx.volt_api.volt.v1.Status status = 1;
3324
3083
 
3325
- // Details of the resource.
3326
- Resource resource = 2;
3084
+ // The live policy in JSON format.
3085
+ string policy = 2;
3327
3086
  }
3328
3087
 
3329
- message ConnectAuthRequest {
3330
- Session session = 1;
3331
-
3332
- string context = 5;
3088
+ message GetResourceRequest {
3089
+ // The id of the resource to retrieve.
3090
+ string resource_id = 1;
3333
3091
 
3334
- string challenge = 6;
3092
+ // Set to include the resource attributes in the response.
3093
+ bool include_attributes = 2;
3335
3094
 
3336
- uint64 timestamp = 7;
3095
+ // Set to include service description protobuf in the response, if applicable.
3096
+ bool include_protobuf = 3;
3337
3097
  }
3338
3098
 
3339
- message ConnectDIDRegistryUpdate {
3340
- DIDRegistryUpdate update = 1;
3099
+ message GetResourceResponse {
3100
+ // Details of any error that occurred on the call.
3101
+ tdx.volt_api.volt.v1.Status status = 1;
3102
+
3103
+ // The retrieved resource metadata.
3104
+ Resource resource = 2;
3341
3105
  }
3342
3106
 
3343
- message ConnectEvent {
3344
- oneof event {
3345
- // An authentication request event.
3346
- ConnectAuthRequest connect_auth_request = 1;
3107
+ // By default, the lookup is performed by combining the criteria below in the form:
3108
+ // (id = id[0] or id = id[1]) and (name = name[0] or name = name[1]) etc...
3109
+ // To combine using 'or' rather than 'and', set the \`combine_terms_exclusive\` flag.
3110
+ // Also see note below regarding attributes.
3111
+ message GetResourcesRequest {
3112
+ repeated string id = 1;
3347
3113
 
3348
- // A resource event notification, such as updated or deleted.
3349
- ConnectResource connect_resource = 2;
3114
+ // Wildcards permitted.
3115
+ repeated string name = 2;
3350
3116
 
3351
- // A DID registry entry update.
3352
- ConnectDIDRegistryUpdate did_registry_update = 3;
3353
- }
3354
- }
3117
+ // Wildcards permitted.
3118
+ repeated string description = 3;
3355
3119
 
3356
- // One of the following payloads will be present in a given ConnectRequest message.
3357
- message ConnectRequest {
3358
- oneof payload {
3359
- // A ConnectHello message is the first message a client sends to the target upon successfully starting the call.
3360
- ConnectHello hello = 1;
3120
+ // Wildcards permitted.
3121
+ repeated string kind = 4;
3361
3122
 
3362
- // Indicates the client is closing the connection.
3363
- ConnectGoodbye goodbye = 2;
3123
+ repeated string parent_id = 5;
3364
3124
 
3365
- // Clients should periodically send ping requests to the target to confirm the stream is still live.
3366
- ConnectPing ping = 4;
3125
+ // Wildcards permitted.
3126
+ repeated string service_api = 6;
3367
3127
 
3368
- // Send invocation responses back to the caller.
3369
- // n.b. The 'request' and 'response' semantics are inverted with remote invocations because the Relayed connection is established by a request **from** the 'target' Volt to the Relay. Hence any invocation on behalf of a client of the Relay involves sending a **response** back down the Relay connection to the 'target' Volt.
3370
- InvokeResponse invoke_response = 5;
3128
+ repeated string owner = 7;
3371
3129
 
3372
- // Send HTTP response back to the originating request.
3373
- // n.b. The 'request' and 'response' semantics are inverted with HTTP proxying for the same reason as \`invoke_response\` above.
3374
- HttpResponse http_response = 6;
3375
- }
3376
- }
3130
+ repeated string store = 8;
3377
3131
 
3378
- // One of the following payloads will be present in a given ConnectResponse message.
3379
- message ConnectResponse {
3380
- // Details of any error that occurred on the call.
3381
- tdx.volt_api.volt.v1.Status status = 1;
3132
+ // Indicates that the above terms should be combined using 'or' rather than 'and' (the default).
3133
+ bool combine_terms_exclusive = 9;
3382
3134
 
3383
- oneof payload {
3384
- // Server response to initial handshake.
3385
- ConnectAcknowledge acknowledge = 2;
3135
+ // Attributes to search by.
3136
+ repeated ResourceAttributeQuery attribute = 10;
3386
3137
 
3387
- // Indicates the server is ending the connection.
3388
- ConnectGoodbye goodbye = 3;
3138
+ // If set, will return resources where *any of* the attribute queries apply, otherwise will only return resources where *all of* the attribute queries apply.
3139
+ bool any_of = 11;
3389
3140
 
3390
- // Notifies clients of various events on the Volt.
3391
- ConnectEvent evt = 4;
3141
+ // Set to include the resource attributes in the response.
3142
+ bool include_attributes = 12;
3392
3143
 
3393
- // Periodic ping response.
3394
- ConnectPing ping = 6;
3144
+ // Set to include service description protobuf in the response where applicable.
3145
+ bool include_protobuf = 13;
3395
3146
 
3396
- // Send an invocation request to a remote target.
3397
- // n.b. The 'request' and 'response' semantics are inverted for remote invocations because the Relayed connection is established by a request **from** the 'target' Volt to the Relay. Hence any invocation on behalf of a client of the Relay involves sending a **response** back down the Relay connection to the 'target' Volt.
3398
- InvokeRequest invoke_request = 7;
3147
+ // Only retrieve resources that have been modified after the given timestamp. Default is 0, which means all resources will be returned.
3148
+ uint64 modified_since = 14;
3399
3149
 
3400
- // Send an HTTP request to a remote target.
3401
- // n.b. The 'request' and 'response' semantics are inverted for HTTP requests for the same reason as \`invoke_request\` above.
3402
- HttpRequest http_request = 8;
3403
- }
3150
+ // Limit the number of resources returned. Default is 0, which means all resources will be returned.
3151
+ uint32 limit = 15;
3404
3152
  }
3405
3153
 
3406
- enum CopyResourceMode {
3407
- COPY_RESOURCE_MODE_UNKNOWN = 0;
3408
- COPY_RESOURCE_MODE_COPY = 1;
3409
- COPY_RESOURCE_MODE_LINK = 2;
3154
+ message GetResourcesResponse {
3155
+ // Details of any error that occurred on the call.
3156
+ tdx.volt_api.volt.v1.Status status = 1;
3157
+
3158
+ // The list of resources that match the lookup.
3159
+ repeated Resource resource = 2;
3410
3160
  }
3411
3161
 
3412
- message CopyResourceRequest {
3413
- // The id of the resource to copy.
3162
+ message GetResourceAncestorsRequest {
3163
+ // The resource id whose ancestors will be retrieved.
3414
3164
  string resource_id = 1;
3415
3165
 
3416
- // The id of the resource to receive the new copy.
3417
- string to_resource_id = 2;
3166
+ // Optional - if set the resource_id resource will be included in the set of resources returned.
3167
+ bool include_resource_id = 2;
3418
3168
 
3419
- // The copy mode to use.
3420
- CopyResourceMode mode = 3;
3169
+ // Optional - restrict the depth search, e.g. for immediate parents depth = 1
3170
+ int32 depth = 3;
3421
3171
 
3422
- // Indicates if all descendants of the resource should be copied too. Only relevant for COPY_RESOURCE_MODE_COPY mode.
3423
- bool recursive = 4;
3172
+ // Optional - only match ancestors of the given kind.
3173
+ string ancestor_kind = 4;
3424
3174
 
3425
- // The parent resource to link from, only relevant for COPY_RESOURCE_MODE_LINK mode.
3426
- string from_resource_id = 5;
3427
- }
3175
+ // Optional - can be used to determine if a resource is an ancestor.
3176
+ string ancestor_id = 5;
3428
3177
 
3429
- message CopyResourceResponse {
3430
- // Details of any error that occurred on the call.
3431
- tdx.volt_api.volt.v1.Status status = 1;
3432
- }
3178
+ // Set to include the service attributes in the response.
3179
+ bool include_attributes = 6;
3433
3180
 
3434
- message DeleteAccessRequest {
3435
- // The id of the access rule to delete.
3436
- string id = 1;
3181
+ // Set to include the service protobuf in the response.
3182
+ bool include_protobuf = 7;
3437
3183
  }
3438
3184
 
3439
- message DeleteAccessResponse {
3185
+ message GetResourceAncestorsResponse {
3440
3186
  // Details of any error that occurred on the call.
3441
3187
  tdx.volt_api.volt.v1.Status status = 1;
3188
+
3189
+ // The retrieved ancestors.
3190
+ repeated Resource ancestor = 2;
3442
3191
  }
3443
3192
 
3444
- message DeleteResourceRequest {
3445
- // The resource to delete.
3193
+ message GetResourceDescendantsRequest {
3194
+ // The resource id whose descendants will be retrieved.
3446
3195
  string resource_id = 1;
3447
3196
 
3448
- // Set to indicate all descendant resources should also be deleted.
3449
- // If this is not set and the resource has descendants, the call will fail.
3450
- bool recursive = 2;
3197
+ // Optional - if set, the fully populated \`resource_id\` resource (i.e. the parent) will be included in the set of resources returned.
3198
+ bool include_resource_id = 2;
3451
3199
 
3452
- // Set to attempt to unlink the resource from this parent resource, rather than completely delete it.
3453
- // The resource will be removed as a descendant from the \`parent_id\` resource. If the resource is has more than one parent, it will not be removed from those other parents.
3454
- // Note that if the resource's only parent is \`parent_id\` it will be removed from that parent and deleted as normal.
3455
- string parent_id = 3;
3456
- }
3200
+ // Optional - restrict the depth search, e.g. for immediate children depth = 1
3201
+ int32 depth = 3;
3457
3202
 
3458
- message DeleteResourceResponse {
3459
- // Details of any error that occurred on the call.
3460
- tdx.volt_api.volt.v1.Status status = 1;
3461
- }
3203
+ // Optional - only match descendants of the given kind.
3204
+ // If multiple kinds are given, resources matching **any of** the kinds will be included.
3205
+ repeated string descendant_kind = 4;
3462
3206
 
3463
- message DiscoverServicesRequest {
3464
- // List the service APIs that should be discovered.
3465
- // The response will include services that match **any of** the terms given.
3466
- // Use of '*' to indicate wildcards is supported.
3467
- repeated string service_api = 1;
3207
+ // Optional - can be used to determine if a resource is a descendant.
3208
+ string descendant_id = 5;
3468
3209
 
3469
- // Set to indicate that offline services should be included in the response.
3470
- bool include_offline = 2;
3210
+ // Optional - restrict to descendants of a given parent, for use in multi-parent hierarchies.
3211
+ string parent_id = 6;
3212
+
3213
+ // Restrict to a specific named resource.
3214
+ string name = 7;
3215
+
3216
+ // Only retrieve resources that have been modified after the given timestamp. Default is 0, which means all resources will be returned.
3217
+ uint64 modified_since = 8;
3471
3218
 
3472
3219
  // Set to include the service attributes in the response.
3473
- bool include_attributes = 3;
3220
+ bool include_attributes = 9;
3474
3221
 
3475
3222
  // Set to include the service protobuf in the response.
3476
- bool include_protobuf = 4;
3223
+ bool include_protobuf = 10;
3477
3224
  }
3478
3225
 
3479
- message DiscoverServicesResponse {
3226
+ message GetResourceDescendantsResponse {
3480
3227
  // Details of any error that occurred on the call.
3481
3228
  tdx.volt_api.volt.v1.Status status = 1;
3482
3229
 
3483
- // The services that were discovered.
3484
- repeated Resource resource = 2;
3230
+ // The retrieved descendants.
3231
+ repeated Resource descendant = 2;
3485
3232
  }
3486
3233
 
3487
- message GetAccessRequest {
3488
- // The resource id that is the target of the access rule. If omitted, all resources will be considered.
3489
- string resource_id = 1;
3234
+ message GetSessionsRequest {
3235
+ string id = 1;
3490
3236
 
3491
- // The identity id that is the subject of the access rule. If omitted, all identities will be considered.
3492
3237
  string identity_did = 2;
3493
3238
 
3494
- // The type of access to retrieve, if omitted all access will be considered.
3495
- string access = 4;
3239
+ string identity_name = 3;
3496
3240
 
3497
- // The type of decision, if omitted all decisions will be considered.
3498
- PolicyDecision decision = 5;
3241
+ SessionStatus status = 4;
3499
3242
  }
3500
3243
 
3501
- message GetAccessResponse {
3244
+ message GetSessionsResponse {
3502
3245
  // Details of any error that occurred on the call.
3503
3246
  tdx.volt_api.volt.v1.Status status = 1;
3504
3247
 
3505
- // The access rules that match the criteria.
3506
- repeated Access access = 2;
3248
+ repeated Session session = 2;
3507
3249
  }
3508
3250
 
3509
- message GetIdentitiesRequest {
3510
- // Optional name of the identity.
3511
- // Use '*' to perform a wildcard search.
3512
- // If omitted all identities will be retrieved.
3513
- string name = 1;
3251
+ message InvokeRequestKeyExchange {
3252
+ bytes nonce = 1;
3253
+
3254
+ bytes encryption_key = 2;
3255
+
3256
+ bytes signature = 3;
3257
+ }
3258
+
3259
+ message InvokeRequest {
3260
+ // Client-assigned identifier for the request. Will be used to match responses and any subsequent requests.
3261
+ uint64 invoke_id = 1;
3262
+
3263
+ // Optional client token to use for the invocation. This is for use by the websocket proxy and is only necessary on the first request of the rpc.
3264
+ string token = 2;
3265
+
3266
+ // The DID of the target at each hop of the path to the service.
3267
+ // This is used by Relays to route the request.
3268
+ repeated string target_did = 4;
3269
+
3270
+ // The initialisation vector for the request payload encryption. This should be a random 16 byte value, that is different for each request.
3271
+ bytes iv = 5;
3272
+
3273
+ // One of the following payloads will be present in a given InvokeRequest message.
3274
+ oneof request_payload {
3275
+ // A serialised and encrypted instance of \`RemoteResponse\` in pure binary format.
3276
+ bytes payload = 6;
3514
3277
 
3515
- // The identity alias criteria, if omitted all identities will be considered.
3516
- IdentityAlias alias = 2;
3517
- }
3278
+ // A serialised and encrypted instance of \`RemoteResponse\` as serialised JSON.
3279
+ bytes json_payload = 7;
3280
+ }
3518
3281
 
3519
- message GetIdentitiesResponse {
3520
- // Details of any error that occurred on the call.
3521
- tdx.volt_api.volt.v1.Status status = 1;
3282
+ // Indicates the client has ended the invocation.
3283
+ bool client_end = 8;
3522
3284
 
3523
- // The identity list that matched the criteria.
3524
- repeated Identity identity = 2;
3525
- }
3285
+ // Reserved for internal use.
3286
+ uint32 hop_index = 9;
3526
3287
 
3527
- // One of \`identity_did\` or \`fingerprint\` must be populated.
3528
- message GetIdentityRequest {
3529
- oneof lookup {
3530
- // The id of the identity to retrieve.
3531
- string identity_did = 1;
3532
- // The public key fingerprint of the identity to retrieve.
3533
- string fingerprint = 2;
3534
- }
3288
+ // Reserved for internal use.
3289
+ string target_service_id = 10;
3535
3290
  }
3536
3291
 
3537
- message GetIdentityResponse {
3538
- // Details of any error that occurred on the call.
3539
- tdx.volt_api.volt.v1.Status status = 1;
3292
+ message InvokeResponse {
3293
+ // The invocation id to match the originating request.
3294
+ uint64 invoke_id = 1;
3540
3295
 
3541
- // The identity details.
3542
- Identity identity = 2;
3543
- }
3296
+ // Key exchange details for the response payload encryption. Only necessary for the first response.
3297
+ InvokeRequestKeyExchange key_exchange = 2;
3544
3298
 
3545
- message GetOneTimeTokenRequest {
3546
- // Optional token TTL, in seconds. Default is 10 seconds.
3547
- int32 ttl = 1;
3548
- }
3299
+ // The initialisation vector for the response payload encryption. This will be a random 16 byte value, that is different for each response.
3300
+ bytes iv = 4;
3549
3301
 
3550
- message GetOneTimeTokenResponse {
3551
- // Details of any error that occurred on the call.
3552
- tdx.volt_api.volt.v1.Status status = 1;
3302
+ // One of the following payloads will be present in a given InvokeResponse message.
3303
+ oneof response_payload {
3304
+ // A serialised and encrypted instance of \`RemoteRequest\` in pure binary format.
3305
+ bytes payload = 5;
3553
3306
 
3554
- // The one-time token.
3555
- string token = 2;
3556
- }
3307
+ // A serialised and encrypted instance of \`RemoteRequest\` as serialised JSON.
3308
+ bytes json_payload = 6;
3557
3309
 
3558
- // This an empty message.
3559
- message GetParametersRequest {
3310
+ // Details of any error that occurred on the call.
3311
+ tdx.volt_api.volt.v1.Status status = 7;
3312
+ }
3313
+
3314
+ // Indicates the server has ended the invocation.
3315
+ bool server_end = 8;
3560
3316
  }
3561
3317
 
3562
- message GetParametersResponse {
3563
- // Details of any error that occurred on the call.
3564
- tdx.volt_api.volt.v1.Status status = 1;
3318
+ message MoveResourceRequest {
3319
+ // The id of the resource to move.
3320
+ string resource_id = 1;
3565
3321
 
3566
- // The retrieved Volt parameters.
3567
- VoltParameters parameters = 2;
3568
- }
3322
+ // The resource the parent folder to move the resource from.
3323
+ string from_resource_id = 2;
3569
3324
 
3570
- // This an empty message.
3571
- message GetPolicyRequest {
3572
- // Set to only retrieve the custom policy document.
3573
- bool custom_policy = 1;
3325
+ // The target folder to move the resource into.
3326
+ string to_resource_id = 3;
3574
3327
  }
3575
3328
 
3576
- message GetPolicyResponse {
3329
+ message MoveResourceResponse {
3577
3330
  // Details of any error that occurred on the call.
3578
3331
  tdx.volt_api.volt.v1.Status status = 1;
3579
-
3580
- // The live policy in JSON format.
3581
- string policy = 2;
3582
3332
  }
3583
3333
 
3584
- message GetResourceRequest {
3585
- // The id of the resource to retrieve.
3334
+ message RequestAccessRequest {
3335
+ // The target resource id.
3586
3336
  string resource_id = 1;
3587
3337
 
3588
- // Set to include the resource attributes in the response.
3589
- bool include_attributes = 2;
3590
-
3591
- // Set to include service description protobuf in the response, if applicable.
3592
- bool include_protobuf = 3;
3338
+ // The type of access requested.
3339
+ string access = 3;
3593
3340
  }
3594
3341
 
3595
- message GetResourceResponse {
3342
+ message RequestAccessResponse {
3596
3343
  // Details of any error that occurred on the call.
3597
3344
  tdx.volt_api.volt.v1.Status status = 1;
3598
3345
 
3599
- // The retrieved resource metadata.
3600
- Resource resource = 2;
3601
- }
3346
+ // The resource being accessed.
3347
+ string resource_id = 2;
3602
3348
 
3603
- // By default, the lookup is performed by combining the criteria below in the form:
3604
- // (id = id[0] or id = id[1]) and (name = name[0] or name = name[1]) etc...
3605
- // To combine using 'or' rather than 'and', set the \`combine_terms_exclusive\` flag.
3606
- // Also see note below regarding attributes.
3607
- message GetResourcesRequest {
3608
- repeated string id = 1;
3349
+ // The identity attempting access.
3350
+ string identity_did = 3;
3609
3351
 
3610
- // Wildcards permitted.
3611
- repeated string name = 2;
3352
+ // Requested access.
3353
+ string access = 5;
3612
3354
 
3613
- // Wildcards permitted.
3614
- repeated string description = 3;
3355
+ // Assigned decision.
3356
+ PolicyDecision decision = 6;
3615
3357
 
3616
- // Wildcards permitted.
3617
- repeated string kind = 4;
3358
+ // Time at which the request was made.
3359
+ int64 request_time = 7;
3618
3360
 
3619
- repeated string parent_id = 5;
3361
+ // Time at which the decision was taken.
3362
+ int64 decision_time = 8;
3620
3363
 
3621
- // Wildcards permitted.
3622
- repeated string service_api = 6;
3364
+ // Counter of number times this access was requested.
3365
+ int32 request_count = 9;
3366
+ }
3623
3367
 
3624
- repeated string owner = 7;
3368
+ message ResourceAttributeQuery {
3369
+ string attribute_id = 1;
3370
+ AttributeDataType data_type = 2;
3371
+ AttributeValue value = 3;
3372
+ }
3625
3373
 
3626
- repeated string store = 8;
3374
+ message SaveAccessRequest {
3375
+ // Omit \`id\` if creating new access.
3376
+ Access access = 1;
3377
+ }
3627
3378
 
3628
- // Indicates that the above terms should be combined using 'or' rather than 'and' (the default).
3629
- bool combine_terms_exclusive = 9;
3379
+ message SaveAccessResponse {
3380
+ // Details of any error that occurred on the call.
3381
+ tdx.volt_api.volt.v1.Status status = 1;
3382
+ }
3630
3383
 
3631
- // Attributes to search by.
3632
- repeated ResourceAttributeQuery attribute = 10;
3384
+ message SaveIdentityRequest {
3385
+ // Details of the identity to save.
3386
+ Identity identity = 1;
3633
3387
 
3634
- // If set, will return resources where *any of* the attribute queries apply, otherwise will only return resources where *all of* the attribute queries apply.
3635
- bool any_of = 11;
3388
+ // Set to indicate this is a new identity.
3389
+ bool create = 2;
3636
3390
 
3637
- // Set to include the resource attributes in the response.
3638
- bool include_attributes = 12;
3391
+ // The list of aliases that should be removed.
3392
+ // For example, this allows a simple form of key rotation whereby an existing public key alias is replaced by a new one while still maintaining the same root identity id.
3393
+ repeated IdentityAlias delete_alias = 3;
3639
3394
 
3640
- // Set to include service description protobuf in the response where applicable.
3641
- bool include_protobuf = 13;
3395
+ // Reserved for system use.
3396
+ string create_in_parent_id = 4;
3642
3397
 
3643
- // Only retrieve resources that have been modified after the given timestamp. Default is 0, which means all resources will be returned.
3644
- uint64 modified_since = 14;
3398
+ // Set to indicate the identity aliases should be purged before saving the identity.
3399
+ // If the \`identity\` field contains aliases they will be saved after the purge.
3400
+ // If the \`identity\` field does not contain aliases this effectively deletes all aliases for this identity.
3401
+ // This allows you to selectively update aliases if required, i.e. don't set this flag and include a single alias in the update.
3402
+ bool purge_aliases = 5;
3645
3403
 
3646
- // Limit the number of resources returned. Default is 0, which means all resources will be returned.
3647
- uint32 limit = 15;
3404
+ string did_document = 6;
3405
+
3406
+ // The signature of the identity did document, if present.
3407
+ string did_update_signature = 7;
3648
3408
  }
3649
3409
 
3650
- message GetResourcesResponse {
3410
+ message SaveIdentityResponse {
3651
3411
  // Details of any error that occurred on the call.
3652
3412
  tdx.volt_api.volt.v1.Status status = 1;
3653
3413
 
3654
- // The list of resources that match the lookup.
3655
- repeated Resource resource = 2;
3414
+ // The updated identity details.
3415
+ Identity identity = 2;
3656
3416
  }
3657
3417
 
3658
- message GetResourceAncestorsRequest {
3659
- // The resource id whose ancestors will be retrieved.
3660
- string resource_id = 1;
3661
-
3662
- // Optional - if set the resource_id resource will be included in the set of resources returned.
3663
- bool include_resource_id = 2;
3418
+ message SaveParametersRequest {
3419
+ // The updated parameters.
3420
+ VoltParameters parameters = 1;
3664
3421
 
3665
- // Optional - restrict the depth search, e.g. for immediate parents depth = 1
3666
- int32 depth = 3;
3422
+ // The current root key passphrase. Only necessary if changes are being made to the Volt key.
3423
+ string key_passphrase = 2;
3667
3424
 
3668
- // Optional - only match ancestors of the given kind.
3669
- string ancestor_kind = 4;
3425
+ // The new root key passphrase. Only necessary if changes are being made to the Volt key.
3426
+ string new_key_passphrase = 3;
3427
+ }
3670
3428
 
3671
- // Optional - can be used to determine if a resource is an ancestor.
3672
- string ancestor_id = 5;
3429
+ message SaveParametersResponse {
3430
+ // Details of any errors that occurred on the call.
3431
+ tdx.volt_api.volt.v1.Status status = 1;
3673
3432
 
3674
- // Set to include the service attributes in the response.
3675
- bool include_attributes = 6;
3433
+ // The updated Volt parameters.
3434
+ VoltParameters parameters = 2;
3676
3435
 
3677
- // Set to include the service protobuf in the response.
3678
- bool include_protobuf = 7;
3436
+ // If set, the client will need to reconnect (usually because the key has changed).
3437
+ bool reconnect = 3;
3679
3438
  }
3680
3439
 
3681
- message GetResourceAncestorsResponse {
3682
- // Details of any error that occurred on the call.
3683
- tdx.volt_api.volt.v1.Status status = 1;
3440
+ message SaveResourceRequest {
3441
+ // Details of the resource to save.
3442
+ Resource resource = 1;
3684
3443
 
3685
- // The retrieved ancestors.
3686
- repeated Resource ancestor = 2;
3687
- }
3444
+ // Set to indicate this is a new resource.
3445
+ bool create = 2;
3688
3446
 
3689
- message GetResourceDescendantsRequest {
3690
- // The resource id whose descendants will be retrieved.
3691
- string resource_id = 1;
3447
+ // The id of the folder resource in which a new resource should be created.
3448
+ // If omitted, the home folder of the currently authenticated identity will be used.
3449
+ string create_in_parent_id = 3;
3692
3450
 
3693
- // Optional - if set, the fully populated \`resource_id\` resource (i.e. the parent) will be included in the set of resources returned.
3694
- bool include_resource_id = 2;
3451
+ // Set to indicate the resource attributes should be purged before saving the resource.
3452
+ // If the \`resource\` field contains attributes they will be saved after the purge.
3453
+ // If the \`resource\` field does not contain attributes this effectively deletes all attributes for this resource.
3454
+ // This allows you to selectively update attributes if required, i.e. don't set this flag and include a single attribute in the update.
3455
+ bool purge_attributes = 4;
3456
+ }
3695
3457
 
3696
- // Optional - restrict the depth search, e.g. for immediate children depth = 1
3697
- int32 depth = 3;
3458
+ message SaveResourceResponse {
3459
+ // Details of any error that occurred on the call.
3460
+ tdx.volt_api.volt.v1.Status status = 1;
3698
3461
 
3699
- // Optional - only match descendants of the given kind.
3700
- // If multiple kinds are given, resources matching **any of** the kinds will be included.
3701
- repeated string descendant_kind = 4;
3462
+ // The updated resource.
3463
+ Resource resource = 2;
3464
+ }
3702
3465
 
3703
- // Optional - can be used to determine if a resource is a descendant.
3704
- string descendant_id = 5;
3466
+ message SaveSessionRequest {
3467
+ Session session = 1;
3468
+ }
3705
3469
 
3706
- // Optional - restrict to descendants of a given parent, for use in multi-parent hierarchies.
3707
- string parent_id = 6;
3470
+ message SaveSessionResponse {
3471
+ // Details of any error that occurred on the call.
3472
+ tdx.volt_api.volt.v1.Status status = 1;
3708
3473
 
3709
- // Restrict to a specific named resource.
3710
- string name = 7;
3474
+ Session session = 2;
3475
+ }
3711
3476
 
3712
- // Only retrieve resources that have been modified after the given timestamp. Default is 0, which means all resources will be returned.
3713
- uint64 modified_since = 8;
3477
+ message SetPolicyRequest {
3478
+ string custom_policy = 1;
3479
+ }
3714
3480
 
3715
- // Set to include the service attributes in the response.
3716
- bool include_attributes = 9;
3481
+ message SetPolicyResponse {
3482
+ // Details of any error that occurred on the call.
3483
+ tdx.volt_api.volt.v1.Status status = 1;
3484
+ }
3717
3485
 
3718
- // Set to include the service protobuf in the response.
3719
- bool include_protobuf = 10;
3486
+ message SetServiceStatusRequest {
3487
+ // The service description details.
3488
+ Resource service = 2;
3720
3489
  }
3721
3490
 
3722
- message GetResourceDescendantsResponse {
3491
+ message SetServiceStatusResponse {
3723
3492
  // Details of any error that occurred on the call.
3724
3493
  tdx.volt_api.volt.v1.Status status = 1;
3725
3494
 
3726
- // The retrieved descendants.
3727
- repeated Resource descendant = 2;
3495
+ // The updated service resource details.
3496
+ Resource resource = 2;
3728
3497
  }
3729
3498
 
3730
- message GetSessionsRequest {
3499
+ message SetAccessRequestDecisionRequest {
3500
+ // The id of the access request.
3731
3501
  string id = 1;
3732
3502
 
3733
- string identity_did = 2;
3503
+ // The decision to save against the access request.
3504
+ PolicyDecision decision = 2;
3505
+ }
3734
3506
 
3735
- string identity_name = 3;
3507
+ message SetAccessRequestDecisionResponse {
3508
+ // Details of any error that occurred on the call.
3509
+ tdx.volt_api.volt.v1.Status status = 1;
3510
+ }
3736
3511
 
3737
- SessionStatus status = 4;
3512
+ // This message is emtpy.
3513
+ message ShutdownRequest {
3738
3514
  }
3739
3515
 
3740
- message GetSessionsResponse {
3516
+ message ShutdownResponse {
3741
3517
  // Details of any error that occurred on the call.
3742
3518
  tdx.volt_api.volt.v1.Status status = 1;
3743
-
3744
- repeated Session session = 2;
3745
3519
  }
3746
3520
 
3747
- message InvokeRequestKeyExchange {
3748
- bytes nonce = 1;
3521
+ message SignVerifyRequest {
3522
+ // Set to indicate this is a request to verify rather than sign.
3523
+ bool verify = 1;
3749
3524
 
3750
- bytes encryption_key = 2;
3525
+ // Set to indicate the signature should be base64 encoded in the response.
3526
+ // Only valid when signing.
3527
+ bool encode = 2;
3751
3528
 
3752
- bytes signature = 3;
3753
- }
3529
+ // The message to sign.
3530
+ // Only valid when signing.
3531
+ string message = 3;
3754
3532
 
3755
- message InvokeRequest {
3756
- // Client-assigned identifier for the request. Will be used to match responses and any subsequent requests.
3757
- uint64 invoke_id = 1;
3533
+ oneof digest {
3534
+ // The digest in raw binary form.
3535
+ // Only valid if verifying.
3536
+ bytes digest_raw = 4;
3758
3537
 
3759
- // Optional client token to use for the invocation. This is for use by the websocket proxy and is only necessary on the first request of the rpc.
3760
- string token = 2;
3538
+ // The digest encoded using base64.
3539
+ // Only valid if verifying.
3540
+ string digest_encoded = 5;
3541
+ }
3761
3542
 
3762
- // The DID of the target at each hop of the path to the service.
3763
- // This is used by Relays to route the request.
3764
- repeated string target_did = 4;
3543
+ // The DID of the identity used to sign or verify the message. If omitted, the authenticated identity will be used.
3544
+ // When signing, the authenticated identity must have volt:sign permission for the given DID.
3545
+ string identity_did = 6;
3546
+ }
3765
3547
 
3766
- // The initialisation vector for the request payload encryption. This should be a random 16 byte value, that is different for each request.
3767
- bytes iv = 5;
3548
+ // Note that if verification was successful the response will be empty (there is no error and no digest is returned).
3549
+ message SignVerifyResponse {
3550
+ // Details of any error that occurred on the call.
3551
+ tdx.volt_api.volt.v1.Status status = 1;
3768
3552
 
3769
- // One of the following payloads will be present in a given InvokeRequest message.
3770
- oneof request_payload {
3771
- // A serialised and encrypted instance of \`RemoteResponse\` in pure binary format.
3772
- bytes payload = 6;
3553
+ oneof payload {
3554
+ // The signature in raw binary form.
3555
+ bytes digest = 2;
3773
3556
 
3774
- // A serialised and encrypted instance of \`RemoteResponse\` as serialised JSON.
3775
- bytes json_payload = 7;
3557
+ // The signature encoded using base64.
3558
+ string digest_encoded = 3;
3776
3559
  }
3560
+ }
3561
+ `;
3562
+ const wire_api = `syntax = "proto3";
3777
3563
 
3778
- // Indicates the client has ended the invocation.
3779
- bool client_end = 8;
3564
+ package tdx.volt_api.volt.v1;
3780
3565
 
3781
- // Reserved for internal use.
3782
- uint32 hop_index = 9;
3566
+ import "tdx/volt_api/volt/v1/status.proto";
3783
3567
 
3784
- // Reserved for internal use.
3785
- string target_service_id = 10;
3568
+ // The Wire API allows clients to subscribe and publish to Volt wire resources.
3569
+ service WireAPI {
3570
+ // Establishes a client-streaming call to the wire resource.
3571
+ rpc PublishWire(stream PublishWireRequest) returns (stream PublishWireResponse);
3572
+
3573
+ // Establishes a bi-directional streaming call to the wire resource.
3574
+ // Although we're only really interested in receiving data from the wire, a bi-directional stream is required so that we can gracefully stop the subscription.
3575
+ rpc SubscribeWire(stream SubscribeWireRequest) returns (stream SubscribeWireResponse);
3786
3576
  }
3787
3577
 
3788
- message InvokeResponse {
3789
- // The invocation id to match the originating request.
3790
- uint64 invoke_id = 1;
3578
+ // Because the publish RPC is streaming, the policy will not be checked until the first message arrives. This can mean that the \`PublishWire\` call appears to succeed but then fails after the first attempt to publish a message.
3579
+ // To avoid this, clients can immediately send a message with the \`wire_id\` set and no \`chunk\` set. This will establish that the correct permissions are in place and fail fast if not.
3580
+ message PublishWireRequest {
3581
+ // Only necessary in the first payload.
3582
+ string wire_id = 1;
3791
3583
 
3792
- InvokeRequestKeyExchange key_exchange = 2;
3584
+ // The chunk of data to publish.
3585
+ bytes chunk = 2;
3793
3586
 
3794
- // The initialisation vector for the response payload encryption. This will be a random 16 byte value, that is different for each response.
3795
- bytes iv = 4;
3587
+ // Whether to persist the chunk.
3588
+ // This is only valid if the wire is configured to persist messages, i.e. the \`volt:wire-persist\` attribute is true.
3589
+ bool do_not_persist = 3;
3590
+ }
3796
3591
 
3797
- // One of the following payloads will be present in a given InvokeResponse message.
3798
- oneof response_payload {
3799
- // A serialised and encrypted instance of \`RemoteRequest\` in pure binary format.
3800
- bytes payload = 5;
3592
+ message PublishWireResponse {
3593
+ // Details of any error that occurred on the call.
3594
+ tdx.volt_api.volt.v1.Status status = 1;
3595
+ }
3801
3596
 
3802
- // A serialised and encrypted instance of \`RemoteRequest\` as serialised JSON.
3803
- bytes json_payload = 6;
3597
+ // The request must include one of the following fields.
3598
+ message SubscribeWireRequest {
3599
+ oneof payload {
3600
+ // The wire id, only required for the first message.
3601
+ string wire_id = 1;
3602
+ // Request to stop the subscription.
3603
+ bool stop = 2;
3604
+ }
3605
+ }
3804
3606
 
3607
+ // One of the following fields will be present in the response.
3608
+ message SubscribeWireResponse {
3609
+ oneof payload {
3805
3610
  // Details of any error that occurred on the call.
3806
- tdx.volt_api.volt.v1.Status status = 7;
3807
- }
3611
+ tdx.volt_api.volt.v1.Status status = 1;
3808
3612
 
3809
- // Indicates the server has ended the invocation.
3810
- bool server_end = 8;
3613
+ // Data received from the wire.
3614
+ bytes chunk = 2;
3615
+ }
3811
3616
  }
3812
3617
 
3813
- message MoveResourceRequest {
3814
- // The id of the resource to move.
3815
- string resource_id = 1;
3618
+ `;
3619
+ const voltProtos = [sqlite, sqlite_database_api, sqlite_server_api, proto_db_sync, proxy_api, relay_api, discovery_api, file, file_api, remote, spark_api, ssi, ssi_api, status, sync_api, terminal_api, volt, volt_api, wire_api];
3816
3620
 
3817
- // The resource the parent folder to move the resource from.
3818
- string from_resource_id = 2;
3621
+ debug__default["default"]("volt-client-grpc:proto-utils");
3819
3622
 
3820
- // The target folder to move the resource into.
3821
- string to_resource_id = 3;
3822
- }
3623
+ const voltServices = [
3624
+ constants.serviceType.voltAPI,
3625
+ constants.serviceType.fileAPI,
3626
+ constants.serviceType.sqliteDatabaseAPI,
3627
+ constants.serviceType.sqliteServerAPI,
3628
+ constants.serviceType.ssiAPI,
3629
+ constants.serviceType.syncAPI,
3630
+ constants.serviceType.relayAPI,
3631
+ constants.serviceType.terminalAPI,
3632
+ constants.serviceType.wireAPI
3633
+ ];
3823
3634
 
3824
- message MoveResourceResponse {
3825
- // Details of any error that occurred on the call.
3826
- tdx.volt_api.volt.v1.Status status = 1;
3635
+ const defaultLoaderOptions = {
3636
+ keepCase: true,
3637
+ longs: String,
3638
+ enums: String,
3639
+ defaults: true,
3640
+ oneofs: true
3641
+ };
3642
+
3643
+ function getServiceDescriptors(service) {
3644
+ let root = new Protobuf__default["default"].Root();
3645
+ for (let protoFile of service.service_description.proto_file) {
3646
+ Protobuf__default["default"].parse(protoFile.protobuf, root, defaultLoaderOptions);
3647
+ }
3648
+
3649
+ // Create a package definition from the root object.
3650
+ const packageDefinition = createPackageDefinition(root, defaultLoaderOptions);
3651
+
3652
+ // Merge the service API definitions into a single object.
3653
+ let serviceDescriptors = {};
3654
+ for (let api of service.service_description.service_api) {
3655
+ const packageDescriptors = packageDefinition[api];
3656
+ serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
3657
+ }
3658
+
3659
+ return serviceDescriptors;
3827
3660
  }
3828
3661
 
3829
- message RequestAccessRequest {
3830
- // The target resource id.
3831
- string resource_id = 1;
3662
+ function getBuiltInServiceDescriptors() {
3663
+ // Create a placeholder service object for the Volt API service.
3664
+ const voltServicePlaceholder = {
3665
+ service_description: {
3666
+ proto_file: voltProtos.map((proto) => ({ protobuf: proto })),
3667
+ service_api: voltServices
3668
+ }
3669
+ };
3832
3670
 
3833
- // The type of access requested.
3834
- string access = 3;
3671
+ return getServiceDescriptors(voltServicePlaceholder);
3835
3672
  }
3836
3673
 
3837
- message RequestAccessResponse {
3838
- // Details of any error that occurred on the call.
3839
- tdx.volt_api.volt.v1.Status status = 1;
3674
+ var protoUtils = /*#__PURE__*/Object.freeze({
3675
+ __proto__: null,
3676
+ getBuiltInServiceDescriptors: getBuiltInServiceDescriptors,
3677
+ getServiceDescriptors: getServiceDescriptors
3678
+ });
3840
3679
 
3841
- // The resource being accessed.
3842
- string resource_id = 2;
3680
+ /* eslint-disable no-underscore-dangle */
3681
+
3682
+ const {
3683
+ aesCreateKey,
3684
+ aesEncrypt: aesEncrypt$1,
3685
+ aesDecrypt: aesDecrypt$1,
3686
+ toBase64,
3687
+ formatPEM: formatPEM$1,
3688
+ keyFromPem: keyFromPem$2,
3689
+ verify,
3690
+ deriveSharedKey: deriveSharedKey$1,
3691
+ x25519: x25519$1
3692
+ } = js.voltUtils;
3843
3693
 
3844
- // The identity attempting access.
3845
- string identity_did = 3;
3694
+ const invokeMethod = "Invoke";
3846
3695
 
3847
- // Requested access.
3848
- string access = 5;
3696
+ const log$5 = debug__default["default"]("volt-client-grpc:grpc-call");
3849
3697
 
3850
- // Assigned decision.
3851
- PolicyDecision decision = 6;
3698
+ function _prepareInvokeRequest(
3699
+ request,
3700
+ method,
3701
+ methodType = "METHOD_TYPE_UNARY"
3702
+ ) {
3703
+ const isServiceRelayed =
3704
+ this._service?.service_description.host_type ===
3705
+ "SERVICE_HOST_TYPE_RELAYED";
3852
3706
 
3853
- // Time at which the request was made.
3854
- int64 request_time = 7;
3707
+ const relaying = this._voltClient.isRelayed || isServiceRelayed;
3855
3708
 
3856
- // Time at which the decision was taken.
3857
- int64 decision_time = 8;
3709
+ let targetDID = [];
3710
+ if (this._voltClient.isRelayed) {
3711
+ targetDID.push(this._voltClient.voltConfig.id);
3712
+ }
3858
3713
 
3859
- // Counter of number times this access was requested.
3860
- int32 request_count = 9;
3861
- }
3714
+ if (isServiceRelayed) {
3715
+ // Add another relay hop if the target service is relayed.
3716
+ log$5("target service is relayed");
3717
+ targetDID.push(this._service.service_description.host_client_id);
3718
+ }
3862
3719
 
3863
- message ResourceAttributeQuery {
3864
- string attribute_id = 1;
3865
- AttributeDataType data_type = 2;
3866
- AttributeValue value = 3;
3867
- }
3720
+ log$5("target is %j", targetDID);
3868
3721
 
3869
- message SaveAccessRequest {
3870
- // Omit \`id\` if creating new access.
3871
- Access access = 1;
3872
- }
3722
+ // Use the service's host client id and public key if the service is relayed.
3723
+ const methodToken = this._voltClient.credential.getIdentityMetadata(
3724
+ this._voltClient.grpc,
3725
+ this._service?.service_description.host_client_id ||
3726
+ this._voltClient.voltConfig.id,
3727
+ this._service?.service_description.host_public_key ||
3728
+ this._voltClient.credential.voltPublicKey,
3729
+ relaying
3730
+ );
3873
3731
 
3874
- message SaveAccessResponse {
3875
- // Details of any error that occurred on the call.
3876
- tdx.volt_api.volt.v1.Status status = 1;
3877
- }
3732
+ let invokeRequest;
3733
+ let callMethod = method;
3878
3734
 
3879
- message SaveIdentityRequest {
3880
- // Details of the identity to save.
3881
- Identity identity = 1;
3735
+ const voltGrpcClient = this._voltClient.getVoltAPIClient();
3882
3736
 
3883
- // Set to indicate this is a new identity.
3884
- bool create = 2;
3737
+ if (relaying) {
3738
+ //
3739
+ // If the volt connection is via a relay (or the target service is relayed),
3740
+ // we wrap the rpc in a RemoteRequest message and send it via a call to Invoke().
3741
+ //
3885
3742
 
3886
- // The list of aliases that should be removed.
3887
- // For example, this allows a simple form of key rotation whereby an existing public key alias is replaced by a new one while still maintaining the same root identity id.
3888
- repeated IdentityAlias delete_alias = 3;
3743
+ // Replace the target method with a call to Invoke on the relay Volt.
3744
+ callMethod = invokeMethod;
3889
3745
 
3890
- // Reserved for system use.
3891
- string create_in_parent_id = 4;
3746
+ // But first we need to perform a key exchange with the target service.
3747
+ if (!this._encryptionKey) {
3748
+ // We haven't exchanged keys yet, so queue the payload.
3749
+ if (request) {
3750
+ this._pendingRequests.push(request);
3751
+ }
3892
3752
 
3893
- // Set to indicate the identity aliases should be purged before saving the identity.
3894
- // If the \`identity\` field contains aliases they will be saved after the purge.
3895
- // If the \`identity\` field does not contain aliases this effectively deletes all aliases for this identity.
3896
- // This allows you to selectively update aliases if required, i.e. don't set this flag and include a single alias in the update.
3897
- bool purge_aliases = 5;
3753
+ if (this._keyExchangePending) {
3754
+ // We've already sent the key exchange request, so do nothing.
3755
+ log$5("key exchange pending, not sending payload");
3756
+ return;
3757
+ }
3898
3758
 
3899
- string did_document = 6;
3759
+ this._keyExchangePending = true;
3900
3760
 
3901
- // The signature of the identity did document, if present.
3902
- string did_update_signature = 7;
3903
- }
3761
+ // Send the key exchange request.
3762
+ invokeRequest = {
3763
+ token: methodToken.token,
3764
+ target_did: targetDID,
3765
+ target_service_id: isServiceRelayed
3766
+ ? this._service.service_description.host_service_id
3767
+ : undefined
3768
+ };
3904
3769
 
3905
- message SaveIdentityResponse {
3906
- // Details of any error that occurred on the call.
3907
- tdx.volt_api.volt.v1.Status status = 1;
3770
+ if (this._voltClient.isRelayed) {
3771
+ // When tunnelling, we serialise the request using the original serialisation
3772
+ // method for Invoke.
3773
+ invokeRequest =
3774
+ voltGrpcClient.Invoke.requestSerializeOriginal(invokeRequest);
3775
+ }
3908
3776
 
3909
- // The updated identity details.
3910
- Identity identity = 2;
3911
- }
3777
+ return {
3778
+ request: invokeRequest,
3779
+ method: callMethod,
3780
+ methodName: method,
3781
+ meta: methodToken
3782
+ };
3783
+ }
3912
3784
 
3913
- message SaveParametersRequest {
3914
- // The updated parameters.
3915
- VoltParameters parameters = 1;
3785
+ let requestPayload;
3916
3786
 
3917
- // The current root key passphrase. Only necessary if changes are being made to the Volt key.
3918
- string key_passphrase = 2;
3787
+ // Serialise the rpc request using the original serialisation method.
3788
+ if (request) {
3789
+ if (this._voltClient.isRelayed) {
3790
+ requestPayload =
3791
+ this._grpcClient[method].requestSerializeOriginal(request);
3792
+ } else {
3793
+ requestPayload = this._grpcClient[method].requestSerialize(request);
3794
+ }
3795
+ } else if (request !== null) {
3796
+ log$5("****************LOOKOUT****************** - empty request");
3797
+ } else {
3798
+ log$5("explicit empty request payload");
3799
+ }
3919
3800
 
3920
- // The new root key passphrase. Only necessary if changes are being made to the Volt key.
3921
- string new_key_passphrase = 3;
3922
- }
3801
+ // Create a tunnel message containing the method call metadata and request payload.
3802
+ const tunnelResp = voltGrpcClient.Tunnel.responseSerialize({
3803
+ method_invoke: {
3804
+ method_name: this._grpcClient[method].path,
3805
+ method_type: methodType,
3806
+ request: requestPayload
3807
+ }
3808
+ });
3923
3809
 
3924
- message SaveParametersResponse {
3925
- // Details of any errors that occurred on the call.
3926
- tdx.volt_api.volt.v1.Status status = 1;
3810
+ // When sending via a relay we need to encrypt the payload.
3811
+ const iv = aesCreateKey().iv;
3812
+ const invokePayload = aesEncrypt$1(this._encryptionKey, iv, tunnelResp);
3927
3813
 
3928
- // The updated Volt parameters.
3929
- VoltParameters parameters = 2;
3814
+ // Construct the Invoke request.
3815
+ invokeRequest = {
3816
+ iv,
3817
+ payload: invokePayload
3818
+ };
3930
3819
 
3931
- // If set, the client will need to reconnect (usually because the key has changed).
3932
- bool reconnect = 3;
3933
- }
3820
+ if (this._voltClient.isRelayed) {
3821
+ // When tunnelling, we serialise the request using the original serialisation
3822
+ // method for Invoke.
3823
+ invokeRequest =
3824
+ voltGrpcClient.Invoke.requestSerializeOriginal(invokeRequest);
3825
+ }
3826
+ } else {
3827
+ invokeRequest = request;
3828
+ }
3934
3829
 
3935
- message SaveResourceRequest {
3936
- // Details of the resource to save.
3937
- Resource resource = 1;
3830
+ return {
3831
+ meta: methodToken,
3832
+ method: callMethod,
3833
+ methodName: method,
3834
+ request: invokeRequest
3835
+ };
3836
+ }
3938
3837
 
3939
- // Set to indicate this is a new resource.
3940
- bool create = 2;
3838
+ function _preparePayloadRequest(request, invokeInfo) {
3839
+ let payloadRequest;
3941
3840
 
3942
- // The id of the folder resource in which a new resource should be created.
3943
- // If omitted, the home folder of the currently authenticated identity will be used.
3944
- string create_in_parent_id = 3;
3841
+ const isServiceRelayed =
3842
+ this._service?.service_description.host_type ===
3843
+ "SERVICE_HOST_TYPE_RELAYED";
3945
3844
 
3946
- // Set to indicate the resource attributes should be purged before saving the resource.
3947
- // If the \`resource\` field contains attributes they will be saved after the purge.
3948
- // If the \`resource\` field does not contain attributes this effectively deletes all attributes for this resource.
3949
- // This allows you to selectively update attributes if required, i.e. don't set this flag and include a single attribute in the update.
3950
- bool purge_attributes = 4;
3951
- }
3845
+ if (this._voltClient.isRelayed || isServiceRelayed) {
3846
+ let requestPayload;
3847
+ if (this._voltClient.isRelayed) {
3848
+ // Serialise the rpc request using the original serialisation method.
3849
+ requestPayload =
3850
+ this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(
3851
+ request
3852
+ );
3853
+ } else {
3854
+ requestPayload =
3855
+ this._grpcClient[invokeInfo.methodName].requestSerialize(request);
3856
+ }
3952
3857
 
3953
- message SaveResourceResponse {
3954
- // Details of any error that occurred on the call.
3955
- tdx.volt_api.volt.v1.Status status = 1;
3858
+ // Use the Tunnel method to serialise the relay request wrapper, this
3859
+ const voltGrpcClient = this._voltClient.getVoltAPIClient();
3956
3860
 
3957
- // The updated resource.
3958
- Resource resource = 2;
3959
- }
3861
+ // Create a tunnel message containing the method call metadata and request payload.
3862
+ const tunnelResp = voltGrpcClient.Tunnel.responseSerialize({
3863
+ method_payload: {
3864
+ payload: requestPayload
3865
+ }
3866
+ });
3960
3867
 
3961
- message SaveSessionRequest {
3962
- Session session = 1;
3963
- }
3868
+ // Encrypt the payload using the shared key.
3869
+ const iv = aesCreateKey().iv;
3870
+ const invokePayload = aesEncrypt$1(this._encryptionKey, iv, tunnelResp);
3964
3871
 
3965
- message SaveSessionResponse {
3966
- // Details of any error that occurred on the call.
3967
- tdx.volt_api.volt.v1.Status status = 1;
3872
+ // We then serialise the entire request using the original serialisation method for Invoke.
3873
+ payloadRequest = {
3874
+ iv,
3875
+ payload: invokePayload
3876
+ };
3968
3877
 
3969
- Session session = 2;
3970
- }
3878
+ if (this._voltClient.isRelayed) {
3879
+ // When tunnelling, we serialise the request using the original serialisation
3880
+ // method for Invoke.
3881
+ payloadRequest =
3882
+ voltGrpcClient.Invoke.requestSerializeOriginal(payloadRequest);
3883
+ }
3884
+ } else {
3885
+ payloadRequest = request;
3886
+ }
3971
3887
 
3972
- message SetPolicyRequest {
3973
- string custom_policy = 1;
3888
+ return payloadRequest;
3974
3889
  }
3975
3890
 
3976
- message SetPolicyResponse {
3977
- // Details of any error that occurred on the call.
3978
- tdx.volt_api.volt.v1.Status status = 1;
3979
- }
3891
+ function _parseResponse(method, meta, response) {
3892
+ // log("parseResponse for %s", this._methodName);
3893
+ let invokeResponse = {};
3980
3894
 
3981
- message SetServiceStatusRequest {
3982
- // The service description details.
3983
- Resource service = 2;
3984
- }
3895
+ const isServiceRelayed =
3896
+ this._service?.service_description.host_type ===
3897
+ "SERVICE_HOST_TYPE_RELAYED";
3985
3898
 
3986
- message SetServiceStatusResponse {
3987
- // Details of any error that occurred on the call.
3988
- tdx.volt_api.volt.v1.Status status = 1;
3899
+ const voltGrpcClient = this._voltClient.getVoltAPIClient();
3989
3900
 
3990
- // The updated service resource details.
3991
- Resource resource = 2;
3992
- }
3901
+ if (this._voltClient.isRelayed || isServiceRelayed) {
3902
+ if (this._voltClient.isRelayed) {
3903
+ // Deserialise the incoming response using the original deserialisation method for Invoke.
3904
+ response = voltGrpcClient.Invoke.responseDeserializeOriginal(response);
3905
+ }
3993
3906
 
3994
- message SetAccessRequestDecisionRequest {
3995
- // The id of the access request.
3996
- string id = 1;
3907
+ if (response.key_exchange) {
3908
+ const encryptionKey = response.key_exchange.encryption_key;
3909
+ const nonce = response.key_exchange.nonce;
3910
+ const signature = response.key_exchange.signature;
3997
3911
 
3998
- // The decision to save against the access request.
3999
- PolicyDecision decision = 2;
4000
- }
3912
+ // The signed message is the encryption key and the nonce.
3913
+ const message = new Uint8Array(encryptionKey.length + nonce.length);
3914
+ message.set(encryptionKey);
3915
+ message.set(nonce, encryptionKey.length);
4001
3916
 
4002
- message SetAccessRequestDecisionResponse {
4003
- // Details of any error that occurred on the call.
4004
- tdx.volt_api.volt.v1.Status status = 1;
4005
- }
3917
+ // Check the signature using the target service public key.
3918
+ const targetKey = keyFromPem$2(
3919
+ this._service?.service_description?.host_public_key ||
3920
+ this._voltClient.credential.voltPublicKey
3921
+ ).publicKey;
4006
3922
 
4007
- // This message is emtpy.
4008
- message ShutdownRequest {
4009
- }
3923
+ if (!verify(targetKey, message, signature)) {
3924
+ throw new Error("signature verification failed");
3925
+ }
4010
3926
 
4011
- message ShutdownResponse {
4012
- // Details of any error that occurred on the call.
4013
- tdx.volt_api.volt.v1.Status status = 1;
4014
- }
3927
+ invokeResponse = {
3928
+ key_exchange: {
3929
+ encryption_key: formatPEM$1(toBase64(encryptionKey), "PUBLIC KEY")
3930
+ }
3931
+ };
3932
+ } else if (response.payload) {
3933
+ let decryptedPayload;
3934
+ if (this._encryptionKey) {
3935
+ decryptedPayload = aesDecrypt$1(
3936
+ this._encryptionKey,
3937
+ response.iv,
3938
+ response.payload
3939
+ );
3940
+ } else {
3941
+ decryptedPayload = response.payload;
3942
+ }
4015
3943
 
4016
- message SignVerifyRequest {
4017
- // Set to indicate this is a request to verify rather than sign.
4018
- bool verify = 1;
3944
+ // Deserialise the decrypted payload using the Tunnel deserialisation method.
3945
+ const responsePayload =
3946
+ voltGrpcClient.Tunnel.requestDeserialize(decryptedPayload);
3947
+ if (responsePayload.payload === "method_payload") {
3948
+ try {
3949
+ // Deserialise the method payload.
3950
+ if (this._voltClient.isRelayed) {
3951
+ // Use the original deserialisation method for the target method.
3952
+ invokeResponse.payload = this._grpcClient[
3953
+ method
3954
+ ].responseDeserializeOriginal(
3955
+ responsePayload.method_payload.payload
3956
+ );
3957
+ } else {
3958
+ invokeResponse.payload = this._grpcClient[
3959
+ method
3960
+ ].responseDeserialize(responsePayload.method_payload.payload);
3961
+ }
3962
+ } catch (err) {
3963
+ log$5("failure deserialising payload for %s", this._methodName);
3964
+ throw new Error(
3965
+ "failure deserialising payload - check protobuf definition matches with data being sent"
3966
+ );
3967
+ }
3968
+ } else if (responsePayload.payload === "method_end") {
3969
+ invokeResponse = responsePayload.method_end;
3970
+ } else {
3971
+ log$5("unexpected tunnel payload type: %s", responsePayload.payload);
3972
+ }
3973
+ } else if (response.status?.message) {
3974
+ // There's been an error in the tunnel.
3975
+ log$5(
3976
+ "Tunnel status received: %s, code %d, description: %s",
3977
+ response.status.message,
3978
+ response.status.code,
3979
+ response.status.description || "n/a"
3980
+ );
3981
+ invokeResponse = {
3982
+ ended: true,
3983
+ error: `Error in tunnel: ${response.status.message}`
3984
+ };
3985
+ } else {
3986
+ invokeResponse.methodId = response.invoke_id;
3987
+ log$5("started method %d for %s", invokeResponse.methodId, method);
3988
+ }
3989
+ } else {
3990
+ invokeResponse = { payload: response };
3991
+ }
4019
3992
 
4020
- // Set to indicate the signature should be base64 encoded in the response.
4021
- // Only valid when signing.
4022
- bool encode = 2;
3993
+ return invokeResponse;
3994
+ }
4023
3995
 
4024
- // The message to sign.
4025
- // Only valid when signing.
4026
- string message = 3;
3996
+ class GRPCCall extends EventEmitter__default["default"] {
3997
+ constructor(voltClient, methodName, methodType, service = undefined) {
3998
+ super();
3999
+ this._methodName = methodName;
4000
+ this._methodType = methodType;
4001
+ this._call = null;
4002
+ this._voltClient = voltClient;
4003
+ this._service = service;
4004
+ this._keyExchangePending = false;
4005
+ this._pendingRequests = [];
4006
+ this._encryptionKey = null;
4007
+ this._ended = false;
4008
+ }
4027
4009
 
4028
- oneof digest {
4029
- // The digest in raw binary form.
4030
- // Only valid if verifying.
4031
- bytes digest_raw = 4;
4010
+ start(grpcClient, request, unary = false) {
4011
+ this._grpcClient = grpcClient;
4032
4012
 
4033
- // The digest encoded using base64.
4034
- // Only valid if verifying.
4035
- string digest_encoded = 5;
4036
- }
4013
+ if (typeof this._grpcClient[this._methodName] !== "function") {
4014
+ throw new Error(`method not found: '${this._methodName}'`);
4015
+ }
4037
4016
 
4038
- // The DID of the identity used to sign or verify the message. If omitted, the authenticated identity will be used.
4039
- // When signing, the authenticated identity must have volt:sign permission for the given DID.
4040
- string identity_did = 6;
4041
- }
4017
+ this._initialRequest = _prepareInvokeRequest.call(
4018
+ this,
4019
+ request,
4020
+ this._methodName,
4021
+ this._methodType
4022
+ );
4042
4023
 
4043
- // Note that if verification was successful the response will be empty (there is no error and no digest is returned).
4044
- message SignVerifyResponse {
4045
- // Details of any error that occurred on the call.
4046
- tdx.volt_api.volt.v1.Status status = 1;
4024
+ const isServiceRelayed =
4025
+ this._service?.service_description.host_type ===
4026
+ "SERVICE_HOST_TYPE_RELAYED";
4047
4027
 
4048
- oneof payload {
4049
- // The signature in raw binary form.
4050
- bytes digest = 2;
4028
+ let callClient;
4029
+ if (this._voltClient.isRelayed || isServiceRelayed) {
4030
+ callClient = this._voltClient.getVoltAPIClient();
4031
+ } else {
4032
+ callClient = this._grpcClient;
4033
+ }
4051
4034
 
4052
- // The signature encoded using base64.
4053
- string digest_encoded = 3;
4054
- }
4055
- }
4056
- `;
4057
- const wire_api = `syntax = "proto3";
4035
+ this._call = callClient[this._initialRequest.method](
4036
+ this._initialRequest?.meta?.metadata
4037
+ );
4058
4038
 
4059
- package tdx.volt_api.volt.v1;
4039
+ this._call.on("data", (streamResponse) => {
4040
+ if (this._ended) {
4041
+ log$5("ignoring data after end on %s", this._methodName);
4042
+ return;
4043
+ }
4060
4044
 
4061
- import "tdx/volt_api/volt/v1/status.proto";
4045
+ try {
4046
+ const parsedResponse = _parseResponse.call(
4047
+ this,
4048
+ this._methodName,
4049
+ this._initialRequest.meta,
4050
+ streamResponse
4051
+ );
4052
+ if (parsedResponse.key_exchange) {
4053
+ const peerKey = keyFromPem$2(
4054
+ parsedResponse.key_exchange.encryption_key
4055
+ ).publicKey;
4062
4056
 
4063
- // The Wire API allows clients to subscribe and publish to Volt wire resources.
4064
- service WireAPI {
4065
- // Establishes a client-streaming call to the wire resource.
4066
- rpc PublishWire(stream PublishWireRequest) returns (stream PublishWireResponse);
4057
+ this._encryptionKey = deriveSharedKey$1(
4058
+ this._initialRequest.meta.sharedKey.key,
4059
+ peerKey,
4060
+ x25519$1
4061
+ );
4067
4062
 
4068
- // Establishes a bi-directional streaming call to the wire resource.
4069
- // Although we're only really interested in receiving data from the wire, a bi-directional stream is required so that we can gracefully stop the subscription.
4070
- rpc SubscribeWire(stream SubscribeWireRequest) returns (stream SubscribeWireResponse);
4071
- }
4063
+ // We've exchanged keys, so send any queued payloads on the next tick.
4064
+ process.nextTick(() => {
4065
+ this._pendingRequests.forEach((pendingRequest) => {
4066
+ this.write(pendingRequest);
4067
+ });
4068
+ this._pendingRequests = [];
4069
+ });
4070
+ } else if (parsedResponse.payload) {
4071
+ // Interpret a non-empty status message as an error.
4072
+ if (parsedResponse.payload?.status?.message) {
4073
+ this.emit(
4074
+ "error",
4075
+ new Error(parsedResponse.payload.status.message)
4076
+ );
4077
+ } else {
4078
+ this.emit("data", parsedResponse.payload);
4079
+ }
4080
+ } else if (parsedResponse.ended) {
4081
+ if (parsedResponse.error) {
4082
+ this.emit("error", new Error(parsedResponse.error));
4083
+ } else {
4084
+ // Not sure this is necessary - we should receive "end" from grpc soon...
4085
+ this.emit("end");
4086
+ }
4087
+ } else {
4088
+ log$5("method id is %s", parsedResponse.methodId);
4089
+ }
4090
+ } catch (err) {
4091
+ log$5(
4092
+ "failure processing %s call response: %s",
4093
+ this._methodName,
4094
+ err.message
4095
+ );
4096
+ this.emit("error", err);
4097
+ }
4098
+ });
4072
4099
 
4073
- // Because the publish RPC is streaming, the policy will not be checked until the first message arrives. This can mean that the \`PublishWire\` call appears to succeed but then fails after the first attempt to publish a message.
4074
- // To avoid this, clients can immediately send a message with the \`wire_id\` set and no \`chunk\` set. This will establish that the correct permissions are in place and fail fast if not.
4075
- message PublishWireRequest {
4076
- // Only necessary in the first payload.
4077
- string wire_id = 1;
4100
+ this._call.on("error", (err) => {
4101
+ log$5(
4102
+ "ERROR - intercepted stream error on %s: %s",
4103
+ this._methodName,
4104
+ err.message
4105
+ );
4106
+ this.emit("error", err);
4107
+ });
4078
4108
 
4079
- // The chunk of data to publish.
4080
- bytes chunk = 2;
4109
+ this._call.on("finish", () => {
4110
+ // The read side has ended (i.e. we called end()).
4111
+ log$5("finished call %s", this._methodName);
4112
+ this.emit("finish");
4113
+ });
4081
4114
 
4082
- // Whether to persist the chunk.
4083
- // This is only valid if the wire is configured to persist messages, i.e. the \`volt:wire-persist\` attribute is true.
4084
- bool do_not_persist = 3;
4085
- }
4115
+ this._call.on("end", () => {
4116
+ // The remote peer ended the stream.
4117
+ log$5(
4118
+ "ended call %s, method id: %s",
4119
+ this._methodName,
4120
+ this._initialRequest.methodId || "n/a - not tunnelling"
4121
+ );
4122
+ this.emit("end");
4123
+ });
4086
4124
 
4087
- message PublishWireResponse {
4088
- // Details of any error that occurred on the call.
4089
- tdx.volt_api.volt.v1.Status status = 1;
4090
- }
4125
+ if (this._initialRequest.request) {
4126
+ if (unary) {
4127
+ this._call.end(this._initialRequest.request);
4128
+ } else {
4129
+ // Allow callee to attach event handlers before we actually send the initial payload.
4130
+ process.nextTick(() => this._call.write(this._initialRequest.request));
4131
+ }
4132
+ }
4091
4133
 
4092
- // The request must include one of the following fields.
4093
- message SubscribeWireRequest {
4094
- oneof payload {
4095
- // The wire id, only required for the first message.
4096
- string wire_id = 1;
4097
- // Request to stop the subscription.
4098
- bool stop = 2;
4134
+ return this;
4099
4135
  }
4100
- }
4101
-
4102
- // One of the following fields will be present in the response.
4103
- message SubscribeWireResponse {
4104
- oneof payload {
4105
- // Details of any error that occurred on the call.
4106
- tdx.volt_api.volt.v1.Status status = 1;
4107
4136
 
4108
- // Data received from the wire.
4109
- bytes chunk = 2;
4137
+ end() {
4138
+ if (this._call) {
4139
+ this._ended = true;
4140
+ return this._call.end();
4141
+ } else {
4142
+ throw new Error("call not initialised");
4143
+ }
4110
4144
  }
4111
- }
4112
-
4113
- `;
4114
- const voltProtos = [sqlite, sqlite_database_api, sqlite_server_api, proto_db_sync, proxy_api, relay_api, discovery_api, file, file_api, remote, spark_api, ssi, ssi_api, status, sync_api, terminal_api, volt, volt_api, wire_api];
4115
-
4116
- debug__default["default"]("volt-client-grpc:proto-utils");
4117
-
4118
- const voltServices = [
4119
- constants.serviceType.voltAPI,
4120
- constants.serviceType.fileAPI,
4121
- constants.serviceType.sqliteDatabaseAPI,
4122
- constants.serviceType.sqliteServerAPI,
4123
- constants.serviceType.ssiAPI,
4124
- constants.serviceType.syncAPI,
4125
- constants.serviceType.relayAPI,
4126
- constants.serviceType.terminalAPI,
4127
- constants.serviceType.wireAPI
4128
- ];
4129
4145
 
4130
- const defaultLoaderOptions = {
4131
- keepCase: true,
4132
- longs: String,
4133
- enums: String,
4134
- defaults: true,
4135
- oneofs: true
4136
- };
4137
-
4138
- function getServiceDescriptors(service) {
4139
- let root = new Protobuf__default["default"].Root();
4140
- for (let protoFile of service.service_description.proto_file) {
4141
- Protobuf__default["default"].parse(protoFile.protobuf, root, defaultLoaderOptions);
4146
+ pause() {
4147
+ log$5("pausing call %s", this._methodName);
4148
+ return this._call?.pause();
4142
4149
  }
4143
4150
 
4144
- // Create a package definition from the root object.
4145
- const packageDefinition = createPackageDefinition(root, defaultLoaderOptions);
4151
+ resume() {
4152
+ log$5("resuming call %s", this._methodName);
4153
+ return this._call?.resume();
4154
+ }
4146
4155
 
4147
- // Merge the service API definitions into a single object.
4148
- let serviceDescriptors = {};
4149
- for (let api of service.service_description.service_api) {
4150
- const packageDescriptors = packageDefinition[api];
4151
- serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
4156
+ get writable() {
4157
+ return this._call?.writable;
4152
4158
  }
4153
4159
 
4154
- return serviceDescriptors;
4155
- }
4160
+ write(request) {
4161
+ if (!this._call) {
4162
+ log$5("ERROR - call not initialised");
4163
+ throw new Error("call not initialised");
4164
+ } else if (this._initialRequest && !this._keyExchangePending) {
4165
+ const payloadRequest = _preparePayloadRequest.call(
4166
+ this,
4167
+ request,
4168
+ this._initialRequest
4169
+ );
4170
+ return this._call.write(payloadRequest);
4171
+ } else {
4172
+ this._keyExchangePending = false;
4156
4173
 
4157
- function getBuiltInServiceDescriptors() {
4158
- // Create a placeholder service object for the Volt API service.
4159
- const voltServicePlaceholder = {
4160
- service_description: {
4161
- proto_file: voltProtos.map((proto) => ({ protobuf: proto })),
4162
- service_api: voltServices
4163
- }
4164
- };
4174
+ this._initialRequest = _prepareInvokeRequest.call(
4175
+ this,
4176
+ request,
4177
+ this._methodName,
4178
+ this._methodType
4179
+ );
4165
4180
 
4166
- return getServiceDescriptors(voltServicePlaceholder);
4181
+ return this._call.write(this._initialRequest.request);
4182
+ }
4183
+ }
4167
4184
  }
4168
4185
 
4169
- var protoUtils = /*#__PURE__*/Object.freeze({
4170
- __proto__: null,
4171
- getBuiltInServiceDescriptors: getBuiltInServiceDescriptors,
4172
- getServiceDescriptors: getServiceDescriptors
4173
- });
4174
-
4175
4186
  const {
4176
4187
  aesDecrypt,
4177
4188
  aesEncrypt,
@@ -4821,30 +4832,6 @@ async function authenticateInternal(
4821
4832
  ownDID = false
4822
4833
  ) {
4823
4834
  try {
4824
- // Check if we need to resolve a Relay volt.
4825
- if (
4826
- typeof this._voltConfig?.relay === "string" ||
4827
- this._voltConfig?.relay?.http_address
4828
- ) {
4829
- const relayURL =
4830
- this._voltConfig.relay?.http_address || this._voltConfig.relay;
4831
- log$2("attempting to retrieve Relay information from %s", relayURL);
4832
- this._voltConfig.relay = await fetchVoltConfig.call(
4833
- this,
4834
- `${relayURL}/discover`
4835
- );
4836
-
4837
- if (this._voltConfig.relay.ca_pem) {
4838
- log$2(
4839
- "Auto-fetched Relay CA %s, remote address %s",
4840
- this._voltConfig.relay.ca_pem,
4841
- this._voltConfig.relay.address
4842
- );
4843
- } else {
4844
- throw new Error("Unable to fetch Relay CA - cannot securely connect.");
4845
- }
4846
- }
4847
-
4848
4835
  if (!this._credential.cache.ca) {
4849
4836
  this._credential.cache.ca = this._voltConfig.ca_pem;
4850
4837
  }
@@ -4874,10 +4861,10 @@ async function authenticateInternal(
4874
4861
  host: this._credential.cache.bindIp
4875
4862
  };
4876
4863
 
4877
- if (this._voltConfig.challenge_code) {
4864
+ if (this._credential.cache.challenge_code) {
4878
4865
  authenticateRequest.challenge = signBase64(
4879
4866
  this._credential.cache.key,
4880
- this._voltConfig.challenge_code
4867
+ this._credential.cache.challenge_code
4881
4868
  );
4882
4869
  } else {
4883
4870
  log$2(
@@ -5174,11 +5161,18 @@ function streamingCallInternal(methodType, method, request, service) {
5174
5161
 
5175
5162
  async function fetchVoltConfig(discovery_url) {
5176
5163
  try {
5177
- const getJSON = bent__default["default"]("json");
5178
- const fullVoltConfig = await getJSON(discovery_url);
5164
+ const response = await fetch(discovery_url);
5165
+
5166
+ if (!response.ok) {
5167
+ log$2("fetchVoltConfig - error [%s]", response.status);
5168
+ return null;
5169
+ }
5170
+
5171
+ const fullVoltConfig = await response.json();
5179
5172
 
5180
5173
  if (!fullVoltConfig || typeof fullVoltConfig !== "object") {
5181
- throw new Error(`invalid config resolved from: ${discovery_url}`);
5174
+ log$2("fetchVoltConfig - invalid config resolved from: %s", discovery_url);
5175
+ return null;
5182
5176
  }
5183
5177
 
5184
5178
  const voltConfig = pick(
@@ -5190,16 +5184,15 @@ async function fetchVoltConfig(discovery_url) {
5190
5184
  "address",
5191
5185
  "http_address",
5192
5186
  "ca_pem",
5193
- "challenge_code",
5194
- "cloud",
5195
5187
  "relay"
5196
5188
  );
5197
5189
 
5190
+ log$2("fetchVoltConfig from %s", discovery_url);
5191
+
5198
5192
  return voltConfig;
5199
5193
  } catch (err) {
5200
- throw new Error(
5201
- `Failure loading config from ${discovery_url}: ${err.message}`
5202
- );
5194
+ log$2("fetchVoltConfig - %s error [%s]", discovery_url, err.message);
5195
+ return null;
5203
5196
  }
5204
5197
  }
5205
5198
 
@@ -5210,8 +5203,13 @@ async function fetchVoltConfigFromDIDInternal(volt_did, registryUrl) {
5210
5203
  throw new Error(`Invalid Volt DID: ${volt_did}`);
5211
5204
  }
5212
5205
 
5213
- const getJSON = bent__default["default"]("json");
5214
- const didDocument = await getJSON(didResolution);
5206
+ const response = await fetch(didResolution);
5207
+
5208
+ if (!response.ok) {
5209
+ throw new Error(`HTTP error, status: ${response.status}`);
5210
+ }
5211
+
5212
+ const didDocument = await response.json();
5215
5213
  const voltConfigServices = findDIDDocumentService(
5216
5214
  didDocument,
5217
5215
  constants.didServiceType.voltConfig
@@ -5227,7 +5225,12 @@ async function fetchVoltConfigFromDIDInternal(volt_did, registryUrl) {
5227
5225
  log$2("resolved DID from %s", didResolution);
5228
5226
 
5229
5227
  const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
5230
- return await fetchVoltConfig.call(this, serviceEndpoint);
5228
+ const voltConfig = await fetchVoltConfig.call(this, serviceEndpoint);
5229
+ if (!voltConfig) {
5230
+ throw new Error(`failed to resolve Volt config from ${serviceEndpoint}`);
5231
+ }
5232
+
5233
+ return voltConfig;
5231
5234
  } catch (err) {
5232
5235
  throw new Error(
5233
5236
  `Failure fetching Volt config from DID document ${volt_did}: ${err.message}`
@@ -5235,23 +5238,130 @@ async function fetchVoltConfigFromDIDInternal(volt_did, registryUrl) {
5235
5238
  }
5236
5239
  }
5237
5240
 
5238
- async function fetchVoltConfigFromDID(
5239
- volt_did,
5240
- registryList = [constants.defaultDIDHostName]
5241
- ) {
5241
+ async function fetchVoltConfigFromDID(volt_did, registryListIn) {
5242
+ let registryList;
5243
+ if (!registryListIn) {
5244
+ // If no registry list is provided, use the default registry list.
5245
+ registryList = constants.defaultRegistryList;
5246
+ } else {
5247
+ // If a registry list is provided, merge it with the default registry list.
5248
+ registryList = Array.from(
5249
+ new Set(registryListIn.concat(constants.defaultRegistryList))
5250
+ );
5251
+ }
5252
+
5253
+ let voltConfig;
5242
5254
  for (let registryUrl of registryList) {
5243
5255
  try {
5244
- return await fetchVoltConfigFromDIDInternal.call(
5256
+ voltConfig = await fetchVoltConfigFromDIDInternal.call(
5245
5257
  this,
5246
5258
  volt_did,
5247
5259
  registryUrl
5248
5260
  );
5261
+
5262
+ // If we've found a config, break out of the loop.
5263
+ break;
5264
+ } catch (err) {
5265
+ log$2("unable to fetch Volt config from %s: %s", registryUrl, err.message);
5266
+ }
5267
+ }
5268
+
5269
+ if (!voltConfig) {
5270
+ log$2("failed to fetch Volt config for %s from any registry", volt_did);
5271
+ }
5272
+
5273
+ return voltConfig;
5274
+ }
5275
+
5276
+ async function resolveVoltConfig(cfg, didRegistryList) {
5277
+ // Start by resolving the relay if one is specified.
5278
+ let relay = cfg?.relay?.http_address || cfg?.relay;
5279
+ if (relay) {
5280
+ relay = await resolveVoltConfig.call(this, relay, didRegistryList);
5281
+ }
5282
+
5283
+ let voltDID = "";
5284
+ let voltHttpAddress = "";
5285
+
5286
+ if (typeof cfg === "string") {
5287
+ // If the configuration is a string, determine if it is a DID or HTTP address.
5288
+ if (cfg.indexOf("did:") === 0) {
5289
+ voltDID = cfg;
5290
+ } else {
5291
+ voltHttpAddress = cfg;
5292
+ }
5293
+ cfg = {};
5294
+ } else if (typeof cfg === "object") {
5295
+ voltDID = cfg.id;
5296
+ voltHttpAddress = cfg.http_address;
5297
+ } else {
5298
+ throw new Error("configuration is missing the 'volt' object");
5299
+ }
5300
+
5301
+ let resolvedConfig;
5302
+
5303
+ if (relay) {
5304
+ if (!voltDID) {
5305
+ throw new Error("relay specified but no Volt DID provided");
5306
+ }
5307
+
5308
+ // Build the sub-domain URL for the Volt discovery service by inserting the volt DID into the relay URL.
5309
+ const relayURL = relay.http_address;
5310
+ const relayParts = relayURL.split("://");
5311
+ const subdomain = voltDID.substring(constants.voltDIDPrefix.length);
5312
+ voltHttpAddress = `${relayParts[0]}://${subdomain}.${relayParts[1]}`;
5313
+ } else if (voltDID) {
5314
+ resolvedConfig = await fetchVoltConfigFromDID.call(
5315
+ this,
5316
+ voltDID,
5317
+ didRegistryList
5318
+ );
5319
+ }
5320
+
5321
+ if (!resolvedConfig && voltHttpAddress) {
5322
+ try {
5323
+ resolvedConfig = await fetchVoltConfig.call(
5324
+ this,
5325
+ `${voltHttpAddress}/discover`
5326
+ );
5327
+
5328
+ // Update the http address in the resolved config to the one we used to
5329
+ // fetch it, since we may have used the relay rather than local address.
5330
+ resolvedConfig.http_address = voltHttpAddress;
5249
5331
  } catch (err) {
5250
- log$2("Failed to fetch Volt config from %s: %s", registryUrl, err.message);
5332
+ log$2(
5333
+ "failed to resolve Volt configuration %s [%s]",
5334
+ voltHttpAddress,
5335
+ err.message
5336
+ );
5337
+ }
5338
+ }
5339
+
5340
+ if (!resolvedConfig) {
5341
+ if (cfg.address && cfg.ca_pem) {
5342
+ log$2(
5343
+ "unable to resolve %s / %s, using existing configuration",
5344
+ voltDID,
5345
+ voltHttpAddress
5346
+ );
5347
+ } else {
5348
+ throw new Error(
5349
+ `failed to resolve Volt ${voltDID} / ${voltHttpAddress} and no existing configuration`
5350
+ );
5251
5351
  }
5352
+ } else {
5353
+ log$2("resolved config from: %s", voltDID || voltHttpAddress);
5354
+ log$2(JSON.stringify(resolvedConfig, null, 2));
5355
+
5356
+ cfg = {
5357
+ ...cfg,
5358
+ ...resolvedConfig
5359
+ };
5252
5360
  }
5253
5361
 
5254
- throw new Error(`Failed to fetch Volt config from any of these registries: ${registryList.join(", ")}`);
5362
+ cfg.relay = relay;
5363
+
5364
+ return cfg;
5255
5365
  }
5256
5366
 
5257
5367
  /* eslint-disable no-underscore-dangle */
@@ -5306,6 +5416,11 @@ class VoltCredential {
5306
5416
 
5307
5417
  // Extract Volt public key.
5308
5418
  this._voltPublicKey = this._voltConfig.public_key;
5419
+ if (!this._voltPublicKey) {
5420
+ // Extract the public key from the Volt CA cert.
5421
+ const voltCA = new crypto__default["default"].X509Certificate(this._voltConfig.ca_pem);
5422
+ this._voltPublicKey = voltCA.publicKey.export({ type: 'spki', format: 'pem' });
5423
+ }
5309
5424
 
5310
5425
  const voltKey = keyFromPem(this._voltPublicKey).publicKey;
5311
5426
  this._voltFingerprint = fingerprintFromPem(voltKey);
@@ -5491,19 +5606,22 @@ class VoltClient extends EventEmitter__default["default"] {
5491
5606
 
5492
5607
  /**
5493
5608
  * Initialises a binding and connection to the volt.
5494
- * @param {string} [config] - the location of the Volt configuration file, or a configuration object
5495
- * @param {object} [extras] - additional configuration properties
5609
+ * @param {string | object} [config] - the location of the Volt configuration
5610
+ * file, or a configuration object
5611
+ * @param {object} [options] - initialisation options
5612
+ * @param {string[]} [options.didRegistryList] - list of DID registry URLs to use for
5613
+ * resolving DIDs
5614
+ * @param {object} [options.extraConfig] - additional configuration to be merged with the
5615
+ * configuration from the config file.
5616
+ * @param {boolean} [options.ownDID] - whether the Volt should manage the DID - set to
5617
+ * true if you want to manage the DID yourself
5496
5618
  */
5497
- async initialise(
5498
- config,
5499
- extras = {},
5500
- ownDID = false,
5501
- didRegistryList = undefined
5502
- ) {
5619
+ async initialise(config, options = {}) {
5620
+ const { ownDID, didRegistryList, extraConfig } = options;
5503
5621
  try {
5504
5622
  if (!config) {
5505
5623
  throw new Error(
5506
- "configPath argument is required to be a non-empty string"
5624
+ "config argument is required to be a non-empty string or JSON object"
5507
5625
  );
5508
5626
  }
5509
5627
 
@@ -5536,7 +5654,8 @@ class VoltClient extends EventEmitter__default["default"] {
5536
5654
  throw new Error("config argument is required to be an object");
5537
5655
  }
5538
5656
 
5539
- this._config = { ...extras, ...configJSON };
5657
+ // Merge any extra configuration supplied, preferring that loaded from file.
5658
+ this._config = { ...extraConfig, ...configJSON };
5540
5659
 
5541
5660
  if (
5542
5661
  !this._config.client_name ||
@@ -5545,52 +5664,13 @@ class VoltClient extends EventEmitter__default["default"] {
5545
5664
  throw new Error("client_name property required in config");
5546
5665
  }
5547
5666
 
5548
- let voltDID = "";
5549
- let voltHttpAddress = "";
5550
- if (typeof this._config.volt === "string") {
5551
- if (this._config.volt.indexOf("did:") === 0) {
5552
- voltDID = this._config.volt;
5553
- } else {
5554
- voltHttpAddress = this._config.volt;
5555
- }
5556
- delete this._config.volt;
5557
- } else if (typeof this._config.volt !== "object") {
5558
- throw new Error("configuration is missing the 'volt' object");
5559
- } else {
5560
- voltDID = this._config.volt.did;
5561
- voltHttpAddress = this._config.volt.http_address;
5562
- }
5563
-
5564
- if (voltDID) {
5565
- const didConfig = await fetchVoltConfigFromDID.call(
5566
- this,
5567
- voltDID,
5568
- didRegistryList
5569
- );
5570
-
5571
- // Remove the relay list from the DID config, as it would overwrite the relay in the supplied config.
5572
- delete didConfig.relay;
5573
-
5574
- this._config = { ...this._config, volt: didConfig };
5575
- log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
5576
- } else if (voltHttpAddress && !this._config?.volt?.id) {
5577
- const discoConfig = await fetchVoltConfig.call(
5578
- this,
5579
- `${voltHttpAddress}/discover`
5580
- );
5581
- this._config = {
5582
- ...this._config,
5583
- volt: { ...this._config.volt, ...discoConfig }
5584
- };
5585
- }
5667
+ this._config.volt = await resolveVoltConfig.call(
5668
+ this,
5669
+ this._config.volt,
5670
+ didRegistryList
5671
+ );
5586
5672
 
5587
5673
  this._voltConfig = this._config.volt;
5588
- if (voltDID) {
5589
- this._voltConfig.did = voltDID;
5590
- }
5591
- if (voltHttpAddress) {
5592
- this._voltConfig.http_address = voltHttpAddress;
5593
- }
5594
5674
 
5595
5675
  if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
5596
5676
  throw new Error("'id' property is missing in Volt configuration");
@@ -5645,13 +5725,14 @@ class VoltClient extends EventEmitter__default["default"] {
5645
5725
 
5646
5726
  return Promise.resolve(this._config);
5647
5727
  } catch (err) {
5648
- log("Failure starting Volt client [%s]", err.message);
5728
+ log("failure starting Volt client [%s]", err.message);
5649
5729
  throw err;
5650
5730
  }
5651
5731
  }
5652
5732
 
5653
- initialiseAndConnect(configPath, helloPayload = undefined, extras = {}) {
5654
- return this.initialise(configPath, extras)
5733
+ initialiseAndConnect(configPath, options) {
5734
+ const helloPayload = options?.helloPayload;
5735
+ return this.initialise(configPath, options)
5655
5736
  .then(() => {
5656
5737
  return connectInternal.call(this, helloPayload);
5657
5738
  })
@@ -6092,6 +6173,10 @@ class VoltClient extends EventEmitter__default["default"] {
6092
6173
  }
6093
6174
  }
6094
6175
 
6176
+ Object.defineProperty(exports, 'SyncProvider', {
6177
+ enumerable: true,
6178
+ get: function () { return voltClientWeb.SyncProvider; }
6179
+ });
6095
6180
  Object.defineProperty(exports, 'generateId', {
6096
6181
  enumerable: true,
6097
6182
  get: function () { return uuid.v4; }