Protobuf Data Synchronisation

This utility provides a means of synchronising arbitrary protobuf data streams to multiple SQLITE databases.

The basic concept is that the app monitors a configured folder for incoming files.

When a new file arrives, the process inspects the file to determine the types of protobuf message it contains (see file format details below).

The process ingests the file and builds SQL CREATE TABLE IF NOT EXISTS and INSERT statements from the contents.

The protoDbSync ingestor uses reflection to translate the protobuf definition to the equivalent SQL schema and statements.

It then uses the tdx Volt API to run the SQL on any number of configured remote databases. A separate sync status is kept for each target database.

File Format

The file format used is essentially the de-facto standard for protobuf data, which is the length-prefixed binary format. This can easily be generated from most protobuf client library auto-generated stubs, for example encodeDelimited in Javascript and SerializeDelimitedToOStream in C++.

Note that currently only basic protobuf message structures are supported - messages with sub-message types or those that make use of oneof or optional are not currently supported.

Most of the time you will not need to manually create files in this format, you can use the Volt Logger command to do it for you.

Each file must have a header present which is a serialisation of a ProtobufSyncConfigurationHeader message, as defined here. The serialisation of the header must include a length prefix.

This header contains a configuration entry (an instance of ProtobufSyncConfiguration) for each message type that will appear in the file.

For example, consider a data producer that is creating two types of message, tcp packets and udp packets, which are defined by the protobuf message types TcpPacket and UdpPacket respectively. In this scenario the ProtobufSyncConfigurationHeader will contain two ProtobufSyncConfiguration instances, the first describing TcpPacket and the second describing UdpPacket.

Each ProtobufSyncConfiguration instance contains the actual protobuf that describes the data type. The main fields of the header are message_proto, which is the protobuf definition of the target messages (as a string), and table_name, which is the name of the table into which the data should be inserted. See ProtobufSyncConfiguration for full details.

As well as the header format described above, each message in the file must be wrapped in a ProtobufSyncWrapper instance and serialised to the file using a length prefix.

Logger integration

The tdx Volt CLI has a ‘logger’ command which can automatically create protoDbSync compatible files, taking input from STDIN or a wire subscription.

To be clear, the tdx Volt Logger will blindly write whatever data is on 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.

In order to create files that are compatible with the protoDbSync utility, data producers must output data in protobuf binary format as serialised instances of the ProtobufSyncWrapper message type.

The idea is to eliminate the need for the producer process to know about the nitty-gritty of the sync file format.

So an arbitrary process can write data (in protobuf binary format for the database sync scenario) to STDIN, and the Volt Logger will create protoDbSync compatible files in a configured folder and pipe the data from STDIN into them up to a configured log size.

See the ‘logger’ command documentation for more details.

Configuration

The process can sync to multiple remote databases. The configuration takes the form of a list of target Volt configurations. See the Appendix for a full working example.

The basic structure of the configuration file is as follows:

{
"extension": "<file extension>",
"rootFolder": "<folder to watch>",
"targets": {
"<target-database-id-1>": {<volt connection object>}
"<target-database-id-2>": {<volt connection object>}
...
"<target-database-id-n>": {<volt connection object>}
}
}

The configuration is made up of following properties:

  • extension - optional file extension to match when watching for incoming data (pdat is the default)
  • rootFolder - the folder the process will watch for incoming files.
  • targets - the list of target database configurations, each entry defines the tdx Volt connection details.
  • target-database-id - this is the id of the target database resource (12439d48-4c0a-4417-822a-e3db70a9d4d4 in the example below), and contains the details of the tdx Volt on which the database is hosted. This is essentially the normal tdx Volt client configuration data, optionally with the addition of the sync property.
  • sync - optional configuration for the sync process.
  • sync.fullInitialSync - the process will send all the data it has seen when it first encounters a new database configuration. The default is ‘true’.

The example below shows a configuration file for a single tdx Volt target, namely resource 12439d48-4c0a-4417-822a-e3db70a9d4d4 on Volt Local RPi:

{
"extension": "pdat",
"rootFolder": "/home/pi/dev/tdxvolt/tdxvolt-core/release/bin/fs20logs",
"targets": {
"12439d48-4c0a-4417-822a-e3db70a9d4d4": {
"client_name": "RPi proto db sync",
"credential": {
"key": "<--REDACTED-->"
},
"volt": {
"ca_pem": "-----BEGIN CERTIFICATE-----\nMIIDnDCCAoSgAwIBAgIECZZpTjANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCRhYjBkNWM2ZS0yMzdhLTQ1YTUtOWMyNy0yMTVkZmQ2ZGEw\nYjAwHhcNMjExMTI5MTg1NjAxWhcNMjIxMTI5MTg1NjAyWjBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCRhYjBkNWM2ZS0yMzdhLTQ1YTUtOWMyNy0yMTVkZmQ2ZGEw\nYjAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfoMpIBSt0kKFFU98S\n/28moM8dLsVlUl0lvuwjw7zHxdEYTqr+ZEuSmDFXARBTdww7bL50TKzSPcPljSW/\nA7uOf29B+OqPtQZQwzOWJ847yyRPSwLgyEgkiUX8lMYH001oriQAVw85RE/FjE+J\nGvRrP988LPpyZltg6P/3ofqiiDlmZ67MikZ0YuqZmbkZj4wH0OnTg1+SIE3coORa\npqd+cEeN7xlzgX98pLTs8bl8W9nd//IkWYiLiw3CTZHr14rg38K+GufJZVyG8R0v\nGwSNuGet/vGN+PZpSoL4GmKmKWfcActkUpJ9IKPpXNde5Evnh+hmtf+0JSoLiAp2\nMP8HAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G\nA1UdDgQWBBTTWm6z1B197MQEm14StUwrHDK1jzANBgkqhkiG9w0BAQsFAAOCAQEA\nfCsHqtU7r0HAsfSdGbfWASnFgUVWi3dB/pewrPqKNTDflijRN2LdlOg1G22tCPYm\nGG7HuMjaostuHNMJ2gBz1N0Ssptz2+veGvdt5M9gErPiNJoK1zJyPADW7mplbe3G\n97S1nv210erAfnXfriwjbiPw5edlpkMdv16V0QxWowIZytmpdHSiq8xX4S90LBMc\nmym9CVFQcwbtPhFg+vcbErtL4C/nE9TAhyG75j+mO1gZzumrrtGuC3CdVKKM+9+g\nQvFsmW9xXEj0lf4RHVibYBlE6Ur1me3BsxaemFIwYPTLOn+MNugJD29BtaHiRtUB\nBA2tApMqeE7CnnBobxmnWA==\n-----END CERTIFICATE-----\n",
"id": "ab0d5c6e-237a-45a5-9c27-215dfd6da0b0"
},
"relay_url": "https://tdxvolt.com",
"sync": {
"fullInitialSync": true
}
}
}
}

Sync algorithm

Possibly ‘sync’ is a misnomer as currently it is essentially a one-way upload. The main objective is to only send any given data file once.

It does this using a set of sub-folders that are dynamically created and managed off of the rootFolder property, an example of which is shown below.

.
├── fs20logs
│ ├── 12439d48-4c0a-4417-822a-e3db70a9d4d4
│ │ ├── sync-2021-12-10T06:31:27.724Z.pdat
│ │ ├── archive
│ │ │ ├── sync-2021-12-01T14:06:38.421Z.pdat
│ │ │ ├── sync-2021-12-01T14:30:30.498Z.pdat
│ │ │ └── sync-2021-12-01T18:22:59.301Z.pdat
│ │ ├── duplicate
│ │ └── error
│ ├── archive
│ │ ├── sync-2021-12-01T14:06:38.421Z.pdat
│ │ ├── sync-2021-12-01T14:30:30.498Z.pdat
│ │ └── sync-2021-12-01T18:22:59.301Z.pdat
│ ├── sync-2021-12-10T06:31:27.724Z.pdat
│ └── sync-2021-12-10T06:47:57.196Z.pdat.lock

For example, considering the above example configuration file, the following directory structure is used:

  • fs20logs - the root folder, which is essentially the ‘pending’ folder - incoming data should be placed here
  • 12439d48-4c0a-4417-822a-e3db70a9d4d4 - the target-specific ‘pending’ folder, a sub-folder like this will be created for each target configured. When a new file arrives in the root folder, it is copied into this folder for each target configured.
  • archive - the files that are successfully processed are moved here. Each target also has an ‘archive’ folder to keep track of the sync status of that target.
  • duplicate - any new file that already exists in archive are skipped and moved here
  • error - any files that fail to transmit are moved here

The process that is creating the data (producer) should only place a file matching the pattern in the root folder when it is complete and ready for transmission. The example producer achieves this by writing to a <timestamp>.pdat.lock file, and then renames the file to remove the .lock when it has reached the desired size. If you’re using the Volt logger command it will do this for you.

File size

The particular maximum file size isn’t mandated, but bear in mind that by default each file is transmitted as a single GRPC call in the form of a bulk SQL INSERT statement. There doesn’t seem to be any hard data on this, but it seems the optimal protobuf message size is around 64 kB, although I’ve used 1 MB without any apparent issues.

Appendix

Configuration file example

This example shows a sync to 3 databases on 3 different Volts.

{
"extension": "pdat",
"rootFolder": "/home/pi/dev/tdxvolt/tdxvolt-core/release/bin/fs20logs",
"targets": {
"05847828-799a-4cc5-975b-03f2908d1443": {
"client_name": "protoDbSync",
"credential": {
"cert": "-----BEGIN CERTIFICATE-----\nMIIDWDCCAkCgAwIBAgIERpkVdzANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCRmODI5NzE4MC1iY2NkLTRjNDktYjRlNS0wY2I0ZTU3Mzhk\nMTUwHhcNMjIwMTI2MTUwMDAzWhcNMjMwMTI2MTUwMDA0WjBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCRkZTVlMGQ4MC0yZWI4LTRlN2YtYTRkMC1kMDgwYTIwY2Vk\nZjUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDbXcT1Syhg4OpN6+kS\nGjvPN8Dq2ZuraMMmmK+8Kc/Efkwr7p83rS/brIyu6R8Hw4ZsQMOFJpa0IhrE2KfJ\nOIn4yLyOJng1ivk8YvE1BvddMoHhamf2OMAQgmKYgZVhLdhA6iJu64gDy//ktrNF\nT3Dt8fOWG6rFtYRtA/SUL+9csIxUW3cSUnQ+BknmrYW2+EeS4GPkVKrVAozIz778\n/d+ceBRItR2p8iC0N3u/VGd3+jwBjjCq3Z1qqzyRzKPHiV7HmmvfrvzBnYqheP13\nolq4OccbJpBTvjQQcb7gXSTVqaGxcGStzYB+8ioGLKpUfBy58xf2odz+lFSsM6vP\nRLIFAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHfD9br8Bi8ZYOq1AGzqwi1DLEmF\nut4RMK9vlssqHyWNsctoEQPJsABXFm37glOvxp5xAxNjE+KkoURjPG+W8PsxgLi6\nO7/TFLkCQ4TESDzzKG+r8+vRF4kqa5UBHv1SBdN1UhCRALFxvgE5JGWOOPA9ivkY\nbhtn22Bfcf6ECm4r+glLoPXs2r16/Ux1qHCQ1frjoM/+IdVFGlBKouVxGw656DGz\niTq4adABfPOEj/ENdzfNsslDCgtEtS6DTZxNsH29bJoSzxXobht04JwaYtqijRx9\nOkMx1sodN+SoaVV0yYFqf3V64WBWoHGLuQzFnQpGb2VfoZlCNYi355Pe92k=\n-----END CERTIFICATE-----\n",
"client_id": "de5e0d80-2eb8-4e7f-a4d0-d080a20cedf5",
"key": "<--REDACTED-->"
},
"sync": {
"fullInitialSync": true
},
"volt": {
"ca_pem": "-----BEGIN CERTIFICATE-----\nMIIDnDCCAoSgAwIBAgIEdxBiBjANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCRmODI5NzE4MC1iY2NkLTRjNDktYjRlNS0wY2I0ZTU3Mzhk\nMTUwHhcNMjIwMTI2MTQ0NTAwWhcNMjMwMTI2MTQ0NTAxWjBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCRmODI5NzE4MC1iY2NkLTRjNDktYjRlNS0wY2I0ZTU3Mzhk\nMTUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfoMpIBSt0kKFFU98S\n/28moM8dLsVlUl0lvuwjw7zHxdEYTqr+ZEuSmDFXARBTdww7bL50TKzSPcPljSW/\nA7uOf29B+OqPtQZQwzOWJ847yyRPSwLgyEgkiUX8lMYH001oriQAVw85RE/FjE+J\nGvRrP988LPpyZltg6P/3ofqiiDlmZ67MikZ0YuqZmbkZj4wH0OnTg1+SIE3coORa\npqd+cEeN7xlzgX98pLTs8bl8W9nd//IkWYiLiw3CTZHr14rg38K+GufJZVyG8R0v\nGwSNuGet/vGN+PZpSoL4GmKmKWfcActkUpJ9IKPpXNde5Evnh+hmtf+0JSoLiAp2\nMP8HAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G\nA1UdDgQWBBTTWm6z1B197MQEm14StUwrHDK1jzANBgkqhkiG9w0BAQsFAAOCAQEA\ncxKQxsCuYlCEm1FgZUgYZ75abPDeyTliD73FwMtktpwtYMqaoMiSaEbuuUZiYjkq\nes+uOA9HNhuAfny/ufhqWp/Hlf1KnLVplEEIoP9l67wXg+/qGMsDehMDcxsALh13\nzvT4a6sSbhFPP/SZ43rumhEA2ZVZMYlBpW937KHhw7aa9j517uR7rOq64aOLjPWf\nsl4xmjNME4e/gYRGoh8RpWQbBnq7Fsrnxc7KDzX6PyRLHSoPmTARC0wchJ42Y4dY\n6k3XrQrccfAd0SvrU/xAUOFLh0tShH1eEBzold7wRPYwTzibeDzU74BoBVRIyxaW\nUiY/O+n407iUSlav95yVTA==\n-----END CERTIFICATE-----\n",
"challenge_code": "w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI=",
"id": "f8297180-bccd-4c49-b4e5-0cb4e5738d15",
"address": "192.168.1.178:33325"
}
},
"6fd1f237-681c-4b58-8d10-39238ac82db2": {
"client_name": "ProtoDbSync",
"credential": {
"cert": "-----BEGIN CERTIFICATE-----\nMIIDWDCCAkCgAwIBAgIEUNJ8szANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCQ2ZmEyZGQ2Yi0wMmZhLTQ3ZGUtYjQ4OC1kYTJkODEzOWNl\nNGMwHhcNMjIwMTI2MTUxMjQ1WhcNMjMwMTI2MTUxMjQ2WjBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCRiNWRkY2ExOS0xZGIzLTRhZWItYWYzNy0yZDA1OTg2YmJi\nZTkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDbXcT1Syhg4OpN6+kS\nGjvPN8Dq2ZuraMMmmK+8Kc/Efkwr7p83rS/brIyu6R8Hw4ZsQMOFJpa0IhrE2KfJ\nOIn4yLyOJng1ivk8YvE1BvddMoHhamf2OMAQgmKYgZVhLdhA6iJu64gDy//ktrNF\nT3Dt8fOWG6rFtYRtA/SUL+9csIxUW3cSUnQ+BknmrYW2+EeS4GPkVKrVAozIz778\n/d+ceBRItR2p8iC0N3u/VGd3+jwBjjCq3Z1qqzyRzKPHiV7HmmvfrvzBnYqheP13\nolq4OccbJpBTvjQQcb7gXSTVqaGxcGStzYB+8ioGLKpUfBy58xf2odz+lFSsM6vP\nRLIFAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGcTyC5Li4sZAeT1F2MLEPrPtZ9S\nSAzADemAbbuyGPQ6+MzBlf3TPPZFG4MicN3g9V0lZzfGPOAdCnXeXQtaMlNDlQua\nwnH94b7sUhZVbmxZ1exX3aHphVTU3ZndhrmRYdRv9UK8PkbL/H8mk07rfUDLo3Nq\nuQycooux3iShd/Hw0kDYVmEaf+nX9WGEpTiuGuIxlsRVj2PLp0G+1pRt0RALV+hj\nvqnyoiYI6BX86hu/Gk1zcxwkWof/RcCbzhyqzbqWzWsjej2bVJE1+LM5PbzXZR2o\nYxrTN6AXcje4T5+WSPD6fD/RWYUAdsj1PvfuFWCGTCEpOx6LEC7hdAiV7rM=\n-----END CERTIFICATE-----\n",
"client_id": "b5ddca19-1db3-4aeb-af37-2d05986bbbe9",
"key": "<--REDACTED-->"
},
"sync": {
"fullInitialSync": true
},
"volt": {
"ca_pem": "-----BEGIN CERTIFICATE-----\nMIIDnDCCAoSgAwIBAgIET8kUKTANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCQ2ZmEyZGQ2Yi0wMmZhLTQ3ZGUtYjQ4OC1kYTJkODEzOWNl\nNGMwHhcNMjIwMTEwMTU1ODMxWhcNMjMwMTEwMTU1ODMyWjBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCQ2ZmEyZGQ2Yi0wMmZhLTQ3ZGUtYjQ4OC1kYTJkODEzOWNl\nNGMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9+iq3yX4JLs/D/tWF\nlhfdz75l5j46KVBkidBKR7K7I7PjVckcsHfA9ifBrohLlHxpVpvKrw4q8xuxAXaC\nAN92kwbjQW1f8cuRrUrKiFAwEiJGm4/aDiRCScrwPSAWRUtxAuPPbIqmbPVAuTt1\nZ1xO4Q0O8DWhDRVo2gwccjILo1NC4ak1AfG50cZ7GX9MpxUQ6GCLNJg1QGh9sxKz\nBY6fxDPYVzIWU7ZNPw799VqFb8PPhsdPXHMozkbIxM9zfUwLCUbOpdM/AuiSgtZj\nBFb9tUn5p+cvIowKpyqG735Crr1WcNH/i2YoXIpCqm18R2GHbVIOz4IvpjiPARq+\nXp0TAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G\nA1UdDgQWBBQYR+nfRdzG5rIdq2bYkZzVzm1ImDANBgkqhkiG9w0BAQsFAAOCAQEA\nu91BCHx0RrBpweP61Q69CefI0xv+yDXz3nDKhkwHbjR9Bp7+6fF5nwp5X0xbFFED\nCt4/JEGZlDf/kAG7WhU8coJc+gufzQuFbwGimNKIWNd6M683Hbp3wtPd2msPVtn9\nlDf/8CarU5+BrYOeTKnjU9CCgHJthxaz/L+S21Y+lu4NSPzKFqRujT9CLhgTQ7pY\nJZ2TJ73QnWuCiG5zLl9lRbHJKTwfOJZsNR0lzHJmhMcFy2J61+iQ8Z5Qt//53F8e\narYP2qxNGev1AIhoRBOboCWOTuWmU4rkHkxBwGJWqVV1SuaNSjYW94omEUonk+bb\nhP3eCDv28hw9UlFveF4Ocw==\n-----END CERTIFICATE-----\n",
"challenge_code": "w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI=",
"id": "6fa2dd6b-02fa-47de-b488-da2d8139ce4c",
"address": "tdxvolt.com:40725"
}
},
"f3a968df-58c2-44d7-b39c-97a5fa78ba90": {
"client_name": "ProtoDbSync",
"credential": {
"cert": "-----BEGIN CERTIFICATE-----\nMIIDWDCCAkCgAwIBAgIEL7VkHjANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCQ3ODI2Mzc2NS03MzBiLTQ1MTQtOGZkOS0xYzc2MmViODFh\nYTIwHhcNMjIwMTI2MTUyMjUzWhcNMjMwMTI2MTUyMjU0WjBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCRlMWFmYzIwYi0yYmE3LTQxMDMtOGYzNS0yNmMzY2RjNTFi\nY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDbXcT1Syhg4OpN6+kS\nGjvPN8Dq2ZuraMMmmK+8Kc/Efkwr7p83rS/brIyu6R8Hw4ZsQMOFJpa0IhrE2KfJ\nOIn4yLyOJng1ivk8YvE1BvddMoHhamf2OMAQgmKYgZVhLdhA6iJu64gDy//ktrNF\nT3Dt8fOWG6rFtYRtA/SUL+9csIxUW3cSUnQ+BknmrYW2+EeS4GPkVKrVAozIz778\n/d+ceBRItR2p8iC0N3u/VGd3+jwBjjCq3Z1qqzyRzKPHiV7HmmvfrvzBnYqheP13\nolq4OccbJpBTvjQQcb7gXSTVqaGxcGStzYB+8ioGLKpUfBy58xf2odz+lFSsM6vP\nRLIFAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEeVgRvcOFfK+f8/IsPN/gX3nMfW\nKsh9BKTCYJR/66b1goRJljXN7cT1zbwxOq2qpDvWhpj8cjMJGbrhE0+OaRe4EThN\nDTBOJ9FgloLqaNpqHAHcmHj/sEdwshogWxxfJk1K0ynpRQjY8zGcATTza7SNdFxO\n3+WFqhn9LcniatP+lcFAqjrKBXCfnFNGi9cALx5UV/WFdLZ1GuA0p/nAymNuARqj\nq8TXah7BqH5YADafsjwOmp/QIC86wtI2yRIn9iTZW4WmeCEQo6gUXWnZ0dpSNORa\nQ1BODiDAlbx7UBz470mAfU5X2Ck/y/mBzJB07r6s8WHTI85CeoGfHOVnfaM=\n-----END CERTIFICATE-----\n",
"client_id": "e1afc20b-2ba7-4103-8f35-26c3cdc51bce",
"key": "<--REDACTED-->"
},
"sync": {
"fullInitialSync": true
},
"volt": {
"ca_pem": "-----BEGIN CERTIFICATE-----\nMIIDnDCCAoSgAwIBAgIELyHuBTANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCQ3ODI2Mzc2NS03MzBiLTQ1MTQtOGZkOS0xYzc2MmViODFh\nYTIwHhcNMjIwMTI2MTUxMzE4WhcNMjMwMTI2MTUxMzE5WjBuMQswCQYDVQQGEwJH\nQjEUMBIGA1UEBwwLU291dGhhbXB0b24xGjAYBgNVBAoMEW5xdWlyaW5nTWluZHMg\nTHRkMS0wKwYDVQQDDCQ3ODI2Mzc2NS03MzBiLTQ1MTQtOGZkOS0xYzc2MmViODFh\nYTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9F99FlMUxTaso+R11\nhP28bT4t9Q3aJFVU/5tpRz6zqD1ocxYs/URaER74iwjK7Z0DHuwpXm9jNOD4t2ef\nOsZ5Vq9iBQcqZCRZ3T6s6Em78ag3OQ2l3VVFBcKh8U4zZqFU2QAHtFHHXH3F3CbX\nuGhweiS6AQ4R479GGVJuoK9+rMQ9YLBG33hmb/XxMMR52i2xkQPa2sX+xGKnsPEY\n1T6vneRTVXHBQHxcScSfOk2fEi3/LX+rkbMTHRScbp7onWmwhc5VtpMpuYuiywxv\nm4TFiSeTJDvk3I1ujgQtcRRJ4ugZNppgh2wBxd8n0/CacC8ssfGtUm20NOdH+OIr\nRuEjAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G\nA1UdDgQWBBRpwpS3YxFTyBXkloz8ryBT5dZ3tzANBgkqhkiG9w0BAQsFAAOCAQEA\nAHUuXJPTqucD4J099M1kqZZn/j5dQn3BfTooeFRqIljT0TXKaNMpBLt5qmfOrKUw\nXR/e7YhaMsHBrHGSwe4drIz/KwmfV1kstWUZbMbF5UQVbyM/vfMlia0CSki9Vy7G\nilA1OxztHMLbV/ToZkbtpBfWZJuXYM8fvA5T929IVr004J1fDot0KeQWxa/eaRVh\nv6OoTfUJnF6QzD5QvS0ZPy9oWiI0LQ4r762MihqtiEwEZ/mcqbvFNnWrsrs37NvL\nbtmQLwgEnnvzYyPA1koHE3rNIPhCjaOZQF08yC5Gjsrccwx+xOubBaXmALBMV457\nnMJq05nWZLkW9irryuIsRw==\n-----END CERTIFICATE-----\n",
"challenge_code": "w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI=",
"id": "78263765-730b-4514-8fd9-1c762eb81aa2",
"address": "192.168.1.69:59969"
}
}
}
}