github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/README.md (about) 1 # vcn <img align="right" src="https://github.com/vchain-us/vcn/blob/master/docs/img/cn-color.eeadbabe.svg" width="160px"/> 2 > ** _CodeNotary.io & CNIL vcn CLI for notarization and authentication of digital artifacts_** 3 4 [![Build and run testsuite](https://github.com/codenotary/vcn/actions/workflows/build.yaml/badge.svg)](https://github.com/codenotary/vcn/actions/workflows/build.yaml) 5 [![Go Report Card](https://goreportcard.com/badge/github.com/vchain-us/vcn?style=flat-square)](https://goreportcard.com/report/github.com/vchain-us/vcn) 6 [![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/vchain-us/vcn) 7 [![Docker pulls](https://img.shields.io/docker/pulls/codenotary/vcn?style=flat-square)](https://hub.docker.com/r/codenotary/vcn) 8 [![Changelog](https://img.shields.io/badge/CHANGELOG-.md-blue?style=flat-square)](https://github.com/vchain-us/vcn/blob/master/CHANGELOG.md) 9 [![Release](https://img.shields.io/github/release/vchain-us/vcn.svg?style=flat-square)](https://github.com/vchain-us/vcn/releases/latest) 10 ## The Trust and Integrity platform for the Cloud native environment 11 Give any digital asset a meaningful, globally-unique, immutable identity that is authentic, verifiable, traceable from anywhere. 12 13 <img align="right" src="https://github.com/vchain-us/vcn/blob/master/docs/img/codenotary_mascot.png" width="256px"/> 14 When using CodeNotary vcn in source code, release, deployment or at runtime, you allow a continuous trust verification that can be used to detect unusual or unwanted activity in your workload and act on it. 15 <br/> 16 Powered by CodeNotary's digital identity infrastructure, vcn lets you notarize all of your digital assets that add a trust level of your choice, custom attributes and a meaningful status without touching or appending anything (unlike digital certificates). 17 That allows change and revocation post-release without breaking any customer environment. 18 <br/> 19 Everything is done in a global, collaborative way to break the common silo solution architecture. Leveraging an immutable, always-on DLT platform allows you to avoid complex setup of Certificate authorities or digital certificates (that are unfit for DevOps anyway). 20 21 ## Table of contents 22 23 - [Quick start](#quick-start) 24 - [DevSecOps in mind](#devsecops-in-mind) 25 - [What kind of behaviors can CodeNotary vcn detect](#what-kind-of-behaviors-can-codenotary-vcn-detect) 26 - [How it works](#how-it-works) 27 - [Installation](#installation) 28 - [Usage](#usage) 29 - [Integrations](#integrations) 30 - [Documentation](#documentation) 31 - [Testing](#testing) 32 - [Codenotary Cloud](#codenotary-cloud) 33 34 - [License](#license) 35 36 ## Quick start 37 38 1. **Download CodeNotary vcn.** There are releases for different platforms: 39 40 - [Download the latest release](https://github.com/vchain-us/vcn/releases/latest) and then read the [Usage](#usage) section below. 41 - We recommend storing `vcn` in your `PATH` - Linux example: 42 ```bash 43 cp vcn-v<version>-linux-amd64 /usr/local/bin/vcn 44 ``` 45 46 2. **Authenticate digital objects** You can use the command as a starting point. 47 48 ```bash 49 vcn authenticate <file|dir://directory|docker://dockerimage|git://gitdirectory> 50 ``` 51 52 3. [**Create your identity (free)**](https://dashboard.codenotary.io/auth/signup) You need an identity at CodeNotary to notarize objects yourself (btw, we're a data-minimum company and only ask for data that is really required) 53 54 55 4. **Notarize existing digital objects** Once you have an account you can start notarizing digital assets to give them an identity. 56 57 ```bash 58 vcn login 59 vcn notarize <file|dir://directory|docker://dockerimage|git://gitdirectory> 60 ``` 61 62 63 ## DevSecOps in mind 64 Codenotary vcn is a solution written by a devops-obsessed engineers for Devops engineers to bring better trust and security to the the CloudNative source to deployment process 65 66 ## What kind of behaviors can CodeNotary vcn detect 67 vcn (and its extensions for Docker, Kubernetes, documents or CI/CD) can detect, authenticate and alert on any behavior that involves using unauthentic digital assets. vcn verification can be embedded anywhere and can be used to trigger alerts, updates or workflows. 68 69 vcn is so versatile, it can help detecting or acting on the following (but not limited to): 70 * Immutable tagging of source code, builds, and container images with version number, owner, timestamp, organization, trust level, and much more 71 * Simple and tamper-proof extraction of notarized tags like version number, owner, timestamp, organization, and trust level from any source code, build and container (based on the related image) 72 * Quickly discover and identify untrusted, revoked or obsolete libraries, builds, and containers in your application 73 * Detect the launch of an authorized or unknown container immediately 74 * Prevent untrusted or revoked containers from starting in production 75 * Verify the integrity and the publisher of all the data received over any channel 76 77 and more 78 * Enable application version checks and actions 79 * Buggy or rogue libraries can be traced by simple revoke or unsupport 80 * Revoke or unsupport your build or build version post-deployment (no complex certificate revocation that includes delivery of newly signed builds) 81 * Stop unwanted containers from being launched 82 * Make revocation part of the remediation process 83 * Use revocation without impairing customer environments 84 * Trace source code to build to deployment by integration into CI/CD or manual workflow 85 * Tag your applications for specific use cases (alpha, beta - non-commercial aso). 86 87 not just containers, also virtual machines - [check out vCenter Connector, in case you're running VMware vSphere](https://github.com/openfaas-incubator/vcenter-connector) 88 * Newly created or existing virtual machines automatically get a unique identity that can be trusted or untrusted 89 * Prevent launch of untrusted VMs 90 * Stop or suspend running outdated or untrusted VMs 91 * Detect the cloning or export of VMs and alert 92 93 94 ## How it works 95 ![vcn How it works](https://raw.githubusercontent.com/vchain-us/vcn/master/docs/vcn_hiwb.png "How it works") 96 97 ## Installation 98 99 ### Download binary 100 101 It's easiest to download the latest version for your platform from the [release page]( 102 https://github.com/vchain-us/vcn/releases). 103 104 Once downloaded, you can rename the binary to `vcn`, then run it from anywhere. 105 > For Linux and macOS you need to mark the file as executable: `chmod +x vcn` 106 107 ### Homebrew / Linuxbrew 108 109 If you are on macOS and using [Homebrew](https://brew.sh/) (or on Linux and using [Linuxbrew](https://linuxbrew.sh/)), you can install `vcn` with the following: 110 111 ``` 112 brew tap vchain-us/brew 113 brew install vcn 114 ``` 115 116 ### Build from Source 117 118 After having installed [golang](https://golang.org/doc/install) 1.12 or newer clone this 119 repository into your working directory. 120 121 Now, you can build `vcn` in the working directory by using `make vcn` and then run `./vcn`. 122 123 Alternatively, you can install `vcn` in your system simply by running `make install`. This will put the `vcn` executable into `GOBIN` which is 124 accessible throughout the system. 125 126 ## Usage 127 128 Basically, `vcn` can notarize or authenticate any of the following kind of assets: 129 130 - a **file** 131 - an entire **directory** (by prefixing the directory path with `dir://`) 132 - a **git commit** (by prefixing the local git working directory path with `git://`) 133 - a **container image** (by using `docker://` or `podman://` followed by the name of an image present in the local registry of docker or podman, respectively) 134 135 > It's possible to provide a hash value directly by using the `--hash` flag. 136 137 For detailed **command line usage** see [docs/cmd/vcn.md](https://github.com/vchain-us/vcn/blob/master/docs/cmd/vcn.md) or just run `vcn help`. 138 139 ### Wildcard support and recursive notarization 140 141 It's also possible to notarize assets using wildcard. 142 With `--recursive` flag is possible to iterate over inner directories. 143 ```shell script 144 ./vcn n "*.md" --recursive 145 ``` 146 147 ### Local API server 148 149 It's possible to start a local API server. All commands are supported. 150 The notarization password can be submitted with the `x-notarization-password` header. 151 Examples: 152 153 ```bash 154 curl --location --request GET '127.0.0.1:8080/inspect/e2b58ab102dbadb3b1fd5139c8d2a937dc622b1b0d0907075edea163fe2cd093' \ 155 --header 'x-notarization-password: *********' \ 156 --header 'Authorization: Basic ****' \ 157 --header 'Content-Type: application/json' \ 158 --data-raw '{ 159 "Kind": "file", 160 "Name": "CONTRIBUTING.md", 161 "Hash": "e2b58ab102dbadb3b1fd5139c8d2a937dc622b1b0d0907075edea163fe2cd093", 162 "Size": 1400, 163 "ContentType": "text/plain; charset=utf-8" 164 }' 165 ``` 166 ### Notarization 167 168 Register an account with [codenotary.io](https://codenotary.io) first. 169 170 Then start with the `login` command. `vcn` will walk you through login and importing up your secret upon initial use. 171 ``` 172 vcn login 173 ``` 174 175 Once your secret is set you can notarize assets like in the following examples: 176 177 ``` 178 vcn notarize <file> 179 vcn notarize dir://<directory> 180 vcn notarize docker://<imageId> 181 vcn notarize podman://<imageId> 182 vcn notarize git://<path_to_git_repo> 183 vcn notarize --hash <hash> 184 ``` 185 186 By default all assets are notarized private, so not much information is disclosed about the asset. If you want to make that public and therefore, more trusted, please use the `--public` flag. 187 188 ``` 189 vcn notarize --public <asset> 190 ``` 191 192 Change the asset's status: 193 194 ``` 195 vcn unsupport <asset> 196 vcn untrust <asset> 197 ``` 198 199 Finally, to fetch all assets you've notarized: 200 201 ``` 202 vcn list 203 ``` 204 205 ### Authentication 206 207 ``` 208 vcn authenticate <file> 209 vcn authenticate dir://<directory> 210 vcn authenticate docker://<imageId> 211 vcn authenticate podman://<imageId> 212 vcn authenticate git://<path_to_git_repo> 213 vcn authenticate --hash <hash> 214 ``` 215 > You can use `vcn authenticate` even without a [codenotary.io](https://codenotary.io) account. 216 217 To output results in `json` or `yaml` formats: 218 ``` 219 vcn authenticate --output=json <asset> 220 vcn authenticate --output=yaml <asset> 221 ``` 222 > Check out the [user guide](https://github.com/vchain-us/vcn/blob/master/docs/user-guide/formatted-output.md) for further details. 223 224 225 ## Integrations 226 227 * [Github Action](https://github.com/marketplace/actions/verify-commit) - An action to verify the authenticity of your commits within your Github workflow 228 * [docker](https://github.com/vchain-us/vcn/blob/master/docs/user-guide/schemes/docker.md) - Out of the box support for notarizing and authenticating Docker images. 229 * [hub.docker.com/r/codenotary/vcn](https://hub.docker.com/r/codenotary/vcn) - The `vcn`'s DockerHub repository. 230 * [kube-notary](https://github.com/vchain-us/kube-notary) - A Kubernetes watchdog for verifying image trust with CodeNotary. 231 * [vcn-watchdog](https://github.com/vchain-us/vcn-watchdog) - Continuous authentication with CodeNotary for Docker. 232 * [jsvcn](https://github.com/vchain-us/jsvcn) - CodeNotary JavaScript Client. 233 * [jvcn](https://github.com/vchain-us/jvcn) - CodeNotary Java Bindings. 234 * [jvcn-maven-plugin](https://github.com/vchain-us/jvcn-maven-plugin) - Maven dependency authentication and enforcement. 235 236 ## Documentation 237 238 * [Command line usage](https://github.com/vchain-us/vcn/blob/master/docs/cmd/vcn.md) 239 * [Configuration](https://github.com/vchain-us/vcn/blob/master/docs/user-guide/configuration.md) 240 * [Environments](https://github.com/vchain-us/vcn/blob/master/docs/user-guide/environments.md) 241 * [Formatted output (json/yaml)](https://github.com/vchain-us/vcn/blob/master/docs/user-guide/formatted-output.md) 242 * [Notarization explained](https://github.com/vchain-us/vcn/blob/master/docs/user-guide/notarization.md) 243 244 ## Examples 245 246 #### Authenticate a Docker image automatically prior to running it 247 248 First, you’ll need to pull the image by using: 249 250 ``` 251 docker pull hello-world 252 ``` 253 254 Then use the below command to put in place an automatic safety check. It allows only verified images to run. 255 256 ``` 257 vcn authenticate docker://hello-world && docker run hello-world 258 ``` 259 If an image was not verified, it will not run and nothing will execute. 260 261 262 #### Authenticate multiple assets 263 You can authenticate multiple assets by piping other command outputs into `vcn`: 264 ``` 265 ls | xargs vcn authenticate 266 ``` 267 > The exit code will be `0` only if all the assets in you other command outputs are verified. 268 269 #### Authenticate by a specific signer 270 By adding `--signerID`, you can authenticate that your asset has been signed by a specific SignerID. 271 > A SignerID is the signer public address (represented as a 40 hex characters long string prefixed with `0x`). 272 273 ``` 274 vcn authenticate --signerID 0x8f2d1422aed72df1dba90cf9a924f2f3eb3ccd87 docker://hello-world 275 ``` 276 277 #### Authenticate by a list of signers 278 279 If an asset you or your organization wants to trust needs to be verified against a list of signers as a prerequisite, then use the `vcn authenticate` command and the following syntax: 280 281 - Add a `--signerID` flag in front of each SignerID you want to add 282 (eg. `--signerID 0x0...1 --signerID 0x0...2`) 283 - Or set the env var `VCN_SIGNERID` correctly by using a space to separate each SignerID (eg. `VCN_SIGNERID=0x0...1 0x0...2`) 284 > Be aware that using the `--signerID` flag will take precedence over `VCN_SIGNERID`. 285 286 The asset authentication will succeed only if the asset has been signed by at least one of the signers. 287 288 #### Authenticate using the asset's hash 289 290 If you want to authenticate an asset using only its hash, you can do so by using the command as shown below: 291 292 ``` 293 vcn authenticate --hash fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e 294 ``` 295 296 #### Unsupport/untrust an asset you do not have anymore 297 298 In case you want to unsupport/untrust an asset of yours that you no longer have, you can do so using the asset hash(es) with the following steps below. 299 300 First, you’ll need to get the hash of the asset from your CodeNotary [dashboard](https://dashboard.codenotary.io/) or alternatively you can use the `vcn list` command. Then, in the CLI, use: 301 302 ``` 303 vcn untrust --hash <asset's hash> 304 # or 305 vcn unsupport --hash <asset's hash> 306 ``` 307 308 #### Notarization within automated environments 309 310 First, you’ll need to make `vcn` have access to the `${HOME}/.vcn` folder that holds your secret (the private key). 311 Then, set up your environment accordingly using the following commands: 312 ``` 313 export VCN_USER=<email> 314 export VCN_PASSWORD=<login password> 315 export VCN_NOTARIZATION_PASSWORD=<notarization password> 316 ``` 317 > It's possible to disable one time password requirement with: 318 >```bash 319 > export VCN_OTP_EMPTY=true 320 > ``` 321 Once done, you can use `vcn` in your non-interactive environment using: 322 323 ``` 324 vcn login 325 vcn notarize <asset> 326 ``` 327 > Other commands like `untrust` and `unsupport` will also work. 328 329 ## Testing 330 ``` 331 make test 332 ``` 333 334 ## Codenotary Cloud 335 336 Vcn was extended in order to be compatible with [Codenotary Cloud](https://codenotary.com/) . 337 Notarized assets informations are stored in a tamperproof ledger with cryptographic verification backed by [immudb](https://codenotary.com/technologies/immudb/), the immutable database. 338 Thanks to this `vcn` is faster and provides more powerful functionalities like local data inclusion, consistency verification and enhanced CLI filters. 339 340 ### Obtain an API Key 341 To provide access to Immutable Ledger a valid API Key is required. 342 This API Key is bound to a specific Ledger and it's required during vcn login. 343 To obtain a valid key you need to get access to a licensed Codenotary Cloud installation. 344 345 ### Login 346 347 To login in Immutable Ledger provides `--lc-port` and `--lc-host` flag, also the user submit API Key when requested. 348 Once host, port and API Key are provided, it's possible to omit them in following commands. Otherwise, the user can provide them in other commands like `notarize`, `verify` or `inspect`. 349 350 ```shell script 351 vcn login --lc-port 443 --lc-host cnlc-host.com 352 ``` 353 354 > One time password (otp) is not mandatory 355 356 Alternatively, for using vcn in non-interactive mode, the user can supply the API Key via the `VCN_LC_API_KEY` environment variable, e.g.: 357 358 ```shell script 359 export VCN_LC_API_KEY=apikeyhere 360 361 # No vcn login command needed 362 363 # Other vcn commands... 364 vcn notarize asset.txt --lc-host cnlc-host.com --lc-port 443 365 ``` 366 367 #### TLS 368 369 By default, vcn will try to establish a secure connection (TLS) with a Immutable Ledger server. 370 371 The user can also provide a custom TLS certificate for the server, in case vcn is not able to download it automatically: 372 373 ```shell script 374 vcn login --lc-port 443 --lc-host cnlc-host.com --lc-cert mycert.pem 375 ``` 376 377 For testing purposes or in case the provided certificate should be always trusted by the client, the user can 378 configure vcn to skip TLS certificate verification with the `--lc-skip-tls-verify` option: 379 380 ```shell script 381 vcn login --lc-port 443 --lc-host cnlc-host.com --lc-cert mycert.pem --lc-skip-tls-verify 382 ``` 383 384 Finally in case the Immutable Ledger Server is not exposed through a TLS endpoint, the user can request a cleartext 385 connection using the `--lc-no-tls` option: 386 387 ```shell script 388 vcn login --lc-port 80 --lc-host cnlc-host.com --lc-no-tls 389 ``` 390 ### Signature verification 391 If the connected Codenotary Cloud server is cryptographically signing messages vcn will requests public key in order to verify signatures. 392 It's possible to specify the public key with the environment vars `VCN_SIGNING_PUB_KEY_FILE={an ECDSA public key file path}` or `VCN_SIGNING_PUB_KEY={an ECDSA public key payload}`. 393 In alternative also flags `--signing-pub-key-file` and `--signing-pub-key` are availables. 394 395 > If no public key is provided `vcn` will automatically trust the first signature found while connecting on a server configured to sign messages and it will save in `~/.vcn-trusted-signing-pub-key` the trusted public key. 396 If `--enforce-signature-verify` flag or `VCN_ENFORCE_SIGNATURE_VERIFY` env var is provided and vcn is run inside a terminal it will requests for a fingerprint confirmation. 397 398 In order to verify the fingerprint on the public openssl key its possible to check CNC dashboard and verify that fingerprint match the server identity. 399 To obtain the fingerprint from an openssl ECDSA `private` key use: 400 ```shell 401 ssh-keygen -f mykey.key -y > mykey.pem 402 ssh-keygen -l -v -f mykey.pem 403 ``` 404 To obtain the fingerprint from an openssl ECDSA `public` key use: 405 ```shell 406 ssh-keygen -i -m PKCS8 -f mykey.pub > mykey.pem.pub 407 ssh-keygen -l -v -f mykey.pem.pub 408 ``` 409 ### Commands 410 All commands reference didn't change. 411 412 ### Add custom metadata when signing assets 413 The user can upload custom metadata when doing an asset notarization using the `--attr` option, e.g.: 414 415 ```shell script 416 vcn n README.md --attr Testme=yes --attr project=5 --attr pipeline=test 417 ``` 418 419 This command would add the custom asset metadata Testme: yes, project: 5, pipeline: test. 420 421 The user can read the metadata back on asset authentication, i.e. using the `jq` utility: 422 423 ```shell script 424 vcn a README.md -o json | jq .metadata 425 ``` 426 427 ### Inspect 428 Inspect has been extended with the addition of new filter: `--last`, `--first`, `--start` and `--end`. 429 With `--last` and `--first` are returned the N first or last respectively. 430 431 ```shell script 432 vcn inspect document.pdf --last 10 433 ``` 434 435 With `--start` and `--end` it's possible to use a time range filter: 436 437 ```shell script 438 vcn inspect document.pdf --start 2020/10/28-08:00:00 --end 2020/10/28-17:00:00 439 ``` 440 441 If no filters are provided only maximum 100 items are returned. 442 443 ### Signer Identifier 444 It's possible to filter results by signer identifier: 445 446 ```shell script 447 vcn inspect document.pdf --signerID CygBE_zb8XnprkkO6ncIrbbwYoUq5T1zfyEF6DhqcAI= 448 ``` 449 ### Attachments 450 When notarizing an asset you can add attachments to the transaction: 451 ```shell script 452 vcn n artifact --attach attachment1:label1 --attach attachment2:label1 --attach attachment3:label2 453 ``` 454 >This is especially useful when running a CI pipeline in multiple steps and each step provides a different result that should be attached (i. e. vulnerability scanner result, compliance scanner result, dependency scanner result). 455 456 To retrieve these attachments you can either use the notarization transaction uid or the labels. 457 When using the uid all attachments of the specific notarization transaction will be downloaded. 458 ```shell script 459 vcn a go.sum --lc-uid 1624700334893475066 --output attachments 460 ``` 461 When using `labels` you can either download a specific attachment or all attachments with the same label. 462 ```shell script 463 vcn a go.sum --attach attachment2:label1 --output attachments 464 vcn a go.sum --attach label1 --output attachments 465 ``` 466 Another example that shows the artifact attachment's download across all notarizations: 467 ```shell script 468 # upload attachments one at a time: 469 vcn n asset.bin --attach file1.txt:labelXYZ 470 vcn n asset.bin --attach file2.txt:labelXYZ 471 vcn n asset.bin --attach file3.txt:labelXYZ 472 # downloads all attached files 473 vcn a asset.bin --attach labelXYZ --output attachments 474 ``` 475 476 The label only command --attach label1 downloads the latest version of all attachments that have the requested label. 477 Existing files will not be overwritte. In case you want to download and overwrite existing files use the `--force` flag. 478 If there are multiple versions of a specific attachment the file will be downloaded with an enumerated postfix. 479 480 ```shell script 481 vcn a go.sum --attach lab1 --output attachments --force 482 ``` 483 484 ### Local API server 485 486 Local API server is supported. 487 The `API Key` can be submitted with the `x-notarization-lc-api-key` header. 488 489 Notarize example: 490 ```bash 491 curl --location --request POST '127.0.0.1:8082/notarize' \ 492 --header 'x-notarization-lc-api-key: oikfnlbjinhhclvjiotckgwfuyfjxntxmcau' \ 493 --header 'Content-Type: application/json' \ 494 --data-raw '{ 495 "Kind":"file", 496 "Name":"CONTRIBUTING.md", 497 "Hash":"e2b58ab102dbadb3b1fd5139c8d2a937dc622b1b0d0907075edea163fe2cd093", 498 "Size":1400, 499 "ContentType":"text/plain; charset=utf-8" 500 }' 501 ``` 502 Authenticate example: 503 ```bash 504 curl --location --request GET '127.0.0.1:8081/authenticate/e2b58ab102dbadb3b1fd5139c8d2a937dc622b1b0d0907075edea163fe2cd093' \ 505 --header 'x-notarization-lc-api-key: oikfnlbjinhhclvjiotckgwfuyfjxntxmcau' 506 ``` 507 Inspect example: 508 ```bash 509 curl --location --request GET '127.0.0.1:8082/authenticate/e2b58ab102dbadb3b1fd5139c8d2a937dc622b1b0d0907075edea163fe2cd093' \ 510 --header 'x-notarization-lc-api-key: oikfnlbjinhhclvjiotckgwfuyfjxntxmcau' 511 ``` 512 Inspect with signerID example: 513 ```bash 514 curl --location --request GET '127.0.0.1:8081/inspect/e2b58ab102dbadb3b1fd5139c8d2a937dc622b1b0d0907075edea163fe2cd093?signerid=yZtm26ZgmZr37NQ41TXbJ2jStMVWZhE-3cp4Wb7gKQo=' \ 515 --header 'x-notarization-lc-api-key: oikfnlbjinhhclvjiotckgwfuyfjxntxmcau' 516 ``` 517 Untrust example: 518 ```bash 519 curl --location --request POST '127.0.0.1:8081/untrust' \ 520 --header 'x-notarization-lc-api-key: oikfnlbjinhhclvjiotckgwfuyfjxntxmcau' \ 521 --header 'Content-Type: application/json' \ 522 --data-raw '{ 523 "Kind": "file", 524 "Name": "CONTRIBUTING.md", 525 "Hash": "e2b58ab102dbadb3b1fd5139c8d2a937dc622b1b0d0907075edea163fe2cd093", 526 "Size": 1400, 527 "ContentType": "text/plain; charset=utf-8" 528 }' 529 ``` 530 531 ## License 532 533 This software is released under [GPL3](https://www.gnu.org/licenses/gpl-3.0.en.html).