Giter VIP home page Giter VIP logo

tessera's Introduction

Build Status codecov Docker Pulls

Important: Breaking change
Users running on 21.10.0 and previous versions will need to perform a database upgrade to work with the latest version of Tessera.

  • For non-H2 users, existing database schema will need to be updated. Execute the appropriate alter script provided.
  • For H2 users, a complete database migration is required before running the alter script. This is due to the considerable number of changes between version 1.4.200 and version 2.0.202 onwards. See more details from H2 release and their recommended upgrade process. Example migration scripts can be found here

  • Important: If using version 21.4.1 and earlier
    Tessera is now released as a zipped distribution instead of an uber jar. If using version 21.4.1 and earlier, see the previous README.

    Tessera is a stateless Java system that is used to enable the encryption, decryption, and distribution of private transactions for Quorum and/or Besu

    Each Tessera node:

    • Generates and maintains a number of private/public key pairs

    • Self manages and discovers all nodes in the network (i.e. their public keys) by connecting to as few as one other node

    • Provides Private and Public API interfaces for communication:

      • Private API - This is used for communication with Quorum
      • Public API - This is used for communication between Tessera peer nodes
    • Provides two way SSL using TLS certificates and various trust models like Trust On First Use (TOFU), whitelist, certificate authority, etc.

    • Supports IP whitelist

    • Connects to any SQL DB which supports the JDBC client

    Documentation

    Docs

    Artefacts

    Runnable distributions

    Tessera

    Remote Enclave Server

    Optional Artefacts

    The following artefacts can be added to a distribution to provide additional functionality.

    Key Vaults

    • Azure: Add support for key pairs stored in Azure Key Vault
    • AWS: Add support for key pairs stored in AWS Secret Store
    • Hashicorp: Add support for key pairs stored in Hashicorp Vault

    Encryptors

    • jnacl: (already included in Tessera and Remote Enclave Server distributions) Add support for NaCl key pairs using jnacl library
    • Elliptical Curve: Add support for elliptic curve key pairs
    • kalium: Add support for NaCl key pairs using kalium library

    Prerequisites

    • Java

      • Java 17+
    • Optional: Gradle

      • If you want to use a locally installed Gradle rather than the included wrapper. Note: wrapper currently uses Gradle 7.0.2.

    Building Tessera from source

    To build and install Tessera:

    1. Clone this repo
    2. Build using the included Gradle Wrapper file
      ./gradlew build   
      

    Installing Tessera

    Download and unpack distribution:

    $ tar xvf tessera-[version].tar
    $ tree tessera-[version]
    tessera-[version]
    ├── bin
    │   ├── tessera
    │   └── tessera.bat
    └── lib
        ├── HikariCP-3.2.0.jar
        ...
    

    Run Tessera (use correct /bin script for your system):

    ./tessera-[version]/bin/tessera help
    

    Supplementing the distribution

    Additional functionality can be added to a distribution by adding .jar files to the /lib directory.

    Adding Tessera artefacts

    Download and unpack the artefact:

    $ tar xvf aws-key-vault-[version].tar
    $ tree aws-key-vault-[version]
    aws-key-vault-[version].tar
    └── lib
        ├── annotations-2.10.25.jar
        ...
    

    Copy the contents of the artefact's /lib into the distribution /lib (make sure to resolve any version conflicts/duplicated .jar files introduced during the copy):

     cp -a aws-key-vault-[version]/lib/. tessera-[version]/lib/
    

    Supporting alternate databases

    By default, Tessera uses an H2 database. To use an alternative database, add the necessary drivers to the lib/ dir:

    For example, to use Oracle database:

    cp ojdbc7.jar tessera-[version]/lib/
    

    DDLs have been provided to help with defining these databases.

    Since Tessera 0.7 a timestamp is recorded with each encrypted transaction stored in the Tessera DB. To update an existing DB to work with Tessera 0.7+, execute one of the provided alter scripts.

    Docker images

    Configuration

    Config File

    A configuration file detailing database, server and network peer information must be provided using the -configfile command line property.

    An in-depth look at configuring Tessera can be found in the Tessera Documentation and includes details on all aspects of configuration including:

    • Cryptographic key config:
      • Using existing private/public key pairs with Tessera
      • How to use Tessera to generate new key pairs
    • TLS config
      • How to enable TLS
      • Choosing a trust mode

    Obfuscate database password in config file

    Certain entries in Tessera config file must be obfuscated in order to prevent any attempts from attackers to gain access to critical part of the application (i.e. database). For the time being, Tessera users have the ability to enable encryption for database password to avoid it being exposed as plain text in the configuration file.

    In Tessera, jasypt library was used together with its Jaxb integration to encrypt/decrypt config values.

    To enable this feature, simply replace your plain-text database password with its encrypted value and wrap it inside an ENC() function.

        "jdbc": {
            "username": "sa",
            "password": "ENC(ujMeokIQ9UFHSuBYetfRjQTpZASgaua3)",
            "url": "jdbc:h2:/qdata/c1/db1",
            "autoCreateTables": true
        }

    Being a Password-Based Encryptor, Jasypt requires a secret key (password) and a configured algorithm to encrypt/decrypt this config entry. This password can either be loaded into Tessera from file system or user input. For file system input, the location of this secret file needs to be set in Environment Variable TESSERA_CONFIG_SECRET

    If the database password is not being wrapped inside ENC() function, Tessera will simply treat it as a plain-text password however this approach is not recommended for production environment.

    • Please note at the moment jasypt encryption is only enabled on jdbc.password field.
    Encrypt database password

    Download and unzip the jasypt package. Redirect to bin directory and the follow commands can be used to encrypt a string

    bash-3.2$ ./encrypt.sh input=dbpassword password=quorum
    
    ----ENVIRONMENT-----------------
    
    Runtime: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 25.171-b11
    
    
    
    ----ARGUMENTS-------------------
    
    input: dbpassword
    password: quorum
    
    
    
    ----OUTPUT----------------------
    
    rJ70hNidkrpkTwHoVn2sGSp3h3uBWxjb
    

    Pick up this output and wrap it inside ENC() function, we should have the following ENC(rJ70hNidkrpkTwHoVn2sGSp3h3uBWxjb) in the config json file.

    Further reading

    • The Tessera Documentation provides additional information on how Tessera works, migrating from Constellation to Tessera, configuration details, and more.
    • Quorum is an Ethereum-based distributed ledger protocol that uses Tessera to provide transaction privacy.
    • Follow the Quorum Examples to see Tessera in action in a demo Quorum network.

    Reporting Security Bugs

    Security is part of our commitment to our users. At Quorum we have a close relationship with the security community, we understand the realm, and encourage security researchers to become part of our mission of building secure reliable software. This section explains how to submit security bugs, and what to expect in return.

    All security bugs in Quorum and its ecosystem (Tessera, Constellation, Cakeshop, ..etc) should be reported by email to [email protected]. Please use the prefix [security] in your subject. This email is delivered to Quorum security team. Your email will be acknowledged, and you'll receive a more detailed response to your email as soon as possible indicating the next steps in handling your report. After the initial reply to your report, the security team will endeavor to keep you informed of the progress being made towards a fix and full announcement.

    If you have not received a reply to your email or you have not heard from the security team please contact any team member through Discord. Please note that Discord channels are public discussion forum. When escalating to this medium, please do not disclose the details of the issue. Simply state that you're trying to reach a member of the security team.

    Responsible Disclosure Process

    Quorum project uses the following responsible disclosure process:

    Once the security report is received it is assigned a primary handler. This person coordinates the fix and release process. The issue is confirmed and a list of affected software is determined. Code is audited to find any potential similar problems. If it is determined, in consultation with the submitter, that a CVE-ID is required, the primary handler will trigger the process. Fixes are applied to the public repository and a new release is issued. On the date that the fixes are applied, announcements are sent to Quorum-announce. At this point you would be able to disclose publicly your finding.

    Note: This process can take some time. Every effort will be made to handle the security bug in as timely a manner as possible, however it's important that we follow the process described above to ensure that disclosures are handled consistently.

    Receiving Security Updates

    The best way to receive security announcements is to subscribe to the Quorum-announce mailing list/channel. Any messages pertaining to a security issue will be prefixed with [security].

    Comments on This Policy If you have any suggestions to improve this policy, please send an email to [email protected] for discussion.

    Contributing

    Tessera is built open source and we welcome external contribution on features and enhancements. Upon review you will be required to complete a Contributor License Agreement (CLA) before we are able to merge. If you have any questions about the contribution process, please feel free to send an email to [email protected]. Please see the Contributors guide for more information about the process.

    Getting Help

    Stuck at some step? Please join our community for support.

    tessera's People

    Contributors

    antonydenyer avatar aristt avatar atoulme avatar baptiste-b-pegasys avatar chris-j-h avatar cucrisis avatar dependabot[bot] avatar emi14 avatar fixanoid avatar frankisawesome avatar jbhurat avatar joshuafernandes avatar kellyshao avatar krish1979 avatar macfarla avatar mark-terry avatar melowe avatar namtruong avatar nicolae-leonte-go avatar nicolasmassart avatar nmvalera avatar oriunbound avatar peterbroadhurst avatar pinges avatar prd-fox avatar rolandtyler avatar satpalsandhu61 avatar timbeiko avatar trung avatar usmansaleem avatar

    Stargazers

     avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

    Watchers

     avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

    tessera's Issues

    Document or Tool generate new config from TOML

    Go through and identify all items in Constellation config files that is not in Tessera config and add support to all through migration script/toolset.

    not in tessera config that is in constellation

    Tessera help looks funky.

    Looks like this on my mac:

     ✘ fixanoid@Felixs-MacBook-Pro  ~/Projects/tessera   master  tessera help  
    usage: tessera -configfile <PATH> [-keygen <PATH>] [-pidfile <PATH>]
        --alwaysSendTo.keyBytes <BYTE[]...>                         Override
                                                                    option for
                                                                    alwaysSend
                                                                    To.keyByte
                                                                    s , type:
                                                                    byte[]
     -configfile <PATH>                                             Path to
                                                                    node
                                                                    configurat
                                                                    ion file
        --jdbc.password <STRING>                                    Override
                                                                    option for
                                                                    jdbc.passw
                                                                    ord ,
                                                                    type:
                                                                    String
    

    All of the param explanations are like this.

    Salvage structured configuration

    We lost a lot of benefit of having a structured xml means for providing configuration before we broke it swapping to a purely json and worse supporting toml file .. now we've created several strands of tech debt reactively just implementing things without sufficient challenging. And therefore risk to the success of the overall program.

    The TOML config is particularly problematic as it attempt to align the private key paths, public key paths and the contents of the passwords file to create a key config. Worse still the private key contents are intact key generation options structured as son fragments.

    base package names

    all classes in tessera core should have the base package com.quorum.tessera.core

    this rule of having a module reference in the package name should be applied to all of the modules.

    com.quorum.tessera.{module ref}

    having a distinct namespace will mean tessera can be more easily packaged for java 9+ module-info descriptors, as modules cannot have package name clashes.

    How to add mysql to 7nodes example?

    I have installed mysql on local machine
    I have downloaded the jar(version 8.0.12) about mysql jdbc and saved at /home/vagrant/tessera/mysql.jar
    I have run the ddl(https://github.com/jpmorganchase/tessera/blob/master/ddls/mysql-ddl.sql) of mysql

    I modify the tessera-init.sh as follwoing and change the jdbc url to mysql

    #!/usr/bin/env bash
    echo "[*] Initialising Tessera configuration"

    currentDir=$(pwd)
    for i in {1..7}
    do
    DDIR="qdata/c$i"
    mkdir -p ${DDIR}
    mkdir -p qdata/logs
    cp "keys/tm$i.pub" "${DDIR}/tm.pub"
    cp "keys/tm$i.key" "${DDIR}/tm.key"
    rm -f "${DDIR}/tm.ipc"
    #change tls to "strict" to enable it (don't forget to also change http -> https)
    cat < ${DDIR}/tessera-config${i}.json
    {
    "useWhiteList": false,
    "jdbc": {
    "username": "root",
    "password": "123456",
    "url": "jdbc:mysql//127.0.0.1:3306/quorum?useSSL=false"

    },
    "server": {
    "port": 900${i},
    "hostName": "http://localhost",
    "sslConfig": {
    "tls": "OFF",
    "generateKeyStoreIfNotExisted": true,
    "serverKeyStore": "${currentDir}/qdata/c${i}/server${i}-keystore",
    "serverKeyStorePassword": "quorum",
    "serverTrustStore": "${currentDir}/qdata/c${i}/server-truststore",
    "serverTrustStorePassword": "quorum",
    "serverTrustMode": "TOFU",
    "knownClientsFile": "${currentDir}/qdata/c${i}/knownClients",
    "clientKeyStore": "${currentDir}/qdata/c${i}/client${i}-keystore",
    "clientKeyStorePassword": "quorum",
    "clientTrustStore": "${currentDir}/qdata/c${i}/client-truststore",
    "clientTrustStorePassword": "quorum",
    "clientTrustMode": "TOFU",
    "knownServersFile": "${currentDir}/qdata/c${i}/knownServers"
    }
    },
    "peer": [
    {
    "url": "http://localhost:9001"
    },
    {
    "url": "http://localhost:9002"
    },
    {
    "url": "http://localhost:9003"
    },
    {
    "url": "http://localhost:9004"
    },
    {
    "url": "http://localhost:9005"
    },
    {
    "url": "http://localhost:9006"
    },
    {
    "url": "http://localhost:9007"
    }
    ],
    "keys": {
    "passwords": [],
    "keyData": [
    {
    "config": $(cat ${currentDir}/qdata/c${i}/tm.key),
    "publicKey": "$(cat ${currentDir}/qdata/c${i}/tm.pub)"
    }
    ]
    },
    "alwaysSendTo": [],
    "unixSocketFile": "${currentDir}/qdata/c${i}/tm.ipc"
    }
    EOF
    done

    Key generation from KeyDataConfig

    com.github.nexus.config.adapters.KeyDataAdapter needs to be interpreted with the create pairs code in KeyManager located in the nexus-app module. The KeyManger code that does this needs to be in the config module and all of this needs to happen in the config module.

        @Override
        public KeyData unmarshal(KeyData keyData) throws Exception {
    
            if (keyData.hasKeys()) {
                return keyData;
            }
    
            //TODO: Generate key pairs from and create populated keyData
            KeyDataConfig keyDataConfig = KeyDataConfigStore.INSTANCE.pop();
    
            return new KeyData(keyDataConfig, null, null);
        }
    

    The private and public keys need generating from the KeyDataConfig object and storing on the KeyData

    Fail during mvn install

    Hello. I was trying install and compile tessera.

    1. $ java -version
      java version "1.8.0_181"
      Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
      Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

    2. $ mvn --version
      Apache Maven 3.3.9
      Maven home: /usr/share/maven
      Java version: 1.8.0_181, vendor: Oracle Corporation
      Java home: /usr/lib/jvm/java-8-oracle/jre
      Default locale: en_US, platform encoding: UTF-8
      OS name: "linux", version: "4.15.0-1022-azure", arch: "amd64", family: "unix"

    3. Apache Tomcat/9.0.11

    I cloned this repo, switch folder and tried run mvn install but got error:
    screen shot 2018-09-10 at 15 28 41

    I used basic config.json
    screen shot 2018-09-10 at 15 29 43

    What did I do wrong?

    KeyGeneratorImpl is attempting to interact with an end user

    I don't know why, but when i'm executing unit tests in the cli module i end up seeing the following

    System.out.println("Enter a relative or absolute path (without extension) to save the keys to");
    System.out.println("or leave blank to not save to separate file:");
    final String path = new Scanner(filenameStream).nextLine();

    if the fileNameStream doesn't have a next line it just sits. It looks like the object should throw an exception in this case or something a CLI can use to inform the user that something is afoot.

    Typo mvn install -P kalium

    In the README :
    Change mvn install -Pkalium to mvn install -P kalium.

    • Bonus: It would be nice to have a 'or' between jnacl and kalium for better understanding.
      Thanks for your work !

    Dont null values/properties during marshalling.

    Rather than masking configuration output to avoid printing privateKey values and/or passwords we have jaxb adapters that null values while they are being marshalled. This has broken override cli args and makes the entire config loading unpredictable and unmaintainable.

    1. Add a masking marshaller to deal with these case
    2. Check that the new function is always used when logging or/and printing to console

    File overwrite tests don't run as a root/admin user

    Tests that create files and remove the writable attribute so an exception is thrown for the test don't work as the root user (on Linux) because the root user always has write access, regardless of permissions.

    Tests that use File#setWritable(false) are the ones that are failing. An alternative way to ensure the exception gets thrown should be used that works with root/admin users.

    Release workflow steps

    the current process used to create 0.5.2

    1. Create and switch to writable branch from master "release from-master"
    mvn clean release:prepare -B
    

    Note: The clean isn't required but was there when I did it.

    mvn clean release:perform -B
    

    After the tag was created on git hub there's a last step to create release from tag.

    Issues/manual steps

    1. Creating the branch from master as can't write to master without a PR
    2. needed to run test suite multiple times, that might add to the 10 minute build time for the release:perform
    3. Release publish step (could be fine for now)

    Support for FUSE file system directory support

    There's a legacy file system data format. Our present code tries to leverage jdbc as much as possible.

    Assuming that is appetite for directory system datastore support the choice is find a jdbc driver that allows us to do this which will give us the benefit of less code changes or add specialised support by way of a FileSystemDAO or something. This will mean more code and having a less homogenous code base.

    Create enclave deployment artefact

    The encryption-api module defines enclave as the service that core uses for encryption operations and key access. Private keys should never be exposed outside of its system boundary.

    One of the proposed deployment options is that enclave can be co-deployed in the same location as private keys and expose its services to tessera which is potentially deployed in a different location.

    Task 1. Provide optional enclave api/transport options to communicate with tessera core.
    Task 2. Provide optional configuration that instructs tessera core how to access enclave securely.

    GRPC acceptance tests should be expanded

    The GRPC acceptance tests do some basic checks to ensure a GRPC service is running, but a full set of tests for each endpoint should be made to ensure the calls act as expected, similar to the REST acceptance tests.

    Private key encryption

    I have a conceptual question regarding Tessera/Constellation and private key encryption/decryption.
    https://github.com/jpmorganchase/tessera/wiki/Configuration#using-existing-keys

    I would like to encrypt a private key using your tools. It looks like that the private key derived from the password is directly used as the Tessera private key. Am I correct with this observation?

    If so, this means that there is a strong dependency between the password, the private key and the public key. If I want to change the password for what ever reason (e.g. compromised password), this means that I can't "keep" my public key, since password => private key => public key.

    Would it be feasible to add another "encryption mode" where the derived key isn't the actual private key, rather an intermediary private key to encrypt/decrypt the actual private key (like geth does it)? Are there any alternatives you would recommend instead (using a 3rd party solution) for encrypting the key with a password that can be rotated?

    KeyGeneratorFactory too tightly coupled.

    com.quorum.tessera.config.keys.KeyGeneratorFactory needs use the service loader or other mechanism so the Cli and other modules that use it aren't forced to use the implementation just to run some unit tests that only need to prove that there is an interaction with the api.

    Database DDL to be added to Tessera repo

    DDL Added:

    tessera/tessera-app/src/main/resources/tesseraDDL.sql

    CREATE TABLE ENCRYPTED_TRANSACTION (ID BIGINT NOT NULL, ENCODED_PAYLOAD LONGVARBINARY NOT NULL, HASH LONGVARBINARY NOT NULL UNIQUE, PRIMARY KEY (ID))
    CREATE SEQUENCE ENC_TX_SEQ INCREMENT BY 50 START WITH 50

    Separate api model objects from internal domain model

    At the moment tessera-core reuses the api objects as its domain model rather than defining its own domain model.

    Task 1. Create domain model in core and redefine transaction manager to use them
    Task 2. Move com.quorum.tessera.api.model in tessera core into jax rs module. Provide convertors to copy between domain and api objects.

    Implement a common set of interfaces to ensure that the read methods are defined on api and domain.

    Provide cli with means to save generated config

    When private and public keys are generated. using -keygen an additional -output or needs defining.

    The default -output should be to sys.out if not defined.. if a path is define the config should be marshalled to that and exit wit running.

    Ported Constellation bug into tessera where -filename has no value

    When a user elects to generate a key from the tessera cli s/he apparently can provide an empty file name. Normal usage is :

    tessera -keygen -filename somefile,someotherfile

    apparently its desirable to also support

    tessera -keygen -filename

    In this case we've now gone to the effort of defining the filename option and then not provided any values for it, the reasonable exception would be that the user is then informed that they haven't defined the file name(s) s/he wishes to use to generate the keys.

    Once we clarify that having no args for -filename is incorrect we need to remove support for it.

    Test CLI Scenerios - Private key generation

    Methodology: Create tessera alias and then cd to tressera/config then run using sample files.

    1. Misleading message when public key path is invalid.
    tessera -configfile src/test/resources/keytests/pubPrivUsingPathsUnlocked_missingPublicKey.json --unixSocketFile /tmp/foo.ipc
    Config validation issue: keys.keyData KeyData 1 : Public key path BOGUS.pub does not exist
    

    FIXED.

    1. Misleading message when private key path is invalid
    tessera -configfile src/test/resources/keytests/pubPrivUsingPathsUnlocked_missingPrivateKey.json --unixSocketFile /tmp/foo.ipc
    Config validation issue: keys.keyData KeyData 1 : Private key path BOGUS.key does not exist
    

    FIXED.

    1. pubPrivUsingPathsLocked case locks and provides on output
      tessera -configfile src/test/resources/keytests/pubPrivUsingPathsLocked.json --unixSocketFile /tmp/foo.ipc
      Expectation is to for cli to return some feedback, but this happy case should result in success

    2. passwordsMissing.json

    tessera -configfile src/test/resources/keytests/passwordsMissing.json --unixSocketFile /tmp/foo.ipc
    Config validation issue: keys.keyData[0].config A locked key was provided without a password.
     Please ensure the same number of passwords are provided as there are keys and remember to include empty passwords for unlocked keys
    

    FIXED

    1. Wrong passwords
    tessera -configfile src/test/resources/keytests/passwordsWrong.json --unixSocketFile /tmp/foo.ipc
    Could not decrypt the private key with the provided password, please double check the passwords provided
    Config validation issue: keys.keyData[0].privateKey Could not decrypt the private key with the provided password, please double check the passwords provided
    

    FIXED

    1. Invalid Unix domain socket file path isn't validated and ends up with big stacktrace on server startup
    tessera -configfile src/test/resources/keytests/pubPrivUsingPathsUnlocked.json
    Config validation issue: unixSocketFile Unable to create file /Users/peter/IdeaProjects/quorum-ex/examples/7nodes/qdata/c1/tm.ipc
    

    FIXED

    PID file arg

    At the moment a pid file system property is used to define an optional pid file. Usage usage should be

    nexus -pidfile /somepath/my.pid ..

    rather than java -Dnexus.pid.file=/somepath/my.pid

    The makes the nexus alias more portable, using the system property means the pid will be used for all invocations.

    Recommend Projects

    • React photo React

      A declarative, efficient, and flexible JavaScript library for building user interfaces.

    • Vue.js photo Vue.js

      🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

    • Typescript photo Typescript

      TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    • TensorFlow photo TensorFlow

      An Open Source Machine Learning Framework for Everyone

    • Django photo Django

      The Web framework for perfectionists with deadlines.

    • D3 photo D3

      Bring data to life with SVG, Canvas and HTML. 📊📈🎉

    Recommend Topics

    • javascript

      JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

    • web

      Some thing interesting about web. New door for the world.

    • server

      A server is a program made to process requests and deliver data to clients.

    • Machine learning

      Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

    • Game

      Some thing interesting about game, make everyone happy.

    Recommend Org

    • Facebook photo Facebook

      We are working to build community through open source technology. NB: members must have two-factor auth.

    • Microsoft photo Microsoft

      Open source projects and samples from Microsoft.

    • Google photo Google

      Google ❤️ Open Source for everyone.

    • D3 photo D3

      Data-Driven Documents codes.