Web client

There are two means of accessing the tdx Volt from web applications, via the HTTP interface or using the websocket API.

The websocket API is recommended because it maps well to the grpc streaming modes.

HTTP interface

The HTTP interface is a work in progress. The tdx Volt API is accessible using pure REST requests, but a javascript library that packages this up is not fully implemented yet.

Websocket API

The websocket API is currently more polished than the HTTP interface.

Begin by installing the @tdxvolt/volt-client-web package:

Terminal window
npm install @tdxvolt/volt-client-web

Configuration

In order to be able to initialise a connection to a Volt, you will need to obtain a client configuration.

How your web application obtains or stores the client configuration is not discussed here, needless to say you need to be careful not to expose keys or other sensitive data. For the purposes of this document, we assume the configuration is obtained via some out-of-band means and stored in the browser `localStorage`. It holds the key in encrypted form. The web application will then prompt the user to enter the key passphrase in order to unlock it.

Example client connection object, obtained from the fusebox identity dialog:

const BobsConfig = {
volt: {
id: "did:volt:4801dd12-410d-46d4-84f8-efb0ba609085",
display_name: "Alice",
address: "192.168.1.195:52991",
http_address: "http://192.168.1.195:52992",
public_key:
"-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA4Y+OuHQWe2P36Vh/8NptLxBexYPO3swzJUGSO43NYD4=\n-----END PUBLIC KEY-----\n",
},
client_name: "Bob",
credential: {
session_id: "e9bb9bb7-63be-48ab-961d-8493f78d73db",
identity_did: "did:volt:fb493b5d-8a7a-44c5-930b-8f24b0cf8104",
key: "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIIbW3ocYXetyUm/e6Hc3lV01ZRRP/Q8vdup/HD6uaQdi\n-----END PRIVATE KEY-----\n",
},
};

Initialisation

Create a tdx Volt instance to enable Bob to connect to the Volt:

import { VoltClient } from "@tdxvolt/volt-client-web";
const client = new VoltClient(window.WebSocket, BobsConfig);

Now attempt to initialise the tdx Volt connection:

await client.initialise();

If initialisation fails, an error will be thrown.

Once initialised successfully, you can issue any API call.

Refer to the API documentation for details of the method names and corresponding request and response messages. Each API method expects a JSON object representing the request(s) as input, and returns JSON objects as indicated by the response message type. The JSON representation of the protobuf message format is intuitive.

Unary calls

All unary calls return a promise that will either resolve with a JSON response object matching that defined by the API protobuf, or reject if there is a problem with the grpc transport or an error status on the response object.

In the example below, we retrieve the resource associated with Bob’s home folder:

client
.initialise()
.then(() => {
return client.GetResource({ resourceId: config.credential.identity_did });
})
.then((response) => {
console.log(response.resource);
})
.catch((err) => {
console.error(err.message);
});

Streaming calls

The promise model does not fit well with streaming calls since they are long-lived.

For this reason, all streaming calls (client streaming, server streaming, and bi-directional streaming) return a call object that is used to manage the call.

The call object emits events as interactions with the underlying websocket occur, and there are 3 events of interest:

  • “error” emitted when an error occurs on the call
  • “data” emitted when a response is received from the Volt
  • “end” emitted when the call ends

An example of a streaming call is shown below, where we subscribe to the @wire wire:

client
.initialise()
.then(() => {
const subscribe = client.SubscribeWire({ wire_id: "@wire" });
subscribe.on("data", (data) => {
console.log(data);
});
})
.catch((err) => {
console.error(err.message);
});