diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..aa1f831433ce9723030c2fb3cd14d95b242d4826
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,98 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+project("vGraph" CXX)
+
+set(CMAKE_SKIP_RPATH TRUE)
+
+set(CMAKE_VERBOSE_MAKEFILE TRUE)
+
+if (!CMAKE_CXX_COMPILER)
+    message(FATAL_ERROR "No C++ compiler found")
+endif()
+
+set(CMAKE_CXX_STANDARD 14)
+
+set(CMAKE_EXE_LINKER_FLAGS "-static-libstdc++")
+
+# Possible values are Debug, Release, RelWithDebInfo, MinSizeRel
+set(CMAKE_BUILD_TYPE "RelWithDebInfo")
+
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "_build")
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "_build")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "_build")
+
+# Set the project home dir
+set(VGRAPH_HOME ${CMAKE_CURRENT_SOURCE_DIR})
+
+# To include customized FindXXX.cmake modules
+set(CMAKE_MODULE_PATH "${VGRAPH_HOME}/cmake" ${CMAKE_MODULE_PATH})
+
+find_package(Boost)
+find_package(OpenSSL)
+find_package(Krb5 REQUIRED gssapi)
+find_package(PCHSupport)
+
+add_compile_options(-Winvalid-pch)
+
+include_directories(SYSTEM ${VGRAPH_HOME}/third-party/_build/include)
+include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
+include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
+include_directories(SYSTEM ${KRB5_INCLUDE_DIRS})
+include_directories(AFTER common)
+include_directories(AFTER interface)
+include_directories(AFTER ${VGRAPH_HOME})
+
+link_directories(third-party/_build/lib ${Boost_LIBRARY_DIRS} ${KRB5_LIBRARY_DIRS})
+
+# All thrift libraries
+set(THRIFT_LIBRARIES
+    thriftcpp2
+    thrift
+    thriftprotocol
+    server
+    async
+    protocol
+    transport
+    concurrency
+    security
+    thriftfrozen2
+    thrift-core
+)
+
+# All compression libraries
+set(COMPRESSION_LIBRARIES bz2 snappy zstd z)
+
+add_subdirectory(third-party)
+add_subdirectory(interface)
+add_subdirectory(common)
+add_subdirectory(dataman)
+add_subdirectory(client)
+#add_subdirectory(storage)
+add_subdirectory(server)
+add_subdirectory(console)
+add_subdirectory(consensus)
+
+add_dependencies(common third-party)
+#add_dependencies(storage_engines common)
+
+add_custom_target(
+    clean-build
+    COMMAND ${CMAKE_MAKE_PROGRAM} clean
+    DEPENDS clean-interface clean-pch
+)
+
+add_custom_target(
+    clean-all
+    COMMAND ${CMAKE_MAKE_PROGRAM} clean
+    DEPENDS clean-interface clean-pch clean-third-party
+)
+
+add_custom_target(
+    distclean
+    COMMAND "find" "." "-name" "CMakeFiles" "|" "xargs" "rm" "-fr"
+    COMMAND "find" "." "-name" "CMakeCache.txt" "|" "xargs" "rm" "-f"
+    COMMAND "find" "." "-name" "cmake_install.cmake" "|" "xargs" "rm" "-f"
+    COMMAND "find" "." "-name" "Makefile" "|" "xargs" "rm" "-f"
+    DEPENDS clean-all
+)
+
diff --git a/src/console/CMakeLists.txt b/src/console/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..93e24feb06a92c868af03e79f27db80487eb29ae
--- /dev/null
+++ b/src/console/CMakeLists.txt
@@ -0,0 +1,46 @@
+add_library(console_obj OBJECT CliManager.cpp CmdProcessor.cpp)
+add_dependencies(console_obj common)
+
+add_executable(
+    vgraph
+    GraphDbConsole.cpp
+    $<TARGET_OBJECTS:console_obj>
+    $<TARGET_OBJECTS:client_cpp_obj>
+    $<TARGET_OBJECTS:dataman_obj>
+    $<TARGET_OBJECTS:base_obj>
+    $<TARGET_OBJECTS:vgraph_thrift_obj>
+    $<TARGET_OBJECTS:time_obj>
+)
+target_link_libraries(
+    vgraph
+    thriftcpp2
+    thrift
+    thrift-core
+    thriftprotocol
+    async
+    protocol
+    transport
+    concurrency
+    security
+    thriftfrozen2
+    wangle
+    folly
+    boost_system
+    boost_context
+    ${OPENSSL_LIBRARIES}
+    ${KRB5_LIBRARIES}
+    event
+    bz2
+    snappy
+    zstd
+    z
+    glog
+    gflags
+    resolv
+    double-conversion
+    dl
+    -pthread
+)
+
+#add_subdirectory(test)
+
diff --git a/src/console/CliManager.cpp b/src/console/CliManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..56dfadd5aa34d9b44c70ebac3ac780bdc7b17c99
--- /dev/null
+++ b/src/console/CliManager.cpp
@@ -0,0 +1,102 @@
+/* Copyright (c) 2018 - present, VE Software Inc. All rights reserved
+ *
+ * This source code is licensed under Apache 2.0 License
+ *  (found in the LICENSE.Apache file in the root directory)
+ */
+
+#include "console/CliManager.h"
+#include "client/cpp/GraphDbClient.h"
+
+DEFINE_string(prompt, "vGraph >",
+              "Default prompt for the command line interface");
+
+
+namespace vesoft {
+namespace vgraph {
+
+const int32_t kMaxAuthInfoRetries = 3;
+const int32_t kMaxUsernameLen = 16;
+const int32_t kMaxPasswordLen = 24;
+const int32_t kMaxCommandLineLen = 1024;
+
+
+bool CliManager::connect(const std::string& addr,
+                         uint16_t port,
+                         const std::string& username,
+                         const std::string& password) {
+    char user[kMaxUsernameLen + 1];
+    char pass[kMaxPasswordLen + 1];
+
+    strncpy(user, username.c_str(), kMaxUsernameLen);
+    user[kMaxUsernameLen] = '\0';
+    strncpy(pass, password.c_str(), kMaxPasswordLen);
+    pass[kMaxPasswordLen] = '\0';
+
+    // Make sure username is not empty
+    for (int32_t i = 0; i < kMaxAuthInfoRetries && !strlen(user); i++) {
+        // Need to interactively get the username
+        std::cout << "Username: ";
+        std::cin.getline(user, kMaxUsernameLen);
+        user[kMaxUsernameLen] = '\0';
+    }
+    if (!strlen(user)) {
+        std::cout << "Authentication failed: "
+                     "Need a valid username to authenticate\n\n";
+        return false;
+    }
+
+    // Make sure password is not empty
+    for (int32_t i = 0; i < kMaxAuthInfoRetries && !strlen(pass); i++) {
+        // Need to interactively get the password
+        std::cout << "Password: ";
+        std::cin.getline(pass, kMaxPasswordLen);
+        pass[kMaxPasswordLen] = '\0';
+    }
+    if (!strlen(pass)) {
+        std::cout << "Authentication failed: "
+                     "Need a valid password\n\n";
+        return false;
+    }
+
+    addr_ = addr;
+    port_ = port;
+    username_ = user;
+    
+    auto client = std::make_unique<GraphDbClient>(addr_, port_);
+    int32_t err = client->connect(user, pass);
+    if (!err) {
+        std::cerr << "\nWelcome to vGraph (Version 0.1)\n\n";
+        cmdProcessor_ = std::make_unique<CmdProcessor>(std::move(client));
+        return true;
+    } else {
+        // There is an error
+        std::cout << "Authentication failed: " << client->getErrorStr() << "\n";
+        return false;
+    }
+}
+
+
+void CliManager::batch(const std::string& filename) {
+}
+
+
+void CliManager::loop() {
+    char cmd[kMaxCommandLineLen + 1];
+
+    while (true) {
+        // Print out prompt
+        std::cout << FLAGS_prompt << " ";
+        std::cin.getline(cmd, kMaxCommandLineLen);
+        cmd[kMaxCommandLineLen] = '\0';
+
+        auto trimmedCmd = folly::trimWhitespace(cmd);
+        if (!trimmedCmd.empty() && !cmdProcessor_->process(trimmedCmd)) {
+            break;
+        }
+    }
+
+    std::cout << "\nBye!\n\n";
+}
+
+}  // namespace vgraph
+}  // namespace vesoft
diff --git a/src/console/CliManager.h b/src/console/CliManager.h
new file mode 100644
index 0000000000000000000000000000000000000000..e1ccb35d9e4fd25345658321ea0c8ffd8456d890
--- /dev/null
+++ b/src/console/CliManager.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2018 - present, VE Software Inc. All rights reserved
+ *
+ * This source code is licensed under Apache 2.0 License
+ *  (found in the LICENSE.Apache file in the root directory)
+ */
+
+#ifndef CONSOLE_CLIMANAGER_H_
+#define CONSOLE_CLIMANAGER_H_
+
+#include "base/Base.h"
+#include "console/CmdProcessor.h"
+
+namespace vesoft {
+namespace vgraph {
+
+class CliManager final {
+public:
+    CliManager() = default;
+    ~CliManager() = default;
+
+    bool connect(const std::string& addr,
+                 uint16_t port,
+                 const std::string& username,
+                 const std::string& password);
+
+    void batch(const std::string& filename);
+
+    void loop();
+
+private:
+    std::string addr_;
+    uint16_t port_;
+    std::string username_;
+
+    std::unique_ptr<CmdProcessor> cmdProcessor_;
+};
+
+}  // namespace vgraph
+}  // namespace vesoft
+#endif  // CONSOLE_CLIMANAGER_H_
diff --git a/src/console/CmdProcessor.cpp b/src/console/CmdProcessor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3ca66b1467fb20e698d43739ceae2b86f5464a7b
--- /dev/null
+++ b/src/console/CmdProcessor.cpp
@@ -0,0 +1,266 @@
+/* Copyright (c) 2018 - present, VE Software Inc. All rights reserved
+ *
+ * This source code is licensed under Apache 2.0 License
+ *  (found in the LICENSE.Apache file in the root directory)
+ */
+
+#include "console/CmdProcessor.h"
+#include "time/Duration.h"
+#include "dataman/RowSetReader.h"
+#include "dataman/RowReader.h"
+
+namespace vesoft {
+namespace vgraph {
+
+#define GET_VALUE_WIDTH(VT, FN, FMT) \
+    VT val; \
+    if (fieldIt->get ## FN (val) == ResultType::SUCCEEDED) { \
+        int32_t len = folly::stringPrintf(FMT, val).size(); \
+        if (widths[fieldIdx] < len) { \
+            widths[fieldIdx] = len; \
+        } \
+    }
+
+std::vector<int16_t> CmdProcessor::calColumnWidths(
+        const RowSetReader* dataReader) const {
+    std::vector<int16_t> widths;
+
+    // Check column names first
+    auto* schema = dataReader->schema();
+    for (int i = 0; i < schema->getNumFields(0); i++) {
+        widths.emplace_back(strlen(schema->getFieldName(i, 0)));
+    }
+
+    // TODO Then check data width
+    auto rowIt = dataReader->begin();
+    while (bool(rowIt)) {
+        int32_t fieldIdx = 0;
+        auto fieldIt = rowIt->begin();
+        while (bool(fieldIt)) {
+            switch (schema->getFieldType(fieldIdx, 0)->get_type()) {
+                case cpp2::SupportedType::BOOL: {
+                    // Enough to hold "false"
+                    if (widths[fieldIdx] < 5) {
+                        widths[fieldIdx] = 5;
+                    }
+                    break;
+                }
+                case cpp2::SupportedType::INT: {
+                    GET_VALUE_WIDTH(int64_t, Int, "%d")
+                    break;
+                }
+                case cpp2::SupportedType::FLOAT: {
+                    GET_VALUE_WIDTH(float, Float, "%f")
+                    break;
+                }
+                case cpp2::SupportedType::DOUBLE: {
+                    GET_VALUE_WIDTH(double, Double, "%f")
+                    break;
+                }
+                case cpp2::SupportedType::STRING: {
+                    folly::StringPiece val;
+                    if (fieldIt->getString(val) == ResultType::SUCCEEDED) {
+                        if (widths[fieldIdx] < val.size()) {
+                            widths[fieldIdx] = val.size();
+                        }
+                    }
+                    break;
+                }
+                case cpp2::SupportedType::VID: {
+                    // Enough to hold "0x{16 letters}"
+                    if (widths[fieldIdx] < 18) {
+                        widths[fieldIdx] = 18;
+                    }
+                    break;
+                }
+                default: {
+                }
+            }
+            ++fieldIt;
+            ++fieldIdx;
+        }
+        ++rowIt;
+    }
+
+    return std::move(widths);
+}
+#undef GET_VALUE_WIDTH
+
+
+int32_t CmdProcessor::printResult(const RowSetReader* dataReader) const {
+    std::vector<int16_t> widths = calColumnWidths(dataReader);
+    int32_t sum = 0;
+    for (auto w : widths) {
+        sum += w;
+    }
+
+    std::string headerLine(sum + 3 * widths.size() + 1, '=');
+    std::string rowLine(sum + 3 * widths.size() + 1, '-');
+    std::cout << headerLine << "\n|";
+
+    std::vector<std::string> formats = printHeader(dataReader, widths);
+    std::cout << headerLine << "\n";
+
+    int32_t numRows = printData(dataReader, rowLine, formats);
+    return numRows;
+}
+
+
+std::vector<std::string> CmdProcessor::printHeader(
+        const RowSetReader* dataReader,
+        const std::vector<int16_t>& widths) const {
+    std::vector<std::string> formats;
+
+    auto* schema = dataReader->schema();
+    for (int i = 0; i < schema->getNumFields(0); i++) {
+        std::string fmt = folly::stringPrintf(" %%%ds |", widths[i]);
+        std::cout << folly::stringPrintf(fmt.c_str(), schema->getFieldName(i, 0));
+        switch (schema->getFieldType(i, 0)->get_type()) {
+            case cpp2::SupportedType::BOOL: {
+                formats.emplace_back(folly::stringPrintf(" %%%ds |", widths[i]));
+                break;
+            }
+            case cpp2::SupportedType::INT: {
+                formats.emplace_back(folly::stringPrintf(" %%%dld |", widths[i]));
+                break;
+            }
+            case cpp2::SupportedType::FLOAT: {
+                formats.emplace_back(folly::stringPrintf(" %%%df |", widths[i]));
+                break;
+            }
+            case cpp2::SupportedType::DOUBLE: {
+                formats.emplace_back(folly::stringPrintf(" %%%df |", widths[i]));
+                break;
+            }
+            case cpp2::SupportedType::STRING: {
+                formats.emplace_back(folly::stringPrintf(" %%%ds |", widths[i]));
+                break;
+            }
+            case cpp2::SupportedType::VID: {
+                formats.emplace_back(folly::stringPrintf(" %%%sX |", widths[i]));
+                break;
+            }
+            default: {
+                formats.emplace_back(" *ERROR* |");
+            }
+        }
+    }
+    std::cout << "\n";
+
+    return std::move(formats);
+}
+
+
+#define PRINT_FIELD_VALUE(VT, FN, VAL) \
+    VT val; \
+    if (fIt->get ## FN (val) == ResultType::SUCCEEDED) { \
+        std::cout << folly::stringPrintf(formats[fIdx].c_str(), (VAL)); \
+    } else { \
+        std::cout << " *BAD* |"; \
+    }
+
+int32_t CmdProcessor::printData(const RowSetReader* dataReader,
+                                const std::string& rowLine,
+                                const std::vector<std::string>& formats) const {
+    int32_t numRows = 0;
+
+    auto* schema = dataReader->schema();
+    auto rowIt = dataReader->begin();
+    while (bool(rowIt)) {
+        int32_t fIdx = 0;
+        auto fIt = rowIt->begin();
+        std::cout << "|";
+        while (bool(fIt)) {
+            switch (schema->getFieldType(fIdx, 0)->get_type()) {
+                case cpp2::SupportedType::BOOL: {
+                    PRINT_FIELD_VALUE(bool, Bool, (val ? "true" : "false"))
+                    break;
+                }
+                case cpp2::SupportedType::INT: {
+                    PRINT_FIELD_VALUE(int64_t, Int, val)
+                    break;
+                }
+                case cpp2::SupportedType::FLOAT: {
+                    PRINT_FIELD_VALUE(float, Float, val)
+                    break;
+                }
+                case cpp2::SupportedType::DOUBLE: {
+                    PRINT_FIELD_VALUE(double, Double, val)
+                    break;
+                }
+                case cpp2::SupportedType::STRING: {
+                    PRINT_FIELD_VALUE(folly::StringPiece, String, val.toString().c_str())
+                    break;
+                }
+                case cpp2::SupportedType::VID: {
+                    PRINT_FIELD_VALUE(int64_t, Vid, val)
+                    break;
+                }
+                default: {
+                    std::cout << " *ERROR* |";
+                }
+            }
+            ++fIt;
+            ++fIdx;
+        }
+        std::cout << "\n" << rowLine << "\n";
+        ++numRows;
+        ++rowIt;
+    }
+
+    return numRows;
+}
+#undef PRINT_FIELD_VALUE
+
+
+bool CmdProcessor::processClientCmd(folly::StringPiece cmd,
+                                    bool& readyToExit) {
+    if (cmd == "exit") {
+        readyToExit = true;
+        return true;
+    } else {
+        readyToExit = false;
+    }
+
+    // TODO(sye) Check for all client commands
+
+    return false;
+}
+
+
+void CmdProcessor::processServerCmd(folly::StringPiece cmd) {
+    time::Duration dur;
+    std::unique_ptr<RowSetReader> dataReader;
+    int32_t res = client_->execute(cmd, dataReader);
+    if (!res) {
+        // Succeeded
+        auto* schema = dataReader->schema();
+        int32_t numRows = 0;
+        if (dataReader->schema()) {
+            // Only print when the schema is no empty
+            numRows = printResult(dataReader.get());
+        }
+        std::cout << "Got " << numRows << " rows (Time spent: "
+                  << client_->getServerLatency() << "/"
+                  << dur.elapsedInMSec() << " ms)\n";
+    } else {
+        std::cout << "[ERROR (" << res << ")]  "
+                  << client_->getErrorStr() << "\n";
+    }
+}
+
+
+bool CmdProcessor::process(folly::StringPiece cmd) {
+    bool exit;
+    if (processClientCmd(cmd, exit)) {
+        return !exit;
+    }
+
+    processServerCmd(cmd);
+
+    return true;
+}
+
+}  // namespace vgraph
+}  // namespace vesoft
+
diff --git a/src/console/CmdProcessor.h b/src/console/CmdProcessor.h
new file mode 100644
index 0000000000000000000000000000000000000000..88b5a1be8ed1af1fb461d30512d523837fb5752a
--- /dev/null
+++ b/src/console/CmdProcessor.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2018 - present, VE Software Inc. All rights reserved
+ *
+ * This source code is licensed under Apache 2.0 License
+ *  (found in the LICENSE.Apache file in the root directory)
+ */
+
+#ifndef CONSOLE_CMDPROCESSOR_H_
+#define CONSOLE_CMDPROCESSOR_H_
+
+#include "base/Base.h"
+#include "client/cpp/GraphDbClient.h"
+
+namespace vesoft {
+namespace vgraph {
+
+class CmdProcessor final {
+public:
+    explicit CmdProcessor(std::unique_ptr<GraphDbClient> client)
+        : client_(std::move(client)) {}
+    ~CmdProcessor() = default;
+
+    // Process the given command
+    //
+    // The method returns false when it's ready to exit, otherwise
+    // the method returns true
+    bool process(folly::StringPiece cmd);
+
+private:
+    std::unique_ptr<GraphDbClient> client_;
+
+    // The method returns true if the given command is a client command
+    // and has been processed. Otherwise, the method returns false
+    bool processClientCmd(folly::StringPiece cmd, bool& readyToExit);
+
+    void processServerCmd(folly::StringPiece cmd);
+
+    std::vector<int16_t> calColumnWidths(const RowSetReader* dataReader) const;
+
+    int32_t printResult(const RowSetReader* dataReader) const;
+    std::vector<std::string> printHeader(const RowSetReader* dataReader,
+                                         const std::vector<int16_t>& widths) const;
+    // The method returns the total number of rows
+    int32_t printData(const RowSetReader* dataReader,
+                      const std::string& rowLine,
+                      const std::vector<std::string>& formats) const;
+};
+
+}  // namespace vgraph
+}  // namespace vesoft
+#endif  // CONSOLE_CMDPROCESSOR_H_
diff --git a/src/console/GraphDbConsole.cpp b/src/console/GraphDbConsole.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2d5ab9ef9a56a07bab0872e5e6c84bb54779c2b2
--- /dev/null
+++ b/src/console/GraphDbConsole.cpp
@@ -0,0 +1,29 @@
+/* Copyright (c) 2018 - present, VE Software Inc. All rights reserved
+ *
+ * This source code is licensed under Apache 2.0 License
+ *  (found in the LICENSE.Apache file in the root directory)
+ */
+
+#include "base/Base.h"
+#include "console/CliManager.h"
+
+DEFINE_string(addr, "127.0.0.1", "GraphDB daemon IP address");
+DEFINE_int32(port, 34500, "GraphDB daemon listening port");
+DEFINE_string(username, "", "Username used to authenticate");
+DEFINE_string(password, "", "Password used to authenticate");
+
+
+int main(int argc, char *argv[]) {
+    folly::init(&argc, &argv, true);
+
+    using namespace vesoft::vgraph;
+
+    CliManager cli;
+    if (!cli.connect(FLAGS_addr, FLAGS_port, FLAGS_username, FLAGS_password)) {
+        exit(1);
+    }
+
+    cli.loop();
+}
+
+