@tdxvolt/volt-client-grpc 0.20.8 → 0.20.11
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 +73 -380
- package/package.json +3 -2
- package/src/grpc-call.js +7 -8
- package/src/grpc-utils.js +5 -3
- package/src/volt-client-internal.js +8 -222
- package/src/volt-client.js +21 -9
- package/src/volt-connection.js +3 -3
- package/src/volt-proto-literals.js +29 -136
package/lib/index.cjs
CHANGED
|
@@ -10,6 +10,7 @@ var debug = require('debug');
|
|
|
10
10
|
var Protobuf = require('protobufjs');
|
|
11
11
|
var camelCase = require('lodash.camelcase');
|
|
12
12
|
var descriptor = require('protobufjs/ext/descriptor/index.js');
|
|
13
|
+
var voltUtility = require('@tdxvolt/volt-utility');
|
|
13
14
|
var EventEmitter = require('events');
|
|
14
15
|
var fs = require('fs');
|
|
15
16
|
var path = require('path');
|
|
@@ -135,12 +136,14 @@ function createClient(
|
|
|
135
136
|
address,
|
|
136
137
|
credentials,
|
|
137
138
|
noSerialise,
|
|
138
|
-
voltId,
|
|
139
|
-
serviceId
|
|
139
|
+
voltId = "",
|
|
140
|
+
serviceId = ""
|
|
140
141
|
) {
|
|
141
142
|
let grpcCredentials;
|
|
142
143
|
if (credentials) {
|
|
143
|
-
const relaying =
|
|
144
|
+
const relaying =
|
|
145
|
+
!!credentials.config?.volt?.relay &&
|
|
146
|
+
!Array.isArray(credentials.config?.volt?.relay);
|
|
144
147
|
const ca = relaying
|
|
145
148
|
? credentials.config?.volt?.relay?.ca_pem
|
|
146
149
|
: credentials.cache.ca;
|
|
@@ -1263,128 +1266,6 @@ message RemoteResponse {
|
|
|
1263
1266
|
HttpRequest http_request = 5;
|
|
1264
1267
|
}
|
|
1265
1268
|
}
|
|
1266
|
-
`;
|
|
1267
|
-
const spark_api = `syntax = "proto3";
|
|
1268
|
-
|
|
1269
|
-
package tdx.volt_api.volt.v1;
|
|
1270
|
-
|
|
1271
|
-
import "tdx/volt_api/volt/v1/status.proto";
|
|
1272
|
-
|
|
1273
|
-
// The Spark API allows clients to start and communicate with Spark VMs.
|
|
1274
|
-
service SparkAPI {
|
|
1275
|
-
// Start or connect to a spark VM and establish a long-lived communication channel.
|
|
1276
|
-
rpc Spark(stream SparkRequest) returns (stream SparkResponse);
|
|
1277
|
-
|
|
1278
|
-
// Get details of running spark VMs.
|
|
1279
|
-
rpc GetSpark(GetSparkRequest) returns (GetSparkResponse);
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
// Start a resource-hosted spark.
|
|
1283
|
-
// Provenance is determined from the resource owner.
|
|
1284
|
-
message StartSparkResource {
|
|
1285
|
-
// The id of the resource containing the VM byte code.
|
|
1286
|
-
string resource_id = 1;
|
|
1287
|
-
|
|
1288
|
-
// JSON-encoded payload to parameterise the spark.
|
|
1289
|
-
string payload = 2;
|
|
1290
|
-
|
|
1291
|
-
// Envelope signature.
|
|
1292
|
-
string signature = 3;
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
// Start a spark from byte code included in the payload, along with issuer/developer claims and signature.
|
|
1296
|
-
message StartSparkAdhoc {
|
|
1297
|
-
// Ad-hoc byte code of VM to run.
|
|
1298
|
-
bytes byte_code = 1;
|
|
1299
|
-
|
|
1300
|
-
// Verifiable credentials associated with this spark.
|
|
1301
|
-
repeated string credential = 2;
|
|
1302
|
-
|
|
1303
|
-
// Envelope signature.
|
|
1304
|
-
string signature = 3;
|
|
1305
|
-
|
|
1306
|
-
// JSON-encoded payload to parameterise the spark.
|
|
1307
|
-
string payload = 4;
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1310
|
-
// Send commands and payloads to communicate with a running spark.
|
|
1311
|
-
message SparkCommand {
|
|
1312
|
-
// Optional id to associate with this command.
|
|
1313
|
-
// If this is omitted, an id will be assigned and returned in the command acknowledgement.
|
|
1314
|
-
string command_id = 1;
|
|
1315
|
-
|
|
1316
|
-
// The command to send to the spark.
|
|
1317
|
-
string command = 2;
|
|
1318
|
-
|
|
1319
|
-
// The command payload.
|
|
1320
|
-
string payload = 3;
|
|
1321
|
-
|
|
1322
|
-
// Command verifiable credential.
|
|
1323
|
-
string credential = 4;
|
|
1324
|
-
|
|
1325
|
-
// Command signature.
|
|
1326
|
-
string signature = 5;
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
// The request must contain one of the messages indicated below.
|
|
1330
|
-
message SparkRequest {
|
|
1331
|
-
oneof payload {
|
|
1332
|
-
StartSparkResource start_resource = 1;
|
|
1333
|
-
StartSparkAdhoc start_adhoc = 2;
|
|
1334
|
-
SparkCommand command = 3;
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
// Receive events and payload from a running spark.
|
|
1339
|
-
message SparkData {
|
|
1340
|
-
// The command id to which this data corresponds.
|
|
1341
|
-
string command_id = 1;
|
|
1342
|
-
|
|
1343
|
-
oneof payload {
|
|
1344
|
-
// Acknowledges receipt of a spark command.
|
|
1345
|
-
bool acknowledge = 2;
|
|
1346
|
-
|
|
1347
|
-
// Indicates the command has completed.
|
|
1348
|
-
bool ended = 3;
|
|
1349
|
-
|
|
1350
|
-
// Will contain error details on command failure.
|
|
1351
|
-
string error = 4;
|
|
1352
|
-
|
|
1353
|
-
// Data emitted by a command, usually in stringified JSON format.
|
|
1354
|
-
string data = 5;
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
// Details of a spark instance.
|
|
1359
|
-
message SparkInstance {
|
|
1360
|
-
// The UUID assigned to the spark instance.
|
|
1361
|
-
string spark_id = 1;
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
// The response will contain one of the messages indicated below.
|
|
1365
|
-
message SparkResponse {
|
|
1366
|
-
oneof payload {
|
|
1367
|
-
// Details of any error that occurred on the call.
|
|
1368
|
-
tdx.volt_api.volt.v1.Status status = 1;
|
|
1369
|
-
|
|
1370
|
-
// Details of the spark instance, sent in response to a start command.
|
|
1371
|
-
SparkInstance spark_instance = 2;
|
|
1372
|
-
|
|
1373
|
-
// Payload data emitted by the spark.
|
|
1374
|
-
SparkData spark_data = 3;
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
message GetSparkRequest {
|
|
1379
|
-
string spark_id = 1;
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
// One of the following fields will be present in the response.
|
|
1383
|
-
message GetSparkResponse {
|
|
1384
|
-
// Details of any error that occurred on the call.
|
|
1385
|
-
tdx.volt_api.volt.v1.Status status = 1;
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
1269
|
`;
|
|
1389
1270
|
const ssi = `syntax = "proto3";
|
|
1390
1271
|
|
|
@@ -1460,12 +1341,11 @@ const ssi_api = `syntax = "proto3";
|
|
|
1460
1341
|
|
|
1461
1342
|
package tdx.volt_api.volt.v1;
|
|
1462
1343
|
|
|
1463
|
-
import "tdx/volt_api/volt/v1/status.proto";
|
|
1464
1344
|
import "tdx/volt_api/volt/v1/ssi.proto";
|
|
1345
|
+
import "tdx/volt_api/volt/v1/status.proto";
|
|
1465
1346
|
|
|
1466
1347
|
service SsiAPI {
|
|
1467
|
-
|
|
1468
|
-
rpc CreateExchangeToken(CreateExchangeTokenRequest) returns (CreateExchangeTokenResponse);
|
|
1348
|
+
rpc CacheEmailCredential(CacheEmailCredentialRequest) returns (CacheEmailCredentialResponse);
|
|
1469
1349
|
|
|
1470
1350
|
// Delete a DID document or all DID documents originating from a given Volt.
|
|
1471
1351
|
// Requires \`volt:delete-did\` API privilege.
|
|
@@ -1496,20 +1376,25 @@ service SsiAPI {
|
|
|
1496
1376
|
rpc SearchDIDRegistry(SearchDIDRegistryRequest) returns (SearchDIDRegistryResponse);
|
|
1497
1377
|
}
|
|
1498
1378
|
|
|
1499
|
-
message
|
|
1500
|
-
|
|
1501
|
-
|
|
1379
|
+
message CacheEmailCredentialRequest {
|
|
1380
|
+
string identity_did = 1;
|
|
1381
|
+
|
|
1382
|
+
string email_address = 2;
|
|
1383
|
+
|
|
1384
|
+
string identity_provider = 3;
|
|
1385
|
+
|
|
1386
|
+
string key_passphrase = 4;
|
|
1502
1387
|
|
|
1503
1388
|
// The time to live for the token, in seconds.
|
|
1504
|
-
uint32 ttl =
|
|
1389
|
+
uint32 ttl = 5;
|
|
1505
1390
|
}
|
|
1506
1391
|
|
|
1507
|
-
message
|
|
1392
|
+
message CacheEmailCredentialResponse {
|
|
1508
1393
|
// Details of any error that occurred on the call.
|
|
1509
1394
|
tdx.volt_api.volt.v1.Status status = 1;
|
|
1510
1395
|
|
|
1511
|
-
// The
|
|
1512
|
-
string
|
|
1396
|
+
// The verifiable credential resource id.
|
|
1397
|
+
string vc_id = 2;
|
|
1513
1398
|
}
|
|
1514
1399
|
|
|
1515
1400
|
message DeleteDIDRequest {
|
|
@@ -1726,6 +1611,7 @@ enum SyncState {
|
|
|
1726
1611
|
SYNC_STATE_SYNCING = 1;
|
|
1727
1612
|
SYNC_STATE_DONE = 2;
|
|
1728
1613
|
SYNC_STATE_UPDATE = 3;
|
|
1614
|
+
SYNC_STATE_AWARENESS = 4;
|
|
1729
1615
|
}
|
|
1730
1616
|
|
|
1731
1617
|
message SyncDocumentStart {
|
|
@@ -1794,7 +1680,8 @@ message GetDocumentJSONResponse {
|
|
|
1794
1680
|
// The current JSON snapshot of the document
|
|
1795
1681
|
string json = 2;
|
|
1796
1682
|
}
|
|
1797
|
-
}
|
|
1683
|
+
}
|
|
1684
|
+
`;
|
|
1798
1685
|
const terminal_api = `syntax = "proto3";
|
|
1799
1686
|
|
|
1800
1687
|
package tdx.volt_api.volt.v1;
|
|
@@ -2709,7 +2596,7 @@ message AuthenticateRequest {
|
|
|
2709
2596
|
// A human-readable name of the entity requesting to authenticate.
|
|
2710
2597
|
string client_name = 6;
|
|
2711
2598
|
|
|
2712
|
-
// Optional challenge code, hashed using SHA-256 and base64 encoded.
|
|
2599
|
+
// Optional challenge code, hashed using SHA-256 and base64 URL encoded (no padding).
|
|
2713
2600
|
string challenge = 7;
|
|
2714
2601
|
|
|
2715
2602
|
// The host name to add as a SAN to the issued certificate.
|
|
@@ -2737,7 +2624,7 @@ message AuthenticateResponse {
|
|
|
2737
2624
|
// This should be persisted by the client, and submitted with all subsequent API calls as part of the call metadata.
|
|
2738
2625
|
string session_id = 2;
|
|
2739
2626
|
|
|
2740
|
-
// The identity
|
|
2627
|
+
// The 'primary' session identity assigned to this authentication. This will be the DID in DID-based sessions, otherwise it will be the first email-based credential found, if any, otherwise it will be the public key fingerprint.
|
|
2741
2628
|
string identity_did = 3;
|
|
2742
2629
|
|
|
2743
2630
|
// A certificate issued by the volt CA, binding the request public key to the identity.
|
|
@@ -2793,6 +2680,9 @@ message CanAccessResourceResponse {
|
|
|
2793
2680
|
|
|
2794
2681
|
// The policy decision for this access request.
|
|
2795
2682
|
PolicyDecision decision = 6;
|
|
2683
|
+
|
|
2684
|
+
// The session that the access relates to.
|
|
2685
|
+
string session_id = 7;
|
|
2796
2686
|
}
|
|
2797
2687
|
|
|
2798
2688
|
message CheckCompatibilityRequest {
|
|
@@ -3138,6 +3028,9 @@ message GetIdentityResponse {
|
|
|
3138
3028
|
message GetOneTimeTokenRequest {
|
|
3139
3029
|
// Optional token TTL, in seconds. Default is 10 seconds.
|
|
3140
3030
|
int32 ttl = 1;
|
|
3031
|
+
|
|
3032
|
+
// Optional token context.
|
|
3033
|
+
string context = 2;
|
|
3141
3034
|
}
|
|
3142
3035
|
|
|
3143
3036
|
message GetOneTimeTokenResponse {
|
|
@@ -3200,6 +3093,9 @@ message GetResourceResponse {
|
|
|
3200
3093
|
message GetResourcesRequest {
|
|
3201
3094
|
repeated string id = 1;
|
|
3202
3095
|
|
|
3096
|
+
// Wildcards permitted.
|
|
3097
|
+
repeated string alias = 16;
|
|
3098
|
+
|
|
3203
3099
|
// Wildcards permitted.
|
|
3204
3100
|
repeated string name = 2;
|
|
3205
3101
|
|
|
@@ -3716,7 +3612,7 @@ message SubscribeWireResponse {
|
|
|
3716
3612
|
}
|
|
3717
3613
|
|
|
3718
3614
|
`;
|
|
3719
|
-
const voltProtos = [sqlite, sqlite_database_api, sqlite_server_api, proto_db_sync, proxy_api, relay_api, discovery_api, file, file_api, remote,
|
|
3615
|
+
const voltProtos = [sqlite, sqlite_database_api, sqlite_server_api, proto_db_sync, proxy_api, relay_api, discovery_api, file, file_api, remote, ssi, ssi_api, status, sync_api, terminal_api, volt, volt_api, wire_api];
|
|
3720
3616
|
|
|
3721
3617
|
debug__default["default"]("volt-client-grpc:proto-utils");
|
|
3722
3618
|
|
|
@@ -4137,11 +4033,6 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
4137
4033
|
);
|
|
4138
4034
|
|
|
4139
4035
|
this._call.on("data", (streamResponse) => {
|
|
4140
|
-
if (this._ended) {
|
|
4141
|
-
log$5("ignoring data after end on %s", this._methodName);
|
|
4142
|
-
return;
|
|
4143
|
-
}
|
|
4144
|
-
|
|
4145
4036
|
try {
|
|
4146
4037
|
const parsedResponse = _parseResponse.call(
|
|
4147
4038
|
this,
|
|
@@ -4149,6 +4040,13 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
4149
4040
|
this._initialRequest.meta,
|
|
4150
4041
|
streamResponse
|
|
4151
4042
|
);
|
|
4043
|
+
|
|
4044
|
+
if (this._ended && !parsedResponse.ended) {
|
|
4045
|
+
log$5("ignoring data after end on %s", this._methodName);
|
|
4046
|
+
log$5(parsedResponse);
|
|
4047
|
+
return;
|
|
4048
|
+
}
|
|
4049
|
+
|
|
4152
4050
|
if (parsedResponse.key_exchange) {
|
|
4153
4051
|
const peerKey = keyFromPem$2(
|
|
4154
4052
|
parsedResponse.key_exchange.encryption_key
|
|
@@ -4180,9 +4078,6 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
4180
4078
|
} else if (parsedResponse.ended) {
|
|
4181
4079
|
if (parsedResponse.error) {
|
|
4182
4080
|
this.emit("error", new Error(parsedResponse.error));
|
|
4183
|
-
} else {
|
|
4184
|
-
// Not sure this is necessary - we should receive "end" from grpc soon...
|
|
4185
|
-
this.emit("end");
|
|
4186
4081
|
}
|
|
4187
4082
|
} else {
|
|
4188
4083
|
log$5("method id is %s", parsedResponse.methodId);
|
|
@@ -4658,6 +4553,7 @@ function _cleanUp(immediateReconnect) {
|
|
|
4658
4553
|
}
|
|
4659
4554
|
|
|
4660
4555
|
if (this._dying) {
|
|
4556
|
+
log$3("dying - clearing reconnect timer");
|
|
4661
4557
|
clearTimeout(this._reconnectTimer);
|
|
4662
4558
|
} else if (immediateReconnect || (this._autoRetry && !this._reconnectTimer)) {
|
|
4663
4559
|
this._reconnectTimer = setTimeout(
|
|
@@ -4670,6 +4566,7 @@ function _cleanUp(immediateReconnect) {
|
|
|
4670
4566
|
}
|
|
4671
4567
|
|
|
4672
4568
|
if (this.call?.writable) {
|
|
4569
|
+
log$3("ending Connect call");
|
|
4673
4570
|
this.call.end();
|
|
4674
4571
|
}
|
|
4675
4572
|
}
|
|
@@ -4760,9 +4657,7 @@ function _doConnect(connectHello) {
|
|
|
4760
4657
|
});
|
|
4761
4658
|
|
|
4762
4659
|
this.call.on("end", () => {
|
|
4763
|
-
|
|
4764
|
-
this.emit("connected", false);
|
|
4765
|
-
}
|
|
4660
|
+
this.emit("connected", false);
|
|
4766
4661
|
_cleanUp.call(this);
|
|
4767
4662
|
});
|
|
4768
4663
|
|
|
@@ -4844,8 +4739,6 @@ class VoltConnection extends EventEmitter__default["default"] {
|
|
|
4844
4739
|
|
|
4845
4740
|
const { publicKeyToPem: publicKeyToPem$1, removeCarriageReturn, signBase64 } = js.voltUtils;
|
|
4846
4741
|
|
|
4847
|
-
const { filter, pick } = lodash__default["default"];
|
|
4848
|
-
|
|
4849
4742
|
const log$2 = debug__default["default"]("volt-client-grpc:volt-client-internal");
|
|
4850
4743
|
|
|
4851
4744
|
function issueAuthenticate(authenticateRequest, ttl) {
|
|
@@ -4918,15 +4811,6 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
4918
4811
|
});
|
|
4919
4812
|
}
|
|
4920
4813
|
|
|
4921
|
-
function findDIDDocumentService(document, serviceType) {
|
|
4922
|
-
if (!document.service) {
|
|
4923
|
-
return [];
|
|
4924
|
-
}
|
|
4925
|
-
|
|
4926
|
-
const matches = filter(document.service, (svc) => svc.type === serviceType);
|
|
4927
|
-
return matches;
|
|
4928
|
-
}
|
|
4929
|
-
|
|
4930
4814
|
async function authenticateInternal(
|
|
4931
4815
|
noDID = false,
|
|
4932
4816
|
ownDID = false,
|
|
@@ -5103,18 +4987,18 @@ function connectInternal(helloPayload) {
|
|
|
5103
4987
|
this._voltConnection.on("connected", (session) => {
|
|
5104
4988
|
this._session = session;
|
|
5105
4989
|
this._connected = !!session;
|
|
4990
|
+
this.emit("connected", session);
|
|
5106
4991
|
if (!this._connected) {
|
|
5107
4992
|
onDisconnected();
|
|
5108
4993
|
}
|
|
5109
|
-
this.emit("connected", session);
|
|
5110
4994
|
});
|
|
5111
4995
|
|
|
5112
4996
|
this._voltConnection.on("error", (err) => {
|
|
5113
4997
|
// Should auto-reconnect.
|
|
5114
4998
|
log$2("Volt connection error [%s]", err.message);
|
|
5115
4999
|
if (this._connected) {
|
|
5116
|
-
onDisconnected();
|
|
5117
5000
|
this.emit("connected", false);
|
|
5001
|
+
onDisconnected();
|
|
5118
5002
|
}
|
|
5119
5003
|
|
|
5120
5004
|
if (err.message.endsWith("session invalid")) {
|
|
@@ -5189,7 +5073,11 @@ function getVoltAPIClientInternal() {
|
|
|
5189
5073
|
const serviceAddress = this.isRelayed
|
|
5190
5074
|
? this._voltConfig.relay.address
|
|
5191
5075
|
: this._voltConfig.address;
|
|
5192
|
-
log$2(
|
|
5076
|
+
log$2(
|
|
5077
|
+
"creating cached VoltAPI service client on %s for %j",
|
|
5078
|
+
serviceAddress,
|
|
5079
|
+
this._voltConfig
|
|
5080
|
+
);
|
|
5193
5081
|
|
|
5194
5082
|
const serviceDescriptors = getBuiltInServiceDescriptors();
|
|
5195
5083
|
|
|
@@ -5198,9 +5086,7 @@ function getVoltAPIClientInternal() {
|
|
|
5198
5086
|
serviceDescriptors,
|
|
5199
5087
|
serviceAddress,
|
|
5200
5088
|
this._credential,
|
|
5201
|
-
this.isRelayed
|
|
5202
|
-
this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
|
|
5203
|
-
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
5089
|
+
this.isRelayed
|
|
5204
5090
|
);
|
|
5205
5091
|
}
|
|
5206
5092
|
|
|
@@ -5285,211 +5171,6 @@ function streamingCallInternal(methodType, method, request, service) {
|
|
|
5285
5171
|
return call;
|
|
5286
5172
|
}
|
|
5287
5173
|
|
|
5288
|
-
async function fetchVoltConfig(discovery_url) {
|
|
5289
|
-
try {
|
|
5290
|
-
const response = await fetch(discovery_url);
|
|
5291
|
-
|
|
5292
|
-
if (!response.ok) {
|
|
5293
|
-
log$2("fetchVoltConfig - error [%s]", response.status);
|
|
5294
|
-
return null;
|
|
5295
|
-
}
|
|
5296
|
-
|
|
5297
|
-
const fullVoltConfig = await response.json();
|
|
5298
|
-
|
|
5299
|
-
if (!fullVoltConfig || typeof fullVoltConfig !== "object") {
|
|
5300
|
-
log$2("fetchVoltConfig - invalid config resolved from: %s", discovery_url);
|
|
5301
|
-
return null;
|
|
5302
|
-
}
|
|
5303
|
-
|
|
5304
|
-
const voltConfig = pick(
|
|
5305
|
-
fullVoltConfig,
|
|
5306
|
-
"id",
|
|
5307
|
-
"display_name",
|
|
5308
|
-
"public_key",
|
|
5309
|
-
"fingerprint",
|
|
5310
|
-
"address",
|
|
5311
|
-
"http_address",
|
|
5312
|
-
"ca_pem",
|
|
5313
|
-
"relay"
|
|
5314
|
-
);
|
|
5315
|
-
|
|
5316
|
-
log$2("fetchVoltConfig from %s", discovery_url);
|
|
5317
|
-
|
|
5318
|
-
return voltConfig;
|
|
5319
|
-
} catch (err) {
|
|
5320
|
-
log$2("fetchVoltConfig - %s error [%s]", discovery_url, err.message);
|
|
5321
|
-
return null;
|
|
5322
|
-
}
|
|
5323
|
-
}
|
|
5324
|
-
|
|
5325
|
-
async function fetchVoltConfigFromDIDInternal(volt_did, registryUrl) {
|
|
5326
|
-
try {
|
|
5327
|
-
const didResolution = getDIDResolutionURL(volt_did, registryUrl);
|
|
5328
|
-
if (!didResolution) {
|
|
5329
|
-
throw new Error(`Invalid Volt DID: ${volt_did}`);
|
|
5330
|
-
}
|
|
5331
|
-
|
|
5332
|
-
const response = await fetch(didResolution);
|
|
5333
|
-
|
|
5334
|
-
if (!response.ok) {
|
|
5335
|
-
throw new Error(`HTTP error, status: ${response.status}`);
|
|
5336
|
-
}
|
|
5337
|
-
|
|
5338
|
-
const didDocument = await response.json();
|
|
5339
|
-
const voltConfigServices = findDIDDocumentService(
|
|
5340
|
-
didDocument,
|
|
5341
|
-
constants.didServiceType.voltConfig
|
|
5342
|
-
);
|
|
5343
|
-
if (voltConfigServices.length !== 1) {
|
|
5344
|
-
log$2(
|
|
5345
|
-
"Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
|
|
5346
|
-
volt_did,
|
|
5347
|
-
voltConfigServices.length
|
|
5348
|
-
);
|
|
5349
|
-
}
|
|
5350
|
-
|
|
5351
|
-
log$2("resolved DID from %s", didResolution);
|
|
5352
|
-
|
|
5353
|
-
const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
|
|
5354
|
-
const voltConfig = await fetchVoltConfig.call(this, serviceEndpoint);
|
|
5355
|
-
if (!voltConfig) {
|
|
5356
|
-
throw new Error(`failed to resolve Volt config from ${serviceEndpoint}`);
|
|
5357
|
-
}
|
|
5358
|
-
|
|
5359
|
-
return voltConfig;
|
|
5360
|
-
} catch (err) {
|
|
5361
|
-
throw new Error(
|
|
5362
|
-
`Failure fetching Volt config from DID document ${volt_did}: ${err.message}`
|
|
5363
|
-
);
|
|
5364
|
-
}
|
|
5365
|
-
}
|
|
5366
|
-
|
|
5367
|
-
async function fetchVoltConfigFromDID(volt_did, registryListIn) {
|
|
5368
|
-
let registryList;
|
|
5369
|
-
if (!registryListIn) {
|
|
5370
|
-
// If no registry list is provided, use the default registry list.
|
|
5371
|
-
registryList = constants.defaultRegistryList;
|
|
5372
|
-
} else {
|
|
5373
|
-
// If a registry list is provided, merge it with the default registry list.
|
|
5374
|
-
registryList = Array.from(
|
|
5375
|
-
new Set(registryListIn.concat(constants.defaultRegistryList))
|
|
5376
|
-
);
|
|
5377
|
-
}
|
|
5378
|
-
|
|
5379
|
-
let voltConfig;
|
|
5380
|
-
for (let registryUrl of registryList) {
|
|
5381
|
-
try {
|
|
5382
|
-
voltConfig = await fetchVoltConfigFromDIDInternal.call(
|
|
5383
|
-
this,
|
|
5384
|
-
volt_did,
|
|
5385
|
-
registryUrl
|
|
5386
|
-
);
|
|
5387
|
-
|
|
5388
|
-
// If we've found a config, break out of the loop.
|
|
5389
|
-
break;
|
|
5390
|
-
} catch (err) {
|
|
5391
|
-
log$2("unable to fetch Volt config from %s: %s", registryUrl, err.message);
|
|
5392
|
-
}
|
|
5393
|
-
}
|
|
5394
|
-
|
|
5395
|
-
if (!voltConfig) {
|
|
5396
|
-
log$2("failed to fetch Volt config for %s from any registry", volt_did);
|
|
5397
|
-
}
|
|
5398
|
-
|
|
5399
|
-
return voltConfig;
|
|
5400
|
-
}
|
|
5401
|
-
|
|
5402
|
-
async function resolveVoltConfig(cfg, didRegistryList) {
|
|
5403
|
-
// Start by resolving the relay if one is specified.
|
|
5404
|
-
let relay = cfg?.relay?.http_address || cfg?.relay;
|
|
5405
|
-
if (relay) {
|
|
5406
|
-
relay = await resolveVoltConfig.call(this, relay, didRegistryList);
|
|
5407
|
-
}
|
|
5408
|
-
|
|
5409
|
-
let voltDID = "";
|
|
5410
|
-
let voltHttpAddress = "";
|
|
5411
|
-
|
|
5412
|
-
if (typeof cfg === "string") {
|
|
5413
|
-
// If the configuration is a string, determine if it is a DID or HTTP address.
|
|
5414
|
-
if (cfg.indexOf("did:") === 0) {
|
|
5415
|
-
voltDID = cfg;
|
|
5416
|
-
} else {
|
|
5417
|
-
voltHttpAddress = cfg;
|
|
5418
|
-
}
|
|
5419
|
-
cfg = {};
|
|
5420
|
-
} else if (typeof cfg === "object") {
|
|
5421
|
-
voltDID = cfg.id;
|
|
5422
|
-
voltHttpAddress = cfg.http_address;
|
|
5423
|
-
} else {
|
|
5424
|
-
throw new Error("configuration is missing the 'volt' object");
|
|
5425
|
-
}
|
|
5426
|
-
|
|
5427
|
-
let resolvedConfig;
|
|
5428
|
-
|
|
5429
|
-
if (relay) {
|
|
5430
|
-
if (!voltDID) {
|
|
5431
|
-
throw new Error("relay specified but no Volt DID provided");
|
|
5432
|
-
}
|
|
5433
|
-
|
|
5434
|
-
// Build the sub-domain URL for the Volt discovery service by inserting the volt DID into the relay URL.
|
|
5435
|
-
const relayURL = relay.http_address;
|
|
5436
|
-
const relayParts = relayURL.split("://");
|
|
5437
|
-
const subdomain = voltDID.substring(constants.voltDIDPrefix.length);
|
|
5438
|
-
voltHttpAddress = `${relayParts[0]}://${subdomain}.${relayParts[1]}`;
|
|
5439
|
-
} else if (voltDID) {
|
|
5440
|
-
resolvedConfig = await fetchVoltConfigFromDID.call(
|
|
5441
|
-
this,
|
|
5442
|
-
voltDID,
|
|
5443
|
-
didRegistryList
|
|
5444
|
-
);
|
|
5445
|
-
}
|
|
5446
|
-
|
|
5447
|
-
if (!resolvedConfig && voltHttpAddress) {
|
|
5448
|
-
try {
|
|
5449
|
-
resolvedConfig = await fetchVoltConfig.call(
|
|
5450
|
-
this,
|
|
5451
|
-
`${voltHttpAddress}/discover`
|
|
5452
|
-
);
|
|
5453
|
-
|
|
5454
|
-
// Update the http address in the resolved config to the one we used to
|
|
5455
|
-
// fetch it, since we may have used the relay rather than local address.
|
|
5456
|
-
resolvedConfig.http_address = voltHttpAddress;
|
|
5457
|
-
} catch (err) {
|
|
5458
|
-
log$2(
|
|
5459
|
-
"failed to resolve Volt configuration %s [%s]",
|
|
5460
|
-
voltHttpAddress,
|
|
5461
|
-
err.message
|
|
5462
|
-
);
|
|
5463
|
-
}
|
|
5464
|
-
}
|
|
5465
|
-
|
|
5466
|
-
if (!resolvedConfig) {
|
|
5467
|
-
if (cfg.address && cfg.ca_pem) {
|
|
5468
|
-
log$2(
|
|
5469
|
-
"unable to resolve %s / %s, using existing configuration",
|
|
5470
|
-
voltDID,
|
|
5471
|
-
voltHttpAddress
|
|
5472
|
-
);
|
|
5473
|
-
} else {
|
|
5474
|
-
throw new Error(
|
|
5475
|
-
`failed to resolve Volt ${voltDID} / ${voltHttpAddress} and no existing configuration`
|
|
5476
|
-
);
|
|
5477
|
-
}
|
|
5478
|
-
} else {
|
|
5479
|
-
log$2("resolved config from: %s", voltDID || voltHttpAddress);
|
|
5480
|
-
log$2(JSON.stringify(resolvedConfig, null, 2));
|
|
5481
|
-
|
|
5482
|
-
cfg = {
|
|
5483
|
-
...cfg,
|
|
5484
|
-
...resolvedConfig
|
|
5485
|
-
};
|
|
5486
|
-
}
|
|
5487
|
-
|
|
5488
|
-
cfg.relay = relay;
|
|
5489
|
-
|
|
5490
|
-
return cfg;
|
|
5491
|
-
}
|
|
5492
|
-
|
|
5493
5174
|
/* eslint-disable no-underscore-dangle */
|
|
5494
5175
|
|
|
5495
5176
|
const {
|
|
@@ -5746,10 +5427,19 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
5746
5427
|
* instead of a DID-based authentication session.
|
|
5747
5428
|
* @param {string} [options.exchangeToken] - optional token to exchange in return for an authenticated session.
|
|
5748
5429
|
* @param {boolean} [options.waitForAuth] - whether to block and poll on 'prompt' authenticate decisions. Default is true.
|
|
5430
|
+
* @param {boolean} [options.noResolve] - whether to skip resolving the Volt configuration. Default is false.
|
|
5749
5431
|
*/
|
|
5750
5432
|
async initialise(config, options = {}) {
|
|
5751
|
-
|
|
5752
|
-
|
|
5433
|
+
log("initialise ", config, options);
|
|
5434
|
+
|
|
5435
|
+
const {
|
|
5436
|
+
ownDID,
|
|
5437
|
+
noDID,
|
|
5438
|
+
didRegistryList,
|
|
5439
|
+
exchangeToken,
|
|
5440
|
+
extraConfig,
|
|
5441
|
+
noResolve
|
|
5442
|
+
} = options;
|
|
5753
5443
|
const waitForAuth =
|
|
5754
5444
|
options.waitForAuth === undefined ? true : !!options.waitForAuth;
|
|
5755
5445
|
|
|
@@ -5799,14 +5489,17 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
5799
5489
|
throw new Error("client_name property required in config");
|
|
5800
5490
|
}
|
|
5801
5491
|
|
|
5802
|
-
|
|
5803
|
-
this
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5492
|
+
if (!noResolve) {
|
|
5493
|
+
this._config.volt = await voltUtility.resolveVoltConfig(
|
|
5494
|
+
this._config.volt,
|
|
5495
|
+
didRegistryList
|
|
5496
|
+
);
|
|
5497
|
+
}
|
|
5807
5498
|
|
|
5808
5499
|
this._voltConfig = this._config.volt;
|
|
5809
5500
|
|
|
5501
|
+
log("config is ", this._voltConfig);
|
|
5502
|
+
|
|
5810
5503
|
if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
|
|
5811
5504
|
throw new Error("'id' property is missing in Volt configuration");
|
|
5812
5505
|
}
|
|
@@ -5864,7 +5557,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
5864
5557
|
this._credential.saveCache();
|
|
5865
5558
|
}
|
|
5866
5559
|
|
|
5867
|
-
return Promise.resolve(this.
|
|
5560
|
+
return Promise.resolve(this.credential.config);
|
|
5868
5561
|
} catch (err) {
|
|
5869
5562
|
log("failure starting Volt client [%s]", err.message);
|
|
5870
5563
|
throw err;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.20.
|
|
6
|
+
"version": "0.20.11",
|
|
7
7
|
"description": "tdx Volt library for nodejs clients",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"exports": {
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"author": "toby.ealden@gmail.com",
|
|
34
34
|
"license": "ISC",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@tdxvolt/volt-client-web": "^0.20.
|
|
36
|
+
"@tdxvolt/volt-client-web": "^0.20.10",
|
|
37
|
+
"@tdxvolt/volt-utility": "^0.20.10",
|
|
37
38
|
"debug": "^4.1.1",
|
|
38
39
|
"ip": "^1.1.5",
|
|
39
40
|
"jsonwebtoken": "^8.5.1",
|
package/src/grpc-call.js
CHANGED
|
@@ -361,11 +361,6 @@ export default class GRPCCall extends EventEmitter {
|
|
|
361
361
|
);
|
|
362
362
|
|
|
363
363
|
this._call.on("data", (streamResponse) => {
|
|
364
|
-
if (this._ended) {
|
|
365
|
-
log("ignoring data after end on %s", this._methodName);
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
364
|
try {
|
|
370
365
|
const parsedResponse = _parseResponse.call(
|
|
371
366
|
this,
|
|
@@ -373,6 +368,13 @@ export default class GRPCCall extends EventEmitter {
|
|
|
373
368
|
this._initialRequest.meta,
|
|
374
369
|
streamResponse
|
|
375
370
|
);
|
|
371
|
+
|
|
372
|
+
if (this._ended && !parsedResponse.ended) {
|
|
373
|
+
log("ignoring data after end on %s", this._methodName);
|
|
374
|
+
log(parsedResponse);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
376
378
|
if (parsedResponse.key_exchange) {
|
|
377
379
|
const peerKey = keyFromPem(
|
|
378
380
|
parsedResponse.key_exchange.encryption_key
|
|
@@ -404,9 +406,6 @@ export default class GRPCCall extends EventEmitter {
|
|
|
404
406
|
} else if (parsedResponse.ended) {
|
|
405
407
|
if (parsedResponse.error) {
|
|
406
408
|
this.emit("error", new Error(parsedResponse.error));
|
|
407
|
-
} else {
|
|
408
|
-
// Not sure this is necessary - we should receive "end" from grpc soon...
|
|
409
|
-
this.emit("end");
|
|
410
409
|
}
|
|
411
410
|
} else {
|
|
412
411
|
log("method id is %s", parsedResponse.methodId);
|
package/src/grpc-utils.js
CHANGED
|
@@ -15,12 +15,14 @@ export function createClient(
|
|
|
15
15
|
address,
|
|
16
16
|
credentials,
|
|
17
17
|
noSerialise,
|
|
18
|
-
voltId,
|
|
19
|
-
serviceId
|
|
18
|
+
voltId = "",
|
|
19
|
+
serviceId = ""
|
|
20
20
|
) {
|
|
21
21
|
let grpcCredentials;
|
|
22
22
|
if (credentials) {
|
|
23
|
-
const relaying =
|
|
23
|
+
const relaying =
|
|
24
|
+
!!credentials.config?.volt?.relay &&
|
|
25
|
+
!Array.isArray(credentials.config?.volt?.relay);
|
|
24
26
|
const ca = relaying
|
|
25
27
|
? credentials.config?.volt?.relay?.ca_pem
|
|
26
28
|
: credentials.cache.ca;
|
|
@@ -4,7 +4,6 @@ import debug from "debug";
|
|
|
4
4
|
import ip from "ip";
|
|
5
5
|
import lodash from "lodash";
|
|
6
6
|
import { v4 as generateId } from "uuid";
|
|
7
|
-
import { constants } from "./constants.js";
|
|
8
7
|
import GRPCCall from "./grpc-call.js";
|
|
9
8
|
import { createClient as createGrpcClient } from "./grpc-utils.js";
|
|
10
9
|
import {
|
|
@@ -12,7 +11,6 @@ import {
|
|
|
12
11
|
getServiceDescriptors
|
|
13
12
|
} from "./proto-utils.js";
|
|
14
13
|
import RpcInvocation from "./rpc-invocation.js";
|
|
15
|
-
import * as voltUtils from "./utils.js";
|
|
16
14
|
import VoltConnection from "./volt-connection.js";
|
|
17
15
|
|
|
18
16
|
const { publicKeyToPem, removeCarriageReturn, signBase64 } = voltClientUtils;
|
|
@@ -91,15 +89,6 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
91
89
|
});
|
|
92
90
|
}
|
|
93
91
|
|
|
94
|
-
function findDIDDocumentService(document, serviceType) {
|
|
95
|
-
if (!document.service) {
|
|
96
|
-
return [];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const matches = filter(document.service, (svc) => svc.type === serviceType);
|
|
100
|
-
return matches;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
92
|
export async function authenticateInternal(
|
|
104
93
|
noDID = false,
|
|
105
94
|
ownDID = false,
|
|
@@ -276,18 +265,18 @@ export function connectInternal(helloPayload) {
|
|
|
276
265
|
this._voltConnection.on("connected", (session) => {
|
|
277
266
|
this._session = session;
|
|
278
267
|
this._connected = !!session;
|
|
268
|
+
this.emit("connected", session);
|
|
279
269
|
if (!this._connected) {
|
|
280
270
|
onDisconnected();
|
|
281
271
|
}
|
|
282
|
-
this.emit("connected", session);
|
|
283
272
|
});
|
|
284
273
|
|
|
285
274
|
this._voltConnection.on("error", (err) => {
|
|
286
275
|
// Should auto-reconnect.
|
|
287
276
|
log("Volt connection error [%s]", err.message);
|
|
288
277
|
if (this._connected) {
|
|
289
|
-
onDisconnected();
|
|
290
278
|
this.emit("connected", false);
|
|
279
|
+
onDisconnected();
|
|
291
280
|
}
|
|
292
281
|
|
|
293
282
|
if (err.message.endsWith("session invalid")) {
|
|
@@ -362,7 +351,11 @@ export function getVoltAPIClientInternal() {
|
|
|
362
351
|
const serviceAddress = this.isRelayed
|
|
363
352
|
? this._voltConfig.relay.address
|
|
364
353
|
: this._voltConfig.address;
|
|
365
|
-
log(
|
|
354
|
+
log(
|
|
355
|
+
"creating cached VoltAPI service client on %s for %j",
|
|
356
|
+
serviceAddress,
|
|
357
|
+
this._voltConfig
|
|
358
|
+
);
|
|
366
359
|
|
|
367
360
|
const serviceDescriptors = getBuiltInServiceDescriptors();
|
|
368
361
|
|
|
@@ -371,9 +364,7 @@ export function getVoltAPIClientInternal() {
|
|
|
371
364
|
serviceDescriptors,
|
|
372
365
|
serviceAddress,
|
|
373
366
|
this._credential,
|
|
374
|
-
this.isRelayed
|
|
375
|
-
this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
|
|
376
|
-
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
367
|
+
this.isRelayed
|
|
377
368
|
);
|
|
378
369
|
}
|
|
379
370
|
|
|
@@ -457,208 +448,3 @@ export function streamingCallInternal(methodType, method, request, service) {
|
|
|
457
448
|
|
|
458
449
|
return call;
|
|
459
450
|
}
|
|
460
|
-
|
|
461
|
-
export async function fetchVoltConfig(discovery_url) {
|
|
462
|
-
try {
|
|
463
|
-
const response = await fetch(discovery_url);
|
|
464
|
-
|
|
465
|
-
if (!response.ok) {
|
|
466
|
-
log("fetchVoltConfig - error [%s]", response.status);
|
|
467
|
-
return null;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
const fullVoltConfig = await response.json();
|
|
471
|
-
|
|
472
|
-
if (!fullVoltConfig || typeof fullVoltConfig !== "object") {
|
|
473
|
-
log("fetchVoltConfig - invalid config resolved from: %s", discovery_url);
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const voltConfig = pick(
|
|
478
|
-
fullVoltConfig,
|
|
479
|
-
"id",
|
|
480
|
-
"display_name",
|
|
481
|
-
"public_key",
|
|
482
|
-
"fingerprint",
|
|
483
|
-
"address",
|
|
484
|
-
"http_address",
|
|
485
|
-
"ca_pem",
|
|
486
|
-
"relay"
|
|
487
|
-
);
|
|
488
|
-
|
|
489
|
-
log("fetchVoltConfig from %s", discovery_url);
|
|
490
|
-
|
|
491
|
-
return voltConfig;
|
|
492
|
-
} catch (err) {
|
|
493
|
-
log("fetchVoltConfig - %s error [%s]", discovery_url, err.message);
|
|
494
|
-
return null;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
async function fetchVoltConfigFromDIDInternal(volt_did, registryUrl) {
|
|
499
|
-
try {
|
|
500
|
-
const didResolution = voltUtils.getDIDResolutionURL(volt_did, registryUrl);
|
|
501
|
-
if (!didResolution) {
|
|
502
|
-
throw new Error(`Invalid Volt DID: ${volt_did}`);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
const response = await fetch(didResolution);
|
|
506
|
-
|
|
507
|
-
if (!response.ok) {
|
|
508
|
-
throw new Error(`HTTP error, status: ${response.status}`);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
const didDocument = await response.json();
|
|
512
|
-
const voltConfigServices = findDIDDocumentService(
|
|
513
|
-
didDocument,
|
|
514
|
-
constants.didServiceType.voltConfig
|
|
515
|
-
);
|
|
516
|
-
if (voltConfigServices.length !== 1) {
|
|
517
|
-
log(
|
|
518
|
-
"Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
|
|
519
|
-
volt_did,
|
|
520
|
-
voltConfigServices.length
|
|
521
|
-
);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
log("resolved DID from %s", didResolution);
|
|
525
|
-
|
|
526
|
-
const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
|
|
527
|
-
const voltConfig = await fetchVoltConfig.call(this, serviceEndpoint);
|
|
528
|
-
if (!voltConfig) {
|
|
529
|
-
throw new Error(`failed to resolve Volt config from ${serviceEndpoint}`);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
return voltConfig;
|
|
533
|
-
} catch (err) {
|
|
534
|
-
throw new Error(
|
|
535
|
-
`Failure fetching Volt config from DID document ${volt_did}: ${err.message}`
|
|
536
|
-
);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
export async function fetchVoltConfigFromDID(volt_did, registryListIn) {
|
|
541
|
-
let registryList;
|
|
542
|
-
if (!registryListIn) {
|
|
543
|
-
// If no registry list is provided, use the default registry list.
|
|
544
|
-
registryList = constants.defaultRegistryList;
|
|
545
|
-
} else {
|
|
546
|
-
// If a registry list is provided, merge it with the default registry list.
|
|
547
|
-
registryList = Array.from(
|
|
548
|
-
new Set(registryListIn.concat(constants.defaultRegistryList))
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
let voltConfig;
|
|
553
|
-
for (let registryUrl of registryList) {
|
|
554
|
-
try {
|
|
555
|
-
voltConfig = await fetchVoltConfigFromDIDInternal.call(
|
|
556
|
-
this,
|
|
557
|
-
volt_did,
|
|
558
|
-
registryUrl
|
|
559
|
-
);
|
|
560
|
-
|
|
561
|
-
// If we've found a config, break out of the loop.
|
|
562
|
-
break;
|
|
563
|
-
} catch (err) {
|
|
564
|
-
log("unable to fetch Volt config from %s: %s", registryUrl, err.message);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
if (!voltConfig) {
|
|
569
|
-
log("failed to fetch Volt config for %s from any registry", volt_did);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
return voltConfig;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
export async function resolveVoltConfig(cfg, didRegistryList) {
|
|
576
|
-
// Start by resolving the relay if one is specified.
|
|
577
|
-
let relay = cfg?.relay?.http_address || cfg?.relay;
|
|
578
|
-
if (relay) {
|
|
579
|
-
relay = await resolveVoltConfig.call(this, relay, didRegistryList);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
let voltDID = "";
|
|
583
|
-
let voltHttpAddress = "";
|
|
584
|
-
|
|
585
|
-
if (typeof cfg === "string") {
|
|
586
|
-
// If the configuration is a string, determine if it is a DID or HTTP address.
|
|
587
|
-
if (cfg.indexOf("did:") === 0) {
|
|
588
|
-
voltDID = cfg;
|
|
589
|
-
} else {
|
|
590
|
-
voltHttpAddress = cfg;
|
|
591
|
-
}
|
|
592
|
-
cfg = {};
|
|
593
|
-
} else if (typeof cfg === "object") {
|
|
594
|
-
voltDID = cfg.id;
|
|
595
|
-
voltHttpAddress = cfg.http_address;
|
|
596
|
-
} else {
|
|
597
|
-
throw new Error("configuration is missing the 'volt' object");
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
let resolvedConfig;
|
|
601
|
-
|
|
602
|
-
if (relay) {
|
|
603
|
-
if (!voltDID) {
|
|
604
|
-
throw new Error("relay specified but no Volt DID provided");
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// Build the sub-domain URL for the Volt discovery service by inserting the volt DID into the relay URL.
|
|
608
|
-
const relayURL = relay.http_address;
|
|
609
|
-
const relayParts = relayURL.split("://");
|
|
610
|
-
const subdomain = voltDID.substring(constants.voltDIDPrefix.length);
|
|
611
|
-
voltHttpAddress = `${relayParts[0]}://${subdomain}.${relayParts[1]}`;
|
|
612
|
-
} else if (voltDID) {
|
|
613
|
-
resolvedConfig = await fetchVoltConfigFromDID.call(
|
|
614
|
-
this,
|
|
615
|
-
voltDID,
|
|
616
|
-
didRegistryList
|
|
617
|
-
);
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
if (!resolvedConfig && voltHttpAddress) {
|
|
621
|
-
try {
|
|
622
|
-
resolvedConfig = await fetchVoltConfig.call(
|
|
623
|
-
this,
|
|
624
|
-
`${voltHttpAddress}/discover`
|
|
625
|
-
);
|
|
626
|
-
|
|
627
|
-
// Update the http address in the resolved config to the one we used to
|
|
628
|
-
// fetch it, since we may have used the relay rather than local address.
|
|
629
|
-
resolvedConfig.http_address = voltHttpAddress;
|
|
630
|
-
} catch (err) {
|
|
631
|
-
log(
|
|
632
|
-
"failed to resolve Volt configuration %s [%s]",
|
|
633
|
-
voltHttpAddress,
|
|
634
|
-
err.message
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
if (!resolvedConfig) {
|
|
640
|
-
if (cfg.address && cfg.ca_pem) {
|
|
641
|
-
log(
|
|
642
|
-
"unable to resolve %s / %s, using existing configuration",
|
|
643
|
-
voltDID,
|
|
644
|
-
voltHttpAddress
|
|
645
|
-
);
|
|
646
|
-
} else {
|
|
647
|
-
throw new Error(
|
|
648
|
-
`failed to resolve Volt ${voltDID} / ${voltHttpAddress} and no existing configuration`
|
|
649
|
-
);
|
|
650
|
-
}
|
|
651
|
-
} else {
|
|
652
|
-
log("resolved config from: %s", voltDID || voltHttpAddress);
|
|
653
|
-
log(JSON.stringify(resolvedConfig, null, 2));
|
|
654
|
-
|
|
655
|
-
cfg = {
|
|
656
|
-
...cfg,
|
|
657
|
-
...resolvedConfig
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
cfg.relay = relay;
|
|
662
|
-
|
|
663
|
-
return cfg;
|
|
664
|
-
}
|
package/src/volt-client.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveVoltConfig } from "@tdxvolt/volt-utility";
|
|
1
2
|
import debug from "debug";
|
|
2
3
|
import EventEmitter from "events";
|
|
3
4
|
import fs from "fs";
|
|
@@ -7,7 +8,6 @@ import {
|
|
|
7
8
|
authenticateInternal,
|
|
8
9
|
connectInternal,
|
|
9
10
|
getVoltAPIClientInternal,
|
|
10
|
-
resolveVoltConfig,
|
|
11
11
|
streamingCallInternal,
|
|
12
12
|
unaryCallInternal
|
|
13
13
|
} from "./volt-client-internal.js";
|
|
@@ -86,10 +86,19 @@ export class VoltClient extends EventEmitter {
|
|
|
86
86
|
* instead of a DID-based authentication session.
|
|
87
87
|
* @param {string} [options.exchangeToken] - optional token to exchange in return for an authenticated session.
|
|
88
88
|
* @param {boolean} [options.waitForAuth] - whether to block and poll on 'prompt' authenticate decisions. Default is true.
|
|
89
|
+
* @param {boolean} [options.noResolve] - whether to skip resolving the Volt configuration. Default is false.
|
|
89
90
|
*/
|
|
90
91
|
async initialise(config, options = {}) {
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
log("initialise ", config, options);
|
|
93
|
+
|
|
94
|
+
const {
|
|
95
|
+
ownDID,
|
|
96
|
+
noDID,
|
|
97
|
+
didRegistryList,
|
|
98
|
+
exchangeToken,
|
|
99
|
+
extraConfig,
|
|
100
|
+
noResolve
|
|
101
|
+
} = options;
|
|
93
102
|
const waitForAuth =
|
|
94
103
|
options.waitForAuth === undefined ? true : !!options.waitForAuth;
|
|
95
104
|
|
|
@@ -139,14 +148,17 @@ export class VoltClient extends EventEmitter {
|
|
|
139
148
|
throw new Error("client_name property required in config");
|
|
140
149
|
}
|
|
141
150
|
|
|
142
|
-
|
|
143
|
-
this
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
151
|
+
if (!noResolve) {
|
|
152
|
+
this._config.volt = await resolveVoltConfig(
|
|
153
|
+
this._config.volt,
|
|
154
|
+
didRegistryList
|
|
155
|
+
);
|
|
156
|
+
}
|
|
147
157
|
|
|
148
158
|
this._voltConfig = this._config.volt;
|
|
149
159
|
|
|
160
|
+
log("config is ", this._voltConfig);
|
|
161
|
+
|
|
150
162
|
if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
|
|
151
163
|
throw new Error("'id' property is missing in Volt configuration");
|
|
152
164
|
}
|
|
@@ -204,7 +216,7 @@ export class VoltClient extends EventEmitter {
|
|
|
204
216
|
this._credential.saveCache();
|
|
205
217
|
}
|
|
206
218
|
|
|
207
|
-
return Promise.resolve(this.
|
|
219
|
+
return Promise.resolve(this.credential.config);
|
|
208
220
|
} catch (err) {
|
|
209
221
|
log("failure starting Volt client [%s]", err.message);
|
|
210
222
|
throw err;
|
package/src/volt-connection.js
CHANGED
|
@@ -20,6 +20,7 @@ function _cleanUp(immediateReconnect) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
if (this._dying) {
|
|
23
|
+
log("dying - clearing reconnect timer");
|
|
23
24
|
clearTimeout(this._reconnectTimer);
|
|
24
25
|
} else if (immediateReconnect || (this._autoRetry && !this._reconnectTimer)) {
|
|
25
26
|
this._reconnectTimer = setTimeout(
|
|
@@ -32,6 +33,7 @@ function _cleanUp(immediateReconnect) {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
if (this.call?.writable) {
|
|
36
|
+
log("ending Connect call");
|
|
35
37
|
this.call.end();
|
|
36
38
|
}
|
|
37
39
|
}
|
|
@@ -122,9 +124,7 @@ function _doConnect(connectHello) {
|
|
|
122
124
|
});
|
|
123
125
|
|
|
124
126
|
this.call.on("end", () => {
|
|
125
|
-
|
|
126
|
-
this.emit("connected", false);
|
|
127
|
-
}
|
|
127
|
+
this.emit("connected", false);
|
|
128
128
|
_cleanUp.call(this);
|
|
129
129
|
});
|
|
130
130
|
|
|
@@ -718,128 +718,6 @@ message RemoteResponse {
|
|
|
718
718
|
HttpRequest http_request = 5;
|
|
719
719
|
}
|
|
720
720
|
}
|
|
721
|
-
`;
|
|
722
|
-
export const spark_api = `syntax = "proto3";
|
|
723
|
-
|
|
724
|
-
package tdx.volt_api.volt.v1;
|
|
725
|
-
|
|
726
|
-
import "tdx/volt_api/volt/v1/status.proto";
|
|
727
|
-
|
|
728
|
-
// The Spark API allows clients to start and communicate with Spark VMs.
|
|
729
|
-
service SparkAPI {
|
|
730
|
-
// Start or connect to a spark VM and establish a long-lived communication channel.
|
|
731
|
-
rpc Spark(stream SparkRequest) returns (stream SparkResponse);
|
|
732
|
-
|
|
733
|
-
// Get details of running spark VMs.
|
|
734
|
-
rpc GetSpark(GetSparkRequest) returns (GetSparkResponse);
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
// Start a resource-hosted spark.
|
|
738
|
-
// Provenance is determined from the resource owner.
|
|
739
|
-
message StartSparkResource {
|
|
740
|
-
// The id of the resource containing the VM byte code.
|
|
741
|
-
string resource_id = 1;
|
|
742
|
-
|
|
743
|
-
// JSON-encoded payload to parameterise the spark.
|
|
744
|
-
string payload = 2;
|
|
745
|
-
|
|
746
|
-
// Envelope signature.
|
|
747
|
-
string signature = 3;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
// Start a spark from byte code included in the payload, along with issuer/developer claims and signature.
|
|
751
|
-
message StartSparkAdhoc {
|
|
752
|
-
// Ad-hoc byte code of VM to run.
|
|
753
|
-
bytes byte_code = 1;
|
|
754
|
-
|
|
755
|
-
// Verifiable credentials associated with this spark.
|
|
756
|
-
repeated string credential = 2;
|
|
757
|
-
|
|
758
|
-
// Envelope signature.
|
|
759
|
-
string signature = 3;
|
|
760
|
-
|
|
761
|
-
// JSON-encoded payload to parameterise the spark.
|
|
762
|
-
string payload = 4;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
// Send commands and payloads to communicate with a running spark.
|
|
766
|
-
message SparkCommand {
|
|
767
|
-
// Optional id to associate with this command.
|
|
768
|
-
// If this is omitted, an id will be assigned and returned in the command acknowledgement.
|
|
769
|
-
string command_id = 1;
|
|
770
|
-
|
|
771
|
-
// The command to send to the spark.
|
|
772
|
-
string command = 2;
|
|
773
|
-
|
|
774
|
-
// The command payload.
|
|
775
|
-
string payload = 3;
|
|
776
|
-
|
|
777
|
-
// Command verifiable credential.
|
|
778
|
-
string credential = 4;
|
|
779
|
-
|
|
780
|
-
// Command signature.
|
|
781
|
-
string signature = 5;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
// The request must contain one of the messages indicated below.
|
|
785
|
-
message SparkRequest {
|
|
786
|
-
oneof payload {
|
|
787
|
-
StartSparkResource start_resource = 1;
|
|
788
|
-
StartSparkAdhoc start_adhoc = 2;
|
|
789
|
-
SparkCommand command = 3;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// Receive events and payload from a running spark.
|
|
794
|
-
message SparkData {
|
|
795
|
-
// The command id to which this data corresponds.
|
|
796
|
-
string command_id = 1;
|
|
797
|
-
|
|
798
|
-
oneof payload {
|
|
799
|
-
// Acknowledges receipt of a spark command.
|
|
800
|
-
bool acknowledge = 2;
|
|
801
|
-
|
|
802
|
-
// Indicates the command has completed.
|
|
803
|
-
bool ended = 3;
|
|
804
|
-
|
|
805
|
-
// Will contain error details on command failure.
|
|
806
|
-
string error = 4;
|
|
807
|
-
|
|
808
|
-
// Data emitted by a command, usually in stringified JSON format.
|
|
809
|
-
string data = 5;
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
// Details of a spark instance.
|
|
814
|
-
message SparkInstance {
|
|
815
|
-
// The UUID assigned to the spark instance.
|
|
816
|
-
string spark_id = 1;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// The response will contain one of the messages indicated below.
|
|
820
|
-
message SparkResponse {
|
|
821
|
-
oneof payload {
|
|
822
|
-
// Details of any error that occurred on the call.
|
|
823
|
-
tdx.volt_api.volt.v1.Status status = 1;
|
|
824
|
-
|
|
825
|
-
// Details of the spark instance, sent in response to a start command.
|
|
826
|
-
SparkInstance spark_instance = 2;
|
|
827
|
-
|
|
828
|
-
// Payload data emitted by the spark.
|
|
829
|
-
SparkData spark_data = 3;
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
message GetSparkRequest {
|
|
834
|
-
string spark_id = 1;
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
// One of the following fields will be present in the response.
|
|
838
|
-
message GetSparkResponse {
|
|
839
|
-
// Details of any error that occurred on the call.
|
|
840
|
-
tdx.volt_api.volt.v1.Status status = 1;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
721
|
`;
|
|
844
722
|
export const ssi = `syntax = "proto3";
|
|
845
723
|
|
|
@@ -915,12 +793,11 @@ export const ssi_api = `syntax = "proto3";
|
|
|
915
793
|
|
|
916
794
|
package tdx.volt_api.volt.v1;
|
|
917
795
|
|
|
918
|
-
import "tdx/volt_api/volt/v1/status.proto";
|
|
919
796
|
import "tdx/volt_api/volt/v1/ssi.proto";
|
|
797
|
+
import "tdx/volt_api/volt/v1/status.proto";
|
|
920
798
|
|
|
921
799
|
service SsiAPI {
|
|
922
|
-
|
|
923
|
-
rpc CreateExchangeToken(CreateExchangeTokenRequest) returns (CreateExchangeTokenResponse);
|
|
800
|
+
rpc CacheEmailCredential(CacheEmailCredentialRequest) returns (CacheEmailCredentialResponse);
|
|
924
801
|
|
|
925
802
|
// Delete a DID document or all DID documents originating from a given Volt.
|
|
926
803
|
// Requires \`volt:delete-did\` API privilege.
|
|
@@ -951,20 +828,25 @@ service SsiAPI {
|
|
|
951
828
|
rpc SearchDIDRegistry(SearchDIDRegistryRequest) returns (SearchDIDRegistryResponse);
|
|
952
829
|
}
|
|
953
830
|
|
|
954
|
-
message
|
|
955
|
-
|
|
956
|
-
|
|
831
|
+
message CacheEmailCredentialRequest {
|
|
832
|
+
string identity_did = 1;
|
|
833
|
+
|
|
834
|
+
string email_address = 2;
|
|
835
|
+
|
|
836
|
+
string identity_provider = 3;
|
|
837
|
+
|
|
838
|
+
string key_passphrase = 4;
|
|
957
839
|
|
|
958
840
|
// The time to live for the token, in seconds.
|
|
959
|
-
uint32 ttl =
|
|
841
|
+
uint32 ttl = 5;
|
|
960
842
|
}
|
|
961
843
|
|
|
962
|
-
message
|
|
844
|
+
message CacheEmailCredentialResponse {
|
|
963
845
|
// Details of any error that occurred on the call.
|
|
964
846
|
tdx.volt_api.volt.v1.Status status = 1;
|
|
965
847
|
|
|
966
|
-
// The
|
|
967
|
-
string
|
|
848
|
+
// The verifiable credential resource id.
|
|
849
|
+
string vc_id = 2;
|
|
968
850
|
}
|
|
969
851
|
|
|
970
852
|
message DeleteDIDRequest {
|
|
@@ -1181,6 +1063,7 @@ enum SyncState {
|
|
|
1181
1063
|
SYNC_STATE_SYNCING = 1;
|
|
1182
1064
|
SYNC_STATE_DONE = 2;
|
|
1183
1065
|
SYNC_STATE_UPDATE = 3;
|
|
1066
|
+
SYNC_STATE_AWARENESS = 4;
|
|
1184
1067
|
}
|
|
1185
1068
|
|
|
1186
1069
|
message SyncDocumentStart {
|
|
@@ -1249,7 +1132,8 @@ message GetDocumentJSONResponse {
|
|
|
1249
1132
|
// The current JSON snapshot of the document
|
|
1250
1133
|
string json = 2;
|
|
1251
1134
|
}
|
|
1252
|
-
}
|
|
1135
|
+
}
|
|
1136
|
+
`;
|
|
1253
1137
|
export const terminal_api = `syntax = "proto3";
|
|
1254
1138
|
|
|
1255
1139
|
package tdx.volt_api.volt.v1;
|
|
@@ -2164,7 +2048,7 @@ message AuthenticateRequest {
|
|
|
2164
2048
|
// A human-readable name of the entity requesting to authenticate.
|
|
2165
2049
|
string client_name = 6;
|
|
2166
2050
|
|
|
2167
|
-
// Optional challenge code, hashed using SHA-256 and base64 encoded.
|
|
2051
|
+
// Optional challenge code, hashed using SHA-256 and base64 URL encoded (no padding).
|
|
2168
2052
|
string challenge = 7;
|
|
2169
2053
|
|
|
2170
2054
|
// The host name to add as a SAN to the issued certificate.
|
|
@@ -2192,7 +2076,7 @@ message AuthenticateResponse {
|
|
|
2192
2076
|
// This should be persisted by the client, and submitted with all subsequent API calls as part of the call metadata.
|
|
2193
2077
|
string session_id = 2;
|
|
2194
2078
|
|
|
2195
|
-
// The identity
|
|
2079
|
+
// The 'primary' session identity assigned to this authentication. This will be the DID in DID-based sessions, otherwise it will be the first email-based credential found, if any, otherwise it will be the public key fingerprint.
|
|
2196
2080
|
string identity_did = 3;
|
|
2197
2081
|
|
|
2198
2082
|
// A certificate issued by the volt CA, binding the request public key to the identity.
|
|
@@ -2248,6 +2132,9 @@ message CanAccessResourceResponse {
|
|
|
2248
2132
|
|
|
2249
2133
|
// The policy decision for this access request.
|
|
2250
2134
|
PolicyDecision decision = 6;
|
|
2135
|
+
|
|
2136
|
+
// The session that the access relates to.
|
|
2137
|
+
string session_id = 7;
|
|
2251
2138
|
}
|
|
2252
2139
|
|
|
2253
2140
|
message CheckCompatibilityRequest {
|
|
@@ -2593,6 +2480,9 @@ message GetIdentityResponse {
|
|
|
2593
2480
|
message GetOneTimeTokenRequest {
|
|
2594
2481
|
// Optional token TTL, in seconds. Default is 10 seconds.
|
|
2595
2482
|
int32 ttl = 1;
|
|
2483
|
+
|
|
2484
|
+
// Optional token context.
|
|
2485
|
+
string context = 2;
|
|
2596
2486
|
}
|
|
2597
2487
|
|
|
2598
2488
|
message GetOneTimeTokenResponse {
|
|
@@ -2655,6 +2545,9 @@ message GetResourceResponse {
|
|
|
2655
2545
|
message GetResourcesRequest {
|
|
2656
2546
|
repeated string id = 1;
|
|
2657
2547
|
|
|
2548
|
+
// Wildcards permitted.
|
|
2549
|
+
repeated string alias = 16;
|
|
2550
|
+
|
|
2658
2551
|
// Wildcards permitted.
|
|
2659
2552
|
repeated string name = 2;
|
|
2660
2553
|
|
|
@@ -3171,4 +3064,4 @@ message SubscribeWireResponse {
|
|
|
3171
3064
|
}
|
|
3172
3065
|
|
|
3173
3066
|
`;
|
|
3174
|
-
export const voltProtos = [sqlite, sqlite_database_api, sqlite_server_api, proto_db_sync, proxy_api, relay_api, discovery_api, file, file_api, remote,
|
|
3067
|
+
export const voltProtos = [sqlite, sqlite_database_api, sqlite_server_api, proto_db_sync, proxy_api, relay_api, discovery_api, file, file_api, remote, ssi, ssi_api, status, sync_api, terminal_api, volt, volt_api, wire_api];
|