Command line interface

Although not strictly a client library as such, the tdx Volt command line interface (CLI) exposes several aspects of the core tdx Volt API for use in shell scripts and other terminal-based processes. This, in combination with the tdx Volt wire functionality, provides opportunities for useful and succinct workflows.

The tdx Volt CLI can also act as a server, and can be daemonised to run one or more Volts in the background.

The tdx Volt CLI supports several commands, and the arguments and syntax of each command varies. The basic format to invoke a command is:

./volt <command> [options]

Help is available for each command using the -h or --help switch, for example:

Terminal window
./volt create --help

tdx Volt management commands

The commands in this section relate to tdx Volt management tasks, such as creating and running Volts.

battery options

There are some options that are common to the tdx Volt management commands, including those to do with configuration of the Battery in which the tdx Volt is contained.

The Battery name can be configured using the -b switch. If this switch is omitted, the default Battery is used.

For example, to create a tdx Volt in a Battery named Household:

Terminal window
./volt create Alice -b Household

Use the --bp switch to specify an encryption passphrase and create a secure Battery:

Terminal window
./volt create Alice -b Household --bp foobar

As above, but using --bp . to request a passphrase prompt:

Terminal window
./volt create Alice -b Household --bp .

Note that once an Battery is encrypted, the passphrase will be required for any subsequent tdx Volt command. For example to start a tdx Volt that is stored in a passphrase-protected Battery:

./volt run 449a3385-f380-41f7-bd0a-e60caaa403cb -bp foobar

create command

The command to create a Volt.

In the following example, the tdx Volt will be created in the default Battery, with no passphrase protection or encryption on either the Battery storage or the tdx Volt itself.

Terminal window
./volt create "Alice's laptop"

To avoid having to copy and paste the UUID when subsequently referring to the tdx Volt, you can give it an alias using the -a switch:

Terminal window
./volt create "Alice's laptop" -a alice

The alias can be substituted for the UUID, by prefixing it with @:

Terminal window
./volt run @alice

Create a secure Volt

Use the -p switch to specify a passphrase that will be used to encrypt the tdx Volt root key and storage.

By default, the tdx Volt will auto-generate a new key at creation and encrypt it with the given passphrase. The tdx Volt will not store the passphrase so you must remember it.

It is recommended to use a phrase rather than a single word for the passphrase, for example "I like cheese".

Terminal window
./volt create "Alice's laptop" -p secret

Use a period with the passphrase switch (-p .) to force the CLI to prompt for the passphrase rather than include it explicitly in the command line.

Terminal window
./volt create "Alice's laptop" -p .

There is currently no way to recover or reset the **tdx Volt** passphrase so if you lose it you will not be able to access the Volt.

Create a tdx Volt using a fixed host

By default when a tdx Volt is created its certificate is bound to the current (or first) ipv4 network interface address. If you would like to bind a tdx Volt to a specific IP address or domain name use the host and port command line switches.

Terminal window
./volt create "Alice's server" --host aliceserver.com --port 40725

Create a tdx Volt with a file-based key

By default the tdx Volt key is stored with the tdx Volt configuration in the Battery. If the Battery is encrypted with a strong passphrase this is a fairly safe option.

Alternatively you can create a tdx Volt using a key stored on the local file system.

Terminal window
./volt create "Alice's laptop" -k /path/to/key/file

If /path/to/key/file does not exist it will be created.

If the key is encrypted (recommended) you can specify or prompt for the passphrase using the -p switch as described above.

Terminal window
./volt create "Alice's laptop" -k /path/to/key/file -p .

See the key strategy section for more information.

For full usage information:

Terminal window
./volt create --help

run command

Use the run command to start a Volt.

This command can be used to daemonise an instance of a Volt, using an init.d script or similar.

To run a tdx Volt that is in the default Battery and is not passphrase-protected:

Terminal window
./volt run f902f0f7-b9b7-4d2a-b05a-2d4e76a16ded

To run a passphrase-protected Volt, use the -p . option to prompt for a passphrase:

Terminal window
./volt run f902f0f7-b9b7-4d2a-b05a-2d4e76a16ded -p .

If the tdx Volt is not in the default Battery, specify the path to the Battery using the -l option:

Terminal window
./volt run f902f0f7-b9b7-4d2a-b05a-2d4e76a16ded -p . -b Household

For full usage information:

Terminal window
./volt run --help

config command

The config command can be used to show the available tdx Volt configurations, or see the detail of a particular tdx Volt configuration.

To list all Volts in the default Battery:

Terminal window
./volt config

To list all Volts in a given Battery:

Terminal window
./volt config -b Household

To view the full configuration for a given Volt, specify the tdx Volt id or alias:

Terminal window
./volt config @alice

For full usage information:

Terminal window
./volt config --help

Client commands

The commands in this section are classed as tdx Volt client commands, and as such they require client credentials in order to authenticate with the target Volt.

There are two ways to specify the client credentials, either via a well-known file named volt.config.json, or using a custom file name and passing it via the command line switch -c.

See the connection section for more details about client credentials.

The following examples assume that the client credentials have been placed in the volt.config.json file.

download command

Download a file or folder from the tdx Volt to the local disk:

Terminal window
./volt download ./shares/images volt-downloads

In the example above, the . prefix of ./shares/images indicates that the path is relative the ‘home’ folder of the client.

To specify a path relative to the root of the tdx Volt itself, use a / prefix:

Terminal window
./volt download /documents/reports volt-downloads

For full usage information:

Terminal window
./volt download --help

upload command

Upload a file or folder to the Volt.

Terminal window
./volt upload ~/Documents/images ./shares

The . prefix on ./shares indicates the target resource is relative to the ‘home’ folder of the client.

To upload to a resource relative to the root of the tdx Volt itself, use a / prefix:

Terminal window
./volt upload ~/Documents/images /documents/images

For full usage information:

Terminal window
./volt upload --help

list command

List resources on the tdx Volt (similar to ls or dir shell commands).

Terminal window
./volt list ./shares

For full usage information:

Terminal window
./volt list --help

wire command

Subscribe or publish to wire resources.

publish

To publish to a wire, specify the wire identifier or alias:

Terminal window
./volt wire @sensor-feed

The wire data is read from STDIN. This enables flows such as:

Terminal window
curl www.google.com | ./volt wire bcc778cd-04b0-4e5e-803c-dcca143790e1
cat somefile | ./volt wire bcc778cd-04b0-4e5e-803c-dcca143790e1

subscribe

To subscribe to a wire, add the -s switch to the command line:

Terminal window
./volt wire 9c797959-eafe-4d2a-8005-1e5ef66c29ac -s

All wire data will be written to STDOUT. This enables flows such as:

Terminal window
./volt wire -s bcc778cd-04b0-4e5e-803c-dcca143790e1 | wire-data.log

For full usage information:

Terminal window
./volt wire --help

Utility commands

logger command

Split data arriving on a wire or STDIN into files on the local disk.

This command was developed for use with the Protobuf Database Sync utility, but it may be useful in other scenarios.

The logger command listens for incoming data on a Volt wire or STDIN, splitting the input into multiple files of a configured maximum size in a configured folder. It makes no attempt to interpret the incoming data.

If a header is given as part of the tdx Volt Logger configuration it will populate and write a ProtobufSyncConfigurationHeader message to the start of each file it creates.

Once a file reaches the configured size (logFileSize) it will be moved to the ingestion folder, where it will get picked up by the protoDbSync process if one is running. The logger will then create a new file to continue logging the incoming data.

This enables scenarios such as:

Terminal window
# Continuously pipe data from a producer, splitting it into log files.
> fs20PacketProducer | volt logger -c fs20.logger.json
Terminal window
# Continuously pipe data from a tdx Volt Wire (grpc stream), splitting it into log files.
> volt logger -c zwave.logger.json -w d1cc1560-9b58-4bdf-a2cf-7d65a2db0c01
Terminal window
# Load data from a URL.
> curl https://somedata.endpoint | volt logger -c snapshot.logger.json

An example logger configuration when reading from STDIN is shown below.

{
"logger": {
"headerId": "tcpdump-logger",
"headers": [
{
"messageName": "TCPDumpPacket",
"messageProto": "syntax = \"proto3\";\n\npackage tranforms;\n\nmessage TCPDumpPacket {\n string timestamp = 1;\n string source_mac_address = 2;\n string source_manufacturer_id = 3;\n bool is_broadcast = 4;\n bool is_arp = 5;\n string target_mac_address = 6;\n string target_manufacturer_id = 7;\n string ether_type = 8;\n string unknown_1 = 9;\n int32 length = 10;\n string source_address = 11;\n string target_address = 12;\n string payload = 13;\n}\n",
"name": "header0",
"tableName": "tcpdump"
}
],
"logFileExtension": "pdat",
"logFilePath": "./logs",
"logFilePrefix": "tcpdump-log-",
"logFileSize": 64000
}
}

If reading from a wire, the configuration should include details of a standard client connection, as shown below. The credentials given should have subscribe access to the source wire.

{
"logger": {
"headerId": "tcpdump-logger",
"headers": [
{
"messageName": "TCPDumpPacket",
"messageProto": "syntax = \"proto3\";\n\npackage tranforms;\n\nmessage TCPDumpPacket {\n string timestamp = 1;\n string source_mac_address = 2;\n string source_manufacturer_id = 3;\n bool is_broadcast = 4;\n bool is_arp = 5;\n string target_mac_address = 6;\n string target_manufacturer_id = 7;\n string ether_type = 8;\n string unknown_1 = 9;\n int32 length = 10;\n string source_address = 11;\n string target_address = 12;\n string payload = 13;\n}\n",
"name": "header0",
"tableName": "tcpdump"
}
],
"logFileExtension": "pdat",
"logFilePath": "./logs",
"logFilePrefix": "tcpdump-log-",
"logFileSize": 64000
},
"client_name": "CLI",
"crypto": {
"cert": "-----BEGIN CERTIFICATE-----\nMIIDWzCCAkOgAwIBAgIEQ7J4qTANBgkqhkiG9w0BAQsFADBxMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMTAwLgYDVQQDDCdjYS4zODAzYzIzOS1mZmM2LTQwYTMtYjg1Yy0xZGE3OTg5\nNDVjM2MwHhcNMjIxMDI1MDcyNDM2WhcNMjMxMDI1MDcyNDM3WjBuMQswCQYDVQQG\nEwJHQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWlu\nZHMgTHRkMS0wKwYDVQQDDCQ1NzZkM2YwNy0wYWFiLTRjZWQtYjEwZi0wODUyMjBj\nMmIxZmYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmVwOOkfIN95Sr\nmqKGPUTNdfFbuz7R0SIXzTuAI+tOiBmhH3QRadSOq58/3OgozW/xJ1jaiSGJKmi+\nMm44dNsCdi5O5HJboBER7A6FKn+R0YWJGmeT9jfzpK/ygUfF2qvRZ622odSVUm+m\n04DP1ZJya22wy9Y3SNvpLI1BqGsWUdjuSyzog246Tt0Dt3OC+VvG3xWCPJdgXyvq\nB1FuLm6l6F60soo+MJ6lWEcZ/oXUdeqTEac56+H8c6ol5raru0oJlawA6vXCfN6h\nRx+VjeFhSBBSIa7b99+7yBLa7xLzpX/Eb2mccUTMSenHG/TxnW5ftbzrXKLEhlDx\n0/SFRW6dAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAKbVpTl+ZIHgSSzOntuo9Ezd\nu3mKx/xWS+RPmXnHU9KsZjCJN00F84pf/a7SNlUJv/Mwob8gTpawVByoFjl6iV7o\nZJc+1op9aYHlmrqumCXairY//dlsGXJ2F7Wl2dqckn+Wqk4Jgk83NucAl5JgxLmR\nB9UwL81pzeXHjSPkaF17BzAxB42fIpDJ1Uies/4BjjwFWn2ebSyC/z3flB2YBF3c\nS1Gak8I56iyTCdjT0LkUoMVHNxFXg6xsorbBN4qy5CM5V5rbTJmZBYzZNIlTusBA\ngqPDsgRCNYGxsbDVB71thOSKzR0mltx2ICqV360MET0II8P3WYS4o7aFegk3OIY=\n-----END CERTIFICATE-----\n",
"client_id": "576d3f07-0aab-4ced-b10f-085220c2b1ff",
"key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCmVwOOkfIN95Sr\nmqKGPUTNdfFbuz7R0SIXzTuAI+tOiBmhH3QRadSOq58/3OgozW/xJ1jaiSGJKmi+\nMm44dNsCdi5O5HJboBER7A6FKn+R0YWJGmeT9jfzpK/ygUfF2qvRZ622odSVUm+m\n04DP1ZJya22wy9Y3SNvpLI1BqGsWUdjuSyzog246Tt0Dt3OC+VvG3xWCPJdgXyvq\nB1FuLm6l6F60soo+MJ6lWEcZ/oXUdeqTEac56+H8c6ol5raru0oJlawA6vXCfN6h\nRx+VjeFhSBBSIa7b99+7yBLa7xLzpX/Eb2mccUTMSenHG/TxnW5ftbzrXKLEhlDx\n0/SFRW6dAgMBAAECggEBAIdTp1soNWtTjyqFZdAcrIsTd0cP8S22HSyMFepMTrXX\nWDKTalR4ayufSLImQOJhML9bKZixlA0J6alDUhSwTTWVfFtG1BrjAMA58h440wyJ\nD0DZsIbZ++9GframowO+waZd2SAKTO8m4BszW0q7EKfx6o25aBAWQINVcZ6HLIg8\nwphpPaZJzMZCnSssAqA26x+yHzyiGBwECuKM382+jjTcasnxh5zh7JvxtWyOXjQM\n3EpCFLlGKCz2UWAxDThoyCqGVIVtkBR2b230TB5mjeI1slDy2iKHDfYXy+AWcDku\naYfQBLo96Fr2jPIBOkRXv91KMsyzNukrYwZzeRrN38ECgYEA0Uukd1F/8Vg0Ie9e\nHh5kXFOn9cB3CU+v9djF9LYxjrw3h/E74AT4ZuPl1PRyDXE6OUWWa7ouH0NhmBUU\nz7U6tlRdeW65S1aUoCkRzGB1nU9LQ0tz3lUKQ3P6SMusqErbJdLiCkXBCapTYr8n\nfliRwKU4CKf4wdH32hRlRs4CaLUCgYEAy3V2HnutXXteDvhhS5PqjVF8BPYq5h84\n5p70bsnCwkz9bDC7WJQwU6b9a4nofWhp3uGwSG/fji5MeRJzSf05Y5KUeZ0Lcvcn\nhGKpOGwE9BDlEedSAchYl6zdTrjo78ZOZ7QHjiuH0dgEc7A+E6S49Z6OaZtos37r\nKx6CAtVaJ0kCgYBT5p7ntiQz+8fqUqrIKCbTXDXYrm8JrIg9Zcj1cJQtRAZ+2JXI\nGDX8CR/5XoTaHqnYi6zhQqF6puhYrxrIqT4AGZHfPCPLr8mk6tHXvFNp3H+vWm/4\nkN6sa7HJvNxaGqf/Yap7s7rOxRjoXPjYDWlgcNslnTB7glB5e/OdjrgogQKBgD/6\nL6pmOZ2rrWgHspCRcq/9b4If5l12c+4RDcvIpfVzQD1FFaRE9O1ZFVc7hl/o9WGg\nlk4w35tV87YelyIs/l7RON8FAxSjo0l9vLiBPw2AQofetWraFQGc2fpnKtg7A2yY\nr8eE6LCTvNKkGOEUaxTRRvbuZ34J6ukkLr21WSQBAoGAeZJfkWCeX+14Qorb48Oq\nS32hYEzxYj2v3/4xUUqh9x9l1OsIUXWsc4c0Ya/D9bnoC3DQZFbwI7dtjhXRfwqj\nXSpFJv7+xq67hEnL2L5Y9s+HslpnM7uko+1bqrkMQ+XWcVSgqnWgQSPsbFia/FdL\npq+eQLE2/TcqWaM7hCWxjoc=\n-----END PRIVATE KEY-----\n"
},
"volt": {
"ca_pem": "-----BEGIN CERTIFICATE-----\nMIIDojCCAoqgAwIBAgIENzfzHjANBgkqhkiG9w0BAQsFADBxMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMTAwLgYDVQQDDCdjYS4zODAzYzIzOS1mZmM2LTQwYTMtYjg1Yy0xZGE3OTg5\nNDVjM2MwHhcNMjIxMDA2MDY0MTQ5WhcNMjMxMDA2MDY0MTUwWjBxMQswCQYDVQQG\nEwJHQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWlu\nZHMgTHRkMTAwLgYDVQQDDCdjYS4zODAzYzIzOS1mZmM2LTQwYTMtYjg1Yy0xZGE3\nOTg5NDVjM2MwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC685OWXJgG\ndBcmOFd0bKGBbH+JCVscnL2UahdO2i1exxVT0buVhI5gsATbvoiYShz8Vqi5H+EX\nTACSBO1ojDMRT+tLvqx61vqFvwpA5iNcN2IpnA72pK5hklS5MBiUQ/R02kqCIX3D\nz0F2ZsWuGo2TWsMGrcCkR8a/1oCLwTa3oO9JZhXckzrYiQZyWWtRG7vmJXz1Enrh\nZ0pXPGa94H7Hrm/q+J6Uy5c1r/sRK5pJH79Z+sTZuM71ooGbMScKfVx/+bd70MWB\nQK1o1vMUjEBACzizietpLXgT8xBYw2QwMGiMZsj4LKiYvDiUrniL5yQlmHTIWI5g\n9v3soaZFp4LRAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD\nAgEGMB0GA1UdDgQWBBQaa1NBh69uMVMOsXKBA4cR/h57HTANBgkqhkiG9w0BAQsF\nAAOCAQEAteNPjx7+9SsnSOs+LE9DdbyvcOOpqbcfSH6PDMyQoNs5o+K+yCTe2GOy\nkB4BMJQq3d+I17ivisQI1/bS6Pn364aO8B+9KeWCQq5MteoLJF4pscM5+R3sxkat\nPmWHHTum/lE1XSG5V2ytaXZvML/spWB7kSKMw7uOWjvr0dZ1Nq+dN1hmLb6p72Ir\n5Gwl2Byqvofet8/Dqx+hMXw5I+v9b3XXzay1mDhL40zgC/WYBsG0nJJwqHmdS8IN\nQrlxJ3fk2HCfQFnVue1h/2LHLM+MnEExdULB/BNp7MS1VeQ135eXfkhHd2fl7LKf\nwBSLRoniDb1gvtKSHuAZ5PaJELswJg==\n-----END CERTIFICATE-----\n",
"challenge_code": "w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI=",
"id": "3803c239-ffc6-40a3-b85c-1da798945c3c",
"address": "192.168.1.139:40265"
}
}

STDIN logger performance

Reading asynchronously from STDIN is surprisingly difficult. The method used by the logger command involves a background thread that reads data from STDIN one byte at a time, and when the accumulated data reaches the configured log size the data is flushed to disk.

On its own this approach causes problems whereby a flush might occur in the middle of an incoming packet, thus breaking the protobuf binary format.

To guard against this, the Volt logger uses a second thread thread which is responsible for flushing the buffer, and will only do so after 50ms of inactivity on STDIN (the interval is configurable via the flushIdleInterval property in the configuration file). As a consequence, the produced log file size may slightly exceed that configured in logFileSize.

The flushing thread described above helps reduce the frequency of split packets, but they can still occur due to network latency or other timing blips on the incoming data. Fortunately the protoDbSync utility can recover from these split packets the majority of the time.

Support for more commands in the Volt CLI is coming soon...