diff --git a/.docker/Dockerfile b/.docker/Dockerfile
index 638a46aaa1491bd2601b240a6ede75d1d7b2306e..b894c62ae3953b96aca1e3a88da795d4c8f13a69 100644
--- a/.docker/Dockerfile
+++ b/.docker/Dockerfile
@@ -1,6 +1,5 @@
-FROM debian:latest
+FROM debian:buster-backports
 
-RUN echo "deb http://deb.debian.org/debian buster-backports main" > /etc/apt/sources.list.d/buster-backports.list
 RUN apt-get update
 RUN apt-get install -y cmake/buster-backports
 RUN apt-get install -y lcov
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eb3340b0a9e3f8fe4786f3cd497c10edf225510b..91c0423ade5bd19d649237dc0a4a14eec0301300 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,12 +20,16 @@
 
 cmake_minimum_required(VERSION 3.14)
 
-set(libcaosdb_VERSION 0.0.4)
+set(libcaosdb_VERSION 0.0.5)
+set(libcaosdb_COMPATIBLE_SERVER_VERSION_MAJOR 0)
+set(libcaosdb_COMPATIBLE_SERVER_VERSION_MINOR 5)
+set(libcaosdb_COMPATIBLE_SERVER_VERSION_PATCH 0)
+set(libcaosdb_COMPATIBLE_SERVER_VERSION_PRE_RELEASE "GRPC${libcaosdb_VERSION}")
 
 project(libcaosdb
     VERSION ${libcaosdb_VERSION}
-    DESCRIPTION "C++ client libraries for CaosDB"
-    LANGUAGES CXX)
+    DESCRIPTION "C and C++ client libraries for CaosDB"
+    LANGUAGES CXX C)
 
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -100,7 +104,6 @@ foreach(i RANGE "${len_proto_files}")
         "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}.grpc.pb.h")
     list(APPEND GRPC_GENERATED_HEADERS "${next_proto_hdr}" "${next_grpc_hdr}")
     list(APPEND GRPC_GENERATED_SOURCES "${next_proto_src}" "${next_grpc_src}")
-
 endforeach()
 
 set(GRPC_GENERATED
@@ -116,28 +119,76 @@ add_custom_command(
         ${PROTO_FILES}
       DEPENDS ${PROTO_FILES})
 
+###############################################################################
+### Set up main targets
+### * [caosdb_grpc] - only in Debug builds. Otherwise this library is compiled
+###   into caosdb libraray
+### * caosdb (links to caosdb_grpc) - The main library.
+### * cxxcaosdbcli - A C++ test client.
+### * ccaosdb - A C-Wrapper of the C++ caosdb library.
+### * ccaosdbcli - A plain C test client.
+###############################################################################
 
 if("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
-    add_library(caosdb_grpc STATIC ${GRPC_GENERATED})
-    add_library(caosdb STATIC ${libcaosdb_INCL} ${libcaosdb_SRC})
+  add_library(caosdb_grpc SHARED ${GRPC_GENERATED})
+  add_library(caosdb SHARED ${libcaosdb_INCL} ${libcaosdb_SRC})
     target_link_libraries(caosdb caosdb_grpc)
     set(LIBCAOSDB caosdb caosdb_grpc)
+
+    target_include_directories(caosdb_grpc PUBLIC
+        $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
+        $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
+
+        $<INSTALL_INTERFACE:include>
+        ${CONAN_INCLUDE_DIRS}
+    )
 else()
     add_library(caosdb
-        STATIC ${libcaosdb_INCL} ${libcaosdb_SRC} ${GRPC_GENERATED})
+        SHARED ${libcaosdb_INCL} ${libcaosdb_SRC} ${GRPC_GENERATED})
     set(LIBCAOSDB caosdb)
 endif()
-add_executable(caosdbcli src/caosdbcli.cpp)
-
 target_link_libraries(caosdb
     ${CONAN_LIBS}
 )
+target_include_directories(caosdb PUBLIC
+    $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
+    $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
+    $<INSTALL_INTERFACE:include>
+    ${CONAN_INCLUDE_DIRS}
+)
 
-target_link_libraries(caosdbcli
+add_library(ccaosdb SHARED src/ccaosdb.cpp)
+target_link_libraries(ccaosdb
     ${LIBCAOSDB}
     ${CONAN_LIBS}
 )
 
+add_executable(ccaosdbcli src/ccaosdbcli.c)
+target_include_directories(ccaosdbcli PUBLIC
+    $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
+    $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
+    $<INSTALL_INTERFACE:include>
+    ${CONAN_INCLUDE_DIRS}
+)
+target_link_libraries(ccaosdbcli
+    ccaosdb
+    ${CONAN_LIBS}
+)
+
+add_executable(cxxcaosdbcli src/cxxcaosdbcli.cpp)
+target_include_directories(cxxcaosdbcli PUBLIC
+    $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
+    $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
+    $<INSTALL_INTERFACE:include>
+    ${CONAN_INCLUDE_DIRS}
+)
+target_link_libraries(cxxcaosdbcli
+    ${LIBCAOSDB}
+    ${CONAN_LIBS}
+)
+
+
+
 #######################################################
 ### LINTING with CLANG-TIDY and INCLUDE-WHAT-YOU-USE
 #######################################################
@@ -165,9 +216,12 @@ if(_LINTING)
         set_target_properties(caosdb PROPERTIES
             CXX_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}"
             )
-        set_target_properties(caosdbcli PROPERTIES
+        set_target_properties(cxxcaosdbcli PROPERTIES
             CXX_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}"
             )
+        set_target_properties(ccaosdbcli PROPERTIES
+            C_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}"
+            )
     endif()
 
     find_program(clang_tidy NAMES clang-tidy clang-tidy-11)
@@ -176,7 +230,7 @@ if(_LINTING)
     else()
         message(STATUS "clang-tidy: ${clang_tidy}")
         set(_CMAKE_CXX_CLANG_TIDY_CHECKS
-            "--checks=*,-fuchsia-*,-llvm-include-order,-llvmlibc-*")
+            "--checks=*,-fuchsia-*,-llvm-include-order,-llvmlibc-*,-readability-convert-member-functions-to-static,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay,-llvm-else-after-return,-readability-else-after-return")
         set(_CMAKE_CXX_CLANG_TIDY "${clang_tidy}"
             "--header-filter=caosdb/.*[^\(\.pb\.h\)]$"
             "--warnings-as-errors=*")
@@ -189,9 +243,12 @@ if(_LINTING)
         set_target_properties(caosdb PROPERTIES
             CXX_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}"
             )
-        set_target_properties(caosdbcli PROPERTIES
+        set_target_properties(cxxcaosdbcli PROPERTIES
             CXX_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}"
             )
+        set_target_properties(ccaosdbcli PROPERTIES
+            C_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}"
+            )
     endif()
 endif()
 
@@ -212,36 +269,10 @@ endif()
 set(libcaosdb_INCLUDE_DEST "include/caosdb")
 set(libcaosdb_LIB_DEST "lib")
 
-target_include_directories(caosdb PUBLIC
-    # headers to include when building from source
-    $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
-    $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
-
-    # headers to include when installing  (implicitly prefixes with ${CMAKE_INSTALL_PREFIX}).
-    $<INSTALL_INTERFACE:include>
-)
-
-if("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
-    target_include_directories(caosdb_grpc PUBLIC
-        # headers to include when building from source
-        $<BUILD_INTERFACE:${libcaosdb_SOURCE_DIR}/include>
-        $<BUILD_INTERFACE:${libcaosdb_BINARY_DIR}/include>
-
-        # headers to include when installing  (implicitly prefixes with ${CMAKE_INSTALL_PREFIX}).
-        $<INSTALL_INTERFACE:include>
-     )
-endif()
-
-target_include_directories(caosdbcli PUBLIC
-   ${libcaosdb_SOURCE_DIR}/include>
-   ${libcaosdb_BINARY_DIR}/include>
-   ${CONAN_INCLUDE_DIRS}
-)
-
 set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/.local/")
 install(
     # targets to install
-    TARGETS ${LIBCAOSDB}
+    TARGETS ${LIBCAOSDB} ccaosdb
     # name of the CMake "export group" containing the targets we want to install
     EXPORT caosdbTargets
     # Dynamic, static library and include destination locations after running
@@ -266,9 +297,29 @@ install(
     DESTINATION ${libcaosdb_CMAKE_DEST}
 )
 
-install(FILES ${libcaosdb_INCL} DESTINATION ${libcaosdb_INCLUDE_DEST})
-install(FILES ${GRPC_GENERATED_HEADERS}
-    DESTINATION ${libcaosdb_INCLUDE_DEST}/${hw_hdrs_path})
+install(FILES ${libcaosdb_INCL} ${PROJECT_SOURCE_DIR}/include/ccaosdb.h DESTINATION ${libcaosdb_INCLUDE_DEST})
+foreach(i RANGE "${len_proto_files}")
+    list(GET PROTO_FILES ${i} next_proto_file)
+
+    # strip away the prefix path and the ".proto" suffix
+    string(REPLACE
+        "${PROJECT_SOURCE_DIR}/proto/proto/caosdb/"
+        ""
+        next_proto_module
+        "${next_proto_file}")
+    string(REPLACE
+        "/main.proto"
+        ""
+        next_proto_module
+        "${next_proto_module}")
+    set(next_proto_hdr
+        "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}/main.pb.h")
+    set(next_grpc_hdr
+        "${CMAKE_CURRENT_BINARY_DIR}/include/caosdb/${next_proto_module}/main.grpc.pb.h")
+    install(FILES ${next_proto_hdr} ${next_grpc_hdr} DESTINATION
+        ${libcaosdb_INCLUDE_DEST}/${next_proto_module})
+endforeach()
+
 install(FILES ${PROJECT_SOURCE_DIR}/caosdbConfig.cmake
     DESTINATION ${libcaosdb_CMAKE_DEST})
 
@@ -287,10 +338,13 @@ install(FILES ${PROJECT_SOURCE_DIR}/caosdbConfigVersion.cmake
 #######################################################
 option(AUTOFORMATTING "call clang-format at configure time" ON)
 if(AUTOFORMATTING)
-    file(GLOB format_test_sources test/*.cpp test/*.h)
+    file(GLOB format_test_sources test/*.cpp test/*.h test/*.h.in)
     execute_process(COMMAND clang-format -i --verbose ${libcaosdb_INCL}
         ${libcaosdb_SRC} ${libcaosdb_TEST_SRC}
-        ${PROJECT_SOURCE_DIR}/src/caosdbcli.cpp
+        ${PROJECT_SOURCE_DIR}/src/cxxcaosdbcli.cpp
+        ${PROJECT_SOURCE_DIR}/src/ccaosdbcli.c
+        ${PROJECT_SOURCE_DIR}/src/ccaosdb.cpp
+        ${PROJECT_SOURCE_DIR}/include/ccaosdb.h
         ${format_test_sources}
         WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
 endif()
diff --git a/README_SETUP.md b/README_SETUP.md
index c68df92223ff44be9c7d26a1896876474e320cdc..bcda25acc80f00ef9801357f786d973face177fa 100644
--- a/README_SETUP.md
+++ b/README_SETUP.md
@@ -9,6 +9,26 @@ We use [cmake](https://cmake.org) as build tool.
 3. `cmake -B . ..`
 4. `cmake --build .`
 
+You may also want to install libcaosdb system-wide to
+`CMAKE_INSTALL_PREFIX/lib` by
+
+5. `cmake --install .`
+
+The default install prefix is `~/.local`. It can be set by adding
+`-DCMAKE_INSTALL_PREFIX=/path/to/install/prefix` to the first cmake
+command (3.).
+
+### How to build on MacOS
+
+Instead of the above conan command (2.) use
+
+2. `conan install .. -s "cppstd=11"`
+
+and continue as you would when building on a Linux system. You may
+have to add `build/lib/` (or, alternatively after installation,
+`CMAKE_INSTALL_PREFIX/lib`) to your `DYLD_LIBRARY_PATH` environmental
+variable.
+
 ## Unit Tests
 
 ### Build
@@ -52,6 +72,26 @@ The coverage report can be viewed in a browser by opening
 
 Please adhere to [Google's C++ naming conventions](https://google.github.io/styleguide/cppguide.html#Naming).
 
+## Client Configuration
+
+You can use a json file for the configuration of the client. See
+`test/test_data/test_caosdb_client.json` for an example. You may use
+`caosdb-client-configuration-schema.json` to validate your schema.
+
+The client will load the configuration file from the first existing
+file in the following locations (precedence from highest to lowest):
+
+1. A file specified by the environment variable
+   `$CAOSDB_CLIENT_CONFIGURATION`.
+2. `$PWD/caosdb_client.json`
+3. `$PWD/caosdb-client.json`
+4. `$PWD/.caosdb_client.json`
+5. `$PWD/.caosdb-client.json`
+6. `$HOME/caosdb_client.json`
+7. `$HOME/caosdb-client.json`
+8. `$HOME/.caosdb_client.json`
+9. `$HOME/.caosdb-client.json`
+
 ## Documentation
 
 In the build directory, run
diff --git a/caosdb-client-configuration-schema.json b/caosdb-client-configuration-schema.json
new file mode 100644
index 0000000000000000000000000000000000000000..882f757636ee82cce994c779b5e174276a89540d
--- /dev/null
+++ b/caosdb-client-configuration-schema.json
@@ -0,0 +1,86 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "CaosDB Client Configuration",
+  "description": "Configuration of the connection, logging, and other things of a CaosDB client.",
+  "type": "object",
+  "additionalProperties": false,
+  "properties": {
+    "connections": {
+      "type": "object",
+      "description": "Named connection configurations.",
+      "properties": {
+        "default": {
+          "oneOf": [
+            {
+              "type": "string",
+              "description": "Name of the default connection."
+            }, {
+              "$ref": "#/definitions/connection_configuration"
+            }
+          ]
+        }
+      },
+      "additionalProperties": {
+        "$ref": "#/definitions/connection_configuration"
+      }
+    },
+    "extension": {
+      "type": "object",
+      "description": "A reserved configuration object which may be used to store additional options for particular clients and special extensions.",
+      "additionalProperties": true
+    }
+  },
+  "definitions": {
+    "connection_configuration": {
+      "type": "object",
+      "description": "A single connection configuration.",
+      "additionalProperties": false,
+      "properties": {
+        "name": { "type": "string" },
+        "host": {
+          "type": "string",
+          "description": "Domain name or ip address of the server host.",
+          "default": "localhost",
+          "examples": ["localhost", "caosdb.example.com", "192.168.0.123"]
+        },
+        "port": {
+          "type": "integer",
+          "description": "Ip port of the grpc end-point of the CaosDB server.",
+          "mininum": 1,
+          "default": 8443,
+          "maximum": 65535
+        },
+        "tls": {
+          "type": "boolean",
+          "description": "Indicates that the connection is using TLS (transport layer security) for authentication of the server and encryption of the communication. Setting this to 'false' is insecure unless other mechanism are in place to prevent a man-in-the-middle attack and eavesdropping by an unauthorized agent.",
+          "default": true
+        },
+        "server_certificate_path": {
+          "type": "string",
+          "description": "Relative or absolute path to a public certificate of a trusted CA (certificate authority) in PEM format. If not specified, the client system's default directory for certificates will be scanned (that is '/etc/ssl/certs/' in many linux distros).",
+          "default": null
+        },
+        "authentication": {
+          "type": "object",
+          "description": "Configuration of the user authentication. If the authentication property is not set, the client attempts to connect to the server as an anonymous user.",
+          "additionalProperties": false,
+          "properties": {
+            "type": {
+              "type": "string",
+              "enum": ["plain"]
+            },
+            "username": { "type": "string" },
+            "password": { "type": "string" },
+            "auth_token": { "type": "string" }
+          },
+          "allOf": [
+            {
+              "if": {"properties": {"type": { "pattern": "^plain$" } } },
+              "then": {"required": ["username", "password"] }
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/conanfile.py b/conanfile.py
index b32c78388637335d93b03bd972e6e5fed5c6dd3b..1ed1abd041788b27a8a63c7286528fbed7cf4740 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -3,7 +3,7 @@ from conans import ConanFile, CMake, tools
 
 class CaosdbConan(ConanFile):
     name = "caosdb"
-    version = "0.0.4"
+    version = "0.0.5"
     license = "AGPL-3.0-or-later"
     author = "Timm C. Fitschen <t.fitschen@indiscale.com>"
     url = "https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib.git"
@@ -14,12 +14,13 @@ class CaosdbConan(ConanFile):
     default_options = {"shared": False, "fPIC": True}
     generators = "cmake"
     requires = [("boost/1.76.0"), ("gtest/1.11.0"), ("grpc/1.38.0")]
-    exports = "*.cpp", "*h", "*.cmake", "*CMakeLists.txt", "*.in", "*.proto"
+    exports = "*.cpp", "*.h", "*.cmake", "*CMakeLists.txt", "*.in", "*.proto", "*.c"
     exports_sources = "src", "doc", "include", "test", "cmake", "proto"
 
     def config_options(self):
         if self.settings.os == "Windows":
             del self.options.fPIC
+        self.options["boost"].without_python = True
 
     # def source(self):
         # self.run("git clone https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib.git")
@@ -47,4 +48,4 @@ class CaosdbConan(ConanFile):
         self.copy("*.a", dst="lib", keep_path=False)
 
     def package_info(self):
-        self.cpp_info.libs = ["caosdb"]
+        self.cpp_info.libs = ["caosdb", "ccaosdb"]
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 5ea0ae59c1110413bc35461d47e068b572641c09..76d7dad56583120795d49f1828998315597e803c 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -21,9 +21,11 @@
 find_package(Doxygen)
 
 if (DOXYGEN_FOUND)
-    string(REPLACE ";" " " DOXYGEN_INPUT "${libcaosdb_INCL}")
+    string(REPLACE ";" " " DOXYGEN_INPUT
+        "${libcaosdb_INCL} ${PROJECT_INCLUDE_DIR}/ccaosdb.h")
     configure_file(Doxyfile.in Doxyfile)
 
+
     # Note: do not put "ALL" - this builds docs together with application EVERY TIME!
     add_custom_target(doc-doxygen
         COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile
@@ -35,7 +37,9 @@ if (DOXYGEN_FOUND)
     if (SPHINX_CMD)
         configure_file(conf.py.in conf.py)
         configure_file(index.rst.in index.rst)
-        configure_file(api/index.rst.in api/index.rst)
+
+        # create C++ docs
+        configure_file(cppapi/index.rst.in cppapi/index.rst)
 
         # create rst pages for every header file
         list(LENGTH libcaosdb_INCL len_header_files)
@@ -43,20 +47,33 @@ if (DOXYGEN_FOUND)
         foreach (i RANGE "${len_header_files}")
             list(GET libcaosdb_INCL ${i} HEADER_FILE)
             string(REPLACE
-                "${PROJECT_INCLUDE_DIR}/caosdb/"
+                "${PROJECT_INCLUDE_DIR}/"
                 ""
                 HEADER_FILE_NAME
                 ${HEADER_FILE})
             string(REPLACE
-                "${CMAKE_BINARY_DIR}/include/caosdb/"
+                "${CMAKE_BINARY_DIR}/include/"
                 ""
                 HEADER_FILE_NAME
                 ${HEADER_FILE_NAME})
+            string(REPLACE
+                "caosdb/"
+                ""
+                DOC_FILE_NAME
+                ${HEADER_FILE_NAME})
             configure_file(
-                api/header_file.rst.in
-                api/_${HEADER_FILE_NAME}.rst)
+                header_file.rst.in
+                cppapi/_${DOC_FILE_NAME}.rst)
         endforeach ()
 
+        # create (plain) C docs
+        configure_file(capi/index.rst.in capi/index.rst)
+        set(HEADER_FILE_NAME ccaosdb.h)
+        set(HEADER_FILE ccaosdb.h)
+        configure_file(
+            header_file.rst.in
+            capi/_ccaosdb.rst)
+
         add_custom_target(doc-sphinx
             COMMAND ${SPHINX_CMD}
                 -b html
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index d0f329b99f37f6baeb05424f73decdb96ccc4cef..798198a973a2996a22402e771d4712b9ec31eafa 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -68,7 +68,7 @@ OUTPUT_DIRECTORY       = doxygen_out
 # performance problems for the file system.
 # The default value is: NO.
 
-CREATE_SUBDIRS         = NO
+CREATE_SUBDIRS         = YES
 
 # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
 # characters to appear in the names of generated files. If set to NO, non-ASCII
@@ -170,7 +170,7 @@ FULL_PATH_NAMES        = YES
 # will be relative from the directory where doxygen is started.
 # This tag requires that the tag FULL_PATH_NAMES is set to YES.
 
-STRIP_FROM_PATH        =
+STRIP_FROM_PATH        = @DOXYGEN_STRIP_FROM_PATH@
 
 # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
 # path mentioned in the documentation of a class, which tells the reader which
@@ -944,7 +944,7 @@ FILE_PATTERNS          = *.c \
 # be searched for input files as well.
 # The default value is: NO.
 
-RECURSIVE              = NO
+RECURSIVE              = YES
 
 # The EXCLUDE tag can be used to specify files and/or directories that should be
 # excluded from the INPUT source files. This way you can easily exclude a
diff --git a/doc/api/.gitignore b/doc/api/.gitignore
deleted file mode 100644
index a463abd406508c6ad4c99ff83aa59e4196947228..0000000000000000000000000000000000000000
--- a/doc/api/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-_*.rst
diff --git a/doc/api/index.rst.in b/doc/capi/index.rst.in
similarity index 95%
rename from doc/api/index.rst.in
rename to doc/capi/index.rst.in
index 8a34c64d35cc72179a07ca96d1ba5914b0d741f1..887f7ee90c7c6a419a90e6b67f00602fc7816f41 100644
--- a/doc/api/index.rst.in
+++ b/doc/capi/index.rst.in
@@ -19,12 +19,13 @@
     # along with this program. If not, see <https://www.gnu.org/licenses/>.
     #
 
-.. _api_root:
+.. _capi_root:
 
-API
-===
+C API
+=====
 
 .. toctree::
     :glob:
 
-    *
+    _*
+    _*/*
diff --git a/doc/conf.py.in b/doc/conf.py.in
index bdc3216f5dae8184a7e3e286225bb4cbd9793af4..f04a7a83ac79d555d819528d3ae6d92ed63580ee 100644
--- a/doc/conf.py.in
+++ b/doc/conf.py.in
@@ -20,10 +20,12 @@
 project = '@CMAKE_PROJECT_NAME@'
 copyright = '2021 IndiScale GmbH'
 author = 'Timm Fitschen'
+version = '@CMAKE_PROJECT_VERSION@'
+release = '@CMAKE_PROJECT_VERSION@'
+
 
 rst_prolog = """
 .. |PROJECT_NAME| replace:: @CMAKE_PROJECT_NAME@
-.. |PROJECT_VERSION| replace:: @CMAKE_PROJECT_VERSION@
 """
 
 
diff --git a/doc/cppapi/index.rst.in b/doc/cppapi/index.rst.in
new file mode 100644
index 0000000000000000000000000000000000000000..c9780a92355ab99e7bbdb9cfc1c405c33c3efd30
--- /dev/null
+++ b/doc/cppapi/index.rst.in
@@ -0,0 +1,31 @@
+..
+    #
+    # This file is a part of the CaosDB Project.
+    #
+    # Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+    # Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+    #
+    # This program is free software: you can redistribute it and/or modify
+    # it under the terms of the GNU Affero General Public License as
+    # published by the Free Software Foundation, either version 3 of the
+    # License, or (at your option) any later version.
+    #
+    # This program is distributed in the hope that it will be useful,
+    # but WITHOUT ANY WARRANTY; without even the implied warranty of
+    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    # GNU Affero General Public License for more details.
+    #
+    # You should have received a copy of the GNU Affero General Public License
+    # along with this program. If not, see <https://www.gnu.org/licenses/>.
+    #
+
+.. _cppapi_root:
+
+C++ API
+=======
+
+.. toctree::
+    :glob:
+
+    _*
+    _*/*
diff --git a/doc/api/header_file.rst.in b/doc/header_file.rst.in
similarity index 100%
rename from doc/api/header_file.rst.in
rename to doc/header_file.rst.in
diff --git a/doc/index.rst.in b/doc/index.rst.in
index 086a2f4096ca7d54a57c1dc01db1898f46f1a230..e3fac2458d21571721f412adb3c120264f14dee6 100644
--- a/doc/index.rst.in
+++ b/doc/index.rst.in
@@ -25,16 +25,14 @@
 Welcome to |PROJECT_NAME|'s documentation!
 ==========================================
 
-Version: |PROJECT_VERSION|
-
 This is work in progress.
 
-
 .. toctree::
     :maxdepth: 4
     :caption: Contents:
 
     Welcome <self>
-    api/index
+    cppapi/index
+    capi/index
 
 * :ref:`genindex`
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 89657f1b2ac9c4798b9a1d744416cb2a0125a298..48f470b38e022dd9deb7c00bf53a09ac62091076 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -21,13 +21,14 @@
 # add all header files to this list
 set(libcaosdb_INCL
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/authentication.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/configuration.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/connection.h
     ${CMAKE_CURRENT_BINARY_DIR}/caosdb/constants.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/entity.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/exceptions.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/info.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction.h
-    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/utils.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/utility.h
     )
 
 # pass variable to parent scope
diff --git a/include/caosdb/authentication.h b/include/caosdb/authentication.h
index d8c3595ddb8a89be2a4d0011d37d6e7fc4a480ee..8071414ef436619def4caf6881ebca027b757a60 100644
--- a/include/caosdb/authentication.h
+++ b/include/caosdb/authentication.h
@@ -22,7 +22,7 @@
 #ifndef CAOSDB_AUTHENTICATION_H
 #define CAOSDB_AUTHENTICATION_H
 /**
- * @file authentication.h
+ * @file caosdb/authentication.h
  * @author Timm Fitschen
  * @date 2021-06-28
  * @brief Configuration and setup of the client authentication.
@@ -32,7 +32,7 @@
 #include <map>                                         // for multimap
 #include <memory>                                      // for shared_ptr
 #include <string>                                      // for string
-#include "caosdb/utils.h"                              // for base64_encode
+#include "caosdb/utility.h"                            // for base64_encode
 #include "grpcpp/impl/codegen/interceptor.h"           // for Status
 #include "grpcpp/impl/codegen/security/auth_context.h" // for AuthContext
 #include "grpcpp/impl/codegen/status.h"                // for Status
@@ -40,7 +40,7 @@
 
 namespace caosdb {
 namespace authentication {
-using caosdb::utils::base64_encode;
+using caosdb::utility::base64_encode;
 using grpc::AuthContext;
 using grpc::MetadataCredentialsPlugin;
 using grpc::Status;
@@ -51,7 +51,8 @@ using grpc::string_ref;
  */
 class Authenticator {
 public:
-  [[nodiscard]] virtual auto getCallCredentials() const
+  virtual ~Authenticator() = default;
+  [[nodiscard]] virtual auto GetCallCredentials() const
     -> std::shared_ptr<grpc::CallCredentials> = 0;
 };
 
@@ -82,7 +83,7 @@ public:
   PlainPasswordAuthenticator(const std::string &username,
                              const std::string &password);
 
-  [[nodiscard]] auto getCallCredentials() const
+  [[nodiscard]] auto GetCallCredentials() const
     -> std::shared_ptr<grpc::CallCredentials> override;
 };
 } // namespace authentication
diff --git a/include/caosdb/configuration.h b/include/caosdb/configuration.h
new file mode 100644
index 0000000000000000000000000000000000000000..6a59880964f4cec402998e11529b60d459bdc338
--- /dev/null
+++ b/include/caosdb/configuration.h
@@ -0,0 +1,221 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CAOSDB_CONFIGURATION_H
+#define CAOSDB_CONFIGURATION_H
+#include <memory>                          // for unique_ptr
+#include <string>                          // for string
+#include "boost/filesystem/operations.hpp" // for exists
+#include "boost/filesystem/path.hpp"       // for path
+#include "boost/json/object.hpp"           // for object
+#include "boost/json/value.hpp"            // for value
+#include "boost/json/value_ref.hpp"        // for array, object
+#include "caosdb/authentication.h"         // for Authenticator, PlainPassw...
+#include "caosdb/connection.h" // for ConnectionConfiguration, Certifi...
+#include "caosdb/exceptions.h" // for ConfigurationError
+#include "caosdb/utility.h"    // for load_json_file
+
+namespace caosdb::configuration {
+using boost::filesystem::exists;
+using boost::filesystem::path;
+using boost::json::array;
+using boost::json::object;
+using boost::json::value;
+using caosdb::authentication::Authenticator;
+using caosdb::authentication::PlainPasswordAuthenticator;
+using caosdb::connection::CertificateProvider;
+using caosdb::connection::ConnectionConfiguration;
+using caosdb::connection::ConnectionManager;
+using caosdb::connection::InsecureConnectionConfiguration;
+using caosdb::connection::PemFileCertificateProvider;
+using caosdb::connection::TlsConnectionConfiguration;
+using caosdb::exceptions::ConfigurationError;
+using caosdb::utility::load_json_file;
+
+/**
+ * Helper class (no state, just member functions) which should only be used by
+ * the ConfigurationManager to construct Connection instances from the stored
+ * configuration.
+ */
+class ConnectionConfigurationHelper {
+public:
+  friend class ConfigurationManager;
+
+private:
+  /**
+   * @param from - a single connection configuration.
+   */
+  inline auto CreateCertificateProvider(const object &from) const
+    -> std::unique_ptr<CertificateProvider>;
+
+  /**
+   * @param from - a single connection configuration.
+   */
+  auto CreateAuthenticator(const object &from) const
+    -> std::unique_ptr<Authenticator>;
+
+  /**
+   * @param from - a single connection configuration.
+   */
+  auto
+  CreateConnectionConfiguration(const bool tls, const std::string &host,
+                                const int port,
+                                const CertificateProvider *certificate_provider,
+                                const Authenticator *authenticator) const
+    -> std::unique_ptr<ConnectionConfiguration>;
+
+  /**
+   * @param from - a single connection configuration.
+   */
+  auto IsTls(const object &from) const -> bool;
+
+  /**
+   * @param from - a single connection configuration.
+   */
+  auto CreateConnectionConfiguration(const object &from) const
+    -> std::unique_ptr<ConnectionConfiguration>;
+};
+
+/**
+ * Reads the configuration file and keeps the configuration. Singleton.
+ *
+ * Currently, this class can only read a single configuration file. No merging
+ * or overwriting is supported.
+ */
+class ConfigurationManager {
+public:
+  static ConfigurationManager &GetInstance() {
+    static ConfigurationManager instance;
+    return instance;
+  };
+
+  /**
+   * See mReset.
+   */
+  inline static auto Reset() -> void { GetInstance().mReset(); }
+
+  /**
+   * See mClear.
+   */
+  inline static auto Clear() -> void { GetInstance().mClear(); }
+
+  /**
+   * See mLoadSingleJSONConfiguration.
+   */
+  inline static auto LoadSingleJSONConfiguration(const path &json_file)
+    -> void {
+    GetInstance().mLoadSingleJSONConfiguration(json_file);
+  }
+
+  /**
+   * See mGetConnectionConfiguration.
+   */
+  inline static auto GetConnectionConfiguration(const std::string &name)
+    -> std::unique_ptr<ConnectionConfiguration> {
+    return GetInstance().mGetConnectionConfiguration(name);
+  }
+
+  /**
+   * Return the ConnectionConfiguration for the default connection.
+   */
+  inline static auto GetDefaultConnectionConfiguration()
+    -> std::unique_ptr<ConnectionConfiguration> {
+    return GetInstance().mGetConnectionConfiguration(
+      GetInstance().mGetDefaultConnectionName());
+  }
+
+  /**
+   * See mGetDefaultConnectionName.
+   */
+  inline static auto GetDefaultConnectionName() -> std::string {
+    return GetInstance().mGetDefaultConnectionName();
+  }
+
+  ConfigurationManager(ConfigurationManager const &) = delete;
+  void operator=(ConfigurationManager const &) = delete;
+
+private:
+  value json_configuration;
+  ConnectionConfigurationHelper connection_configuration_helper;
+  inline ConfigurationManager() { InitializeDefaults(); };
+
+  /**
+   * Initialize this ConfigurationManager with the defaults.
+   *
+   * Currently, this means, that the ConfigurationManager attempts to load the
+   * first existing file from the LIBCAOSDB_CONFIGURATION_FILES_PRECEDENCE list
+   * of file locations.
+   */
+  auto InitializeDefaults() -> void;
+
+  /**
+   * Return a json object representing the current configuration.
+   */
+  auto GetConfiguration() const -> const object &;
+
+  /**
+   * Return the connection configurations.
+   */
+  auto GetConnections() const -> const object &;
+
+  /**
+   * Return the configuration for the connection with the given name (as a json
+   * object).
+   */
+  auto GetConnection(const std::string &name) const -> const object &;
+
+  /**
+   * Reset this ConfigurationManager.
+   *
+   * The current configuration is deleted and a new configuration is being
+   * loaded via InitializeDefaults.
+   */
+  auto mReset() -> void;
+
+  /**
+   * Clear this ConfigurationManager.
+   *
+   * Afterwards, this ConfigurationManager is uninitilized.
+   *
+   * In contrast to mReset, this method only deletes the current configuration
+   * but does not load a new one via InitializeDefaults.
+   */
+  auto mClear() -> void;
+
+  /**
+   * Load a configuration from a json file.
+   */
+  auto mLoadSingleJSONConfiguration(const path &json_file) -> void;
+
+  /**
+   * Return the ConnectionConfiguration for the connection of the given name.
+   */
+  auto mGetConnectionConfiguration(const std::string &name) const
+    -> std::unique_ptr<ConnectionConfiguration>;
+
+  /**
+   * Return the ConnectionConfiguration for the default connection.
+   */
+  auto mGetDefaultConnectionName() const -> std::string;
+};
+
+} // namespace caosdb::configuration
+#endif
diff --git a/include/caosdb/connection.h b/include/caosdb/connection.h
index 0e722a512b22bbd18fdb18f3fcecaeeb7a0a64a7..2c58d57ce66375d1650834872d461464dd912be0 100644
--- a/include/caosdb/connection.h
+++ b/include/caosdb/connection.h
@@ -28,21 +28,16 @@
  * @brief Configuration and setup of the connection.
  */
 #include <iosfwd>                                // for ostream
+#include <map>                                   // for map
 #include <memory>                                // for shared_ptr, unique_ptr
-#include <string>                                // for string
+#include <string>                                // for string, basic_string
+#include "caosdb/authentication.h"               // for Authenticator
 #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionSe...
+#include "caosdb/info.h"                         // for VersionInfo
 #include "caosdb/info/v1alpha1/main.grpc.pb.h"   // for GeneralInfoService:...
 #include "caosdb/transaction.h"                  // for Transaction
-#include "grpcpp/impl/codegen/client_callback.h" // for Channel
-namespace caosdb::authentication {
-class Authenticator;
-} // namespace caosdb::authentication
-namespace caosdb::info {
-class VersionInfo;
-} // namespace caosdb::info
-namespace grpc {
-class ChannelCredentials;
-} // namespace grpc
+#include "grpcpp/channel.h"                      // for Channel
+#include "grpcpp/security/credentials.h"         // for ChannelCredentials
 
 namespace caosdb::connection {
 using caosdb::authentication::Authenticator;
@@ -52,95 +47,153 @@ using caosdb::info::v1alpha1::GeneralInfoService;
 using caosdb::transaction::Transaction;
 using grpc::ChannelCredentials;
 
-class CACertificateProvider {
+class CertificateProvider {
 public:
-  [[nodiscard]] auto virtual getCACertPem() const -> std::string = 0;
+  [[nodiscard]] auto virtual GetCertificatePem() const -> std::string = 0;
+  virtual ~CertificateProvider() = default;
 };
 
-class PemFileCACertProvider : public CACertificateProvider {
+class PemFileCertificateProvider : public CertificateProvider {
 private:
-  std::string cacert;
+  std::string certificate_provider;
 
 public:
-  explicit PemFileCACertProvider(const std::string &path);
-  [[nodiscard]] auto getCACertPem() const -> std::string override;
+  explicit PemFileCertificateProvider(const std::string &path);
+  [[nodiscard]] auto GetCertificatePem() const -> std::string override;
 };
 
-class PemCACertProvider : public CACertificateProvider {
+class PemCertificateProvider : public CertificateProvider {
 private:
-  std::string cacert;
+  std::string certificate_provider;
 
 public:
-  explicit PemCACertProvider(const std::string &cacert);
-  [[nodiscard]] auto getCACertPem() const -> std::string override;
+  explicit PemCertificateProvider(const std::string &certificate_provider);
+  [[nodiscard]] auto GetCertificatePem() const -> std::string override;
 };
 
 /**
  * @brief Configuration of the CaosDB connection.
  */
-class CaosDBConnectionConfig {
+class ConnectionConfiguration {
 private:
   std::string host;
   int port;
 
 public:
-  CaosDBConnectionConfig(const std::string &host, int port);
+  ConnectionConfiguration(const std::string &host, int port);
+  virtual ~ConnectionConfiguration() = default;
   friend auto operator<<(std::ostream &out,
-                         const CaosDBConnectionConfig &config)
+                         const ConnectionConfiguration &configuration)
     -> std::ostream &;
 
-  [[nodiscard]] auto virtual toString() const -> std::string = 0;
-  [[nodiscard]] auto getHost() const -> std::string;
-  [[nodiscard]] auto getPort() const -> int;
-  [[nodiscard]] auto virtual getChannelCredentials() const
+  [[nodiscard]] auto virtual ToString() const -> std::string = 0;
+  [[nodiscard]] auto GetHost() const -> std::string;
+  [[nodiscard]] auto GetPort() const -> int;
+  [[nodiscard]] auto virtual GetChannelCredentials() const
     -> std::shared_ptr<ChannelCredentials> = 0;
 };
 
-class InsecureCaosDBConnectionConfig : public CaosDBConnectionConfig {
+class InsecureConnectionConfiguration : public ConnectionConfiguration {
 private:
   std::shared_ptr<ChannelCredentials> credentials;
 
 public:
-  InsecureCaosDBConnectionConfig(const std::string &host, int port);
-  [[nodiscard]] auto getChannelCredentials() const
+  InsecureConnectionConfiguration(const std::string &host, int port);
+  [[nodiscard]] auto GetChannelCredentials() const
     -> std::shared_ptr<ChannelCredentials> override;
-  [[nodiscard]] auto toString() const -> std::string override;
+  [[nodiscard]] auto ToString() const -> std::string override;
 };
 
-class SslCaosDBConnectionConfig : public CaosDBConnectionConfig {
+class TlsConnectionConfiguration : public ConnectionConfiguration {
 private:
   std::shared_ptr<ChannelCredentials> credentials;
-  std::string cacert;
+  std::string certificate_provider;
 
 public:
-  SslCaosDBConnectionConfig(
-    const std::string &host, int port,
-    const std::shared_ptr<CACertificateProvider> &cacert);
-  SslCaosDBConnectionConfig(
-    const std::string &host, int port,
-    const std::shared_ptr<CACertificateProvider> &cacert,
-    const std::shared_ptr<Authenticator> &authenticator);
-  [[nodiscard]] auto getChannelCredentials() const
+  TlsConnectionConfiguration(const std::string &host, int port);
+  TlsConnectionConfiguration(const std::string &host, int port,
+                             const Authenticator &authenticator);
+  TlsConnectionConfiguration(const std::string &host, int port,
+                             const CertificateProvider &certificate_provider);
+  TlsConnectionConfiguration(const std::string &host, int port,
+                             const CertificateProvider &certificate_provider,
+                             const Authenticator &authenticator);
+  [[nodiscard]] auto GetChannelCredentials() const
     -> std::shared_ptr<ChannelCredentials> override;
-  [[nodiscard]] auto toString() const -> std::string override;
+  [[nodiscard]] auto ToString() const -> std::string override;
 };
 
 /**
  * @brief A reusable connection to a CaosDBServer.
  */
-class CaosDBConnection {
+class Connection {
   std::shared_ptr<grpc::Channel> channel;
-  std::shared_ptr<CaosDBConnectionConfig> config;
   std::unique_ptr<GeneralInfoService::Stub> general_info_service;
   std::shared_ptr<EntityTransactionService::Stub> entity_transaction_service;
 
 public:
-  explicit CaosDBConnection(
-    const std::shared_ptr<CaosDBConnectionConfig> &config);
-  friend auto operator<<(std::ostream &out, const CaosDBConnection &connection)
+  explicit Connection(const ConnectionConfiguration &configuration);
+  friend auto operator<<(std::ostream &out, const Connection &connection)
     -> std::ostream &;
   [[nodiscard]] auto GetVersionInfo() const -> std::unique_ptr<VersionInfo>;
   [[nodiscard]] auto CreateTransaction() const -> std::unique_ptr<Transaction>;
 };
+
+/**
+ * Lazily creates and caches reusable connection instances. Singleton.
+ *
+ * This class delegates the configuration of new connections to the global
+ * ConfigurationManager.
+ *
+ * A reset of the ConfigurationManager also resets the ConnectionManager.
+ *
+ * @brief Lazily creates and caches reusable connection instances.
+ */
+class ConnectionManager {
+private:
+  mutable std::map<std::string, std::shared_ptr<Connection>> connections;
+  mutable std::string default_connection_name;
+  inline ConnectionManager(){};
+
+  auto mHasConnection(const std::string &name) const -> bool;
+
+  auto mGetConnection(const std::string &name) const
+    -> const std::shared_ptr<Connection> &;
+
+  auto mGetDefaultConnection() const -> const std::shared_ptr<Connection> &;
+
+  inline auto mReset() -> void {
+    connections.clear();
+    default_connection_name = std::string();
+  }
+
+public:
+  static ConnectionManager &GetInstance() {
+    static ConnectionManager instance;
+    return instance;
+  };
+
+  inline static auto HasConnection(const std::string &name) -> bool {
+    return ConnectionManager::GetInstance().mHasConnection(name);
+  };
+
+  inline static auto GetConnection(const std::string &name)
+    -> const std::shared_ptr<Connection> & {
+    return ConnectionManager::GetInstance().mGetConnection(name);
+  };
+
+  inline static auto GetDefaultConnection()
+    -> const std::shared_ptr<Connection> & {
+    return ConnectionManager::GetInstance().mGetDefaultConnection();
+  };
+
+  inline static auto Reset() -> void {
+    return ConnectionManager::GetInstance().mReset();
+  };
+
+  ConnectionManager(ConnectionManager const &) = delete;
+  void operator=(ConnectionManager const &) = delete;
+};
+
 } // namespace caosdb::connection
 #endif
diff --git a/include/caosdb/constants.h.in b/include/caosdb/constants.h.in
index 02c0341d17b77bbf843debea349af45b91516662..bfec36bfbebf91fcd3e9eed38dd9ff305afc7a74 100644
--- a/include/caosdb/constants.h.in
+++ b/include/caosdb/constants.h.in
@@ -22,11 +22,34 @@
 
 #ifndef CAOSDB_CONSTANTS_H
 #define CAOSDB_CONSTANTS_H
+#ifdef __cplusplus
 namespace caosdb {
+#endif
 // clang-format off
-constexpr int LIBCAOSDB_VERSION_MAJOR = @libcaosdb_VERSION_MAJOR@;
-constexpr int LIBCAOSDB_VERSION_MINOR = @libcaosdb_VERSION_MINOR@;
-constexpr int LIBCAOSDB_VERSION_PATCH = @libcaosdb_VERSION_PATCH@;
+const int LIBCAOSDB_VERSION_MAJOR = @libcaosdb_VERSION_MAJOR@;
+const int LIBCAOSDB_VERSION_MINOR = @libcaosdb_VERSION_MINOR@;
+const int LIBCAOSDB_VERSION_PATCH = @libcaosdb_VERSION_PATCH@;
+const int COMPATIBLE_SERVER_VERSION_MAJOR = @libcaosdb_COMPATIBLE_SERVER_VERSION_MAJOR@;
+const int COMPATIBLE_SERVER_VERSION_MINOR = @libcaosdb_COMPATIBLE_SERVER_VERSION_MINOR@;
+const int COMPATIBLE_SERVER_VERSION_PATCH = @libcaosdb_COMPATIBLE_SERVER_VERSION_PATCH@;
+const char* COMPATIBLE_SERVER_VERSION_PRE_RELEASE = "@libcaosdb_COMPATIBLE_SERVER_VERSION_PRE_RELEASE@";
+
+/**
+ * Precedence of configuration files from highest to lowest.
+ */
+const char* LIBCAOSDB_CONFIGURATION_FILES_PRECEDENCE[] = {
+  "$CAOSDB_CLIENT_CONFIGURATION",
+  "caosdb_client.json",
+  "caosdb-client.json",
+  ".caosdb_client.json",
+  ".caosdb-client.json",
+  "$HOME/caosdb_client.json",
+  "$HOME/caosdb-client.json",
+  "$HOME/.caosdb_client.json",
+  "$HOME/.caosdb-client.json"
+};
 // clang-format on
+#ifdef __cplusplus
 } // namespace caosdb
 #endif
+#endif
diff --git a/include/caosdb/exceptions.h b/include/caosdb/exceptions.h
index 59ff3d4f08f6a8718faa2eb2e065d4ed5b04c493..a0971a13be02adc6c16837011f7df2c45fb7c089 100644
--- a/include/caosdb/exceptions.h
+++ b/include/caosdb/exceptions.h
@@ -45,5 +45,23 @@ public:
     : runtime_error(what_arg) {}
 };
 
+/**
+ * @brief The connection is known to the ConnectionManager under this name.
+ */
+class UnknownConnectionError : public runtime_error {
+public:
+  explicit UnknownConnectionError(const std::string &what_arg)
+    : runtime_error(what_arg) {}
+};
+
+/**
+ * @brief Exception for errors of the ConnectionManager.
+ */
+class ConfigurationError : public runtime_error {
+public:
+  explicit ConfigurationError(const std::string &what_arg)
+    : runtime_error(what_arg) {}
+};
+
 } // namespace caosdb::exceptions
 #endif
diff --git a/include/caosdb/utils.h b/include/caosdb/utility.h
similarity index 64%
rename from include/caosdb/utils.h
rename to include/caosdb/utility.h
index 20aebbfe7c776f0323db3383d589f05c214d8e55..5d3ac6147222f01b0edc4f659a6bc242b8bd21b0 100644
--- a/include/caosdb/utils.h
+++ b/include/caosdb/utility.h
@@ -21,14 +21,23 @@
 
 #ifndef CAOSDB_UTILS_H
 #define CAOSDB_UTILS_H
+#include <cassert>
 #include <iostream>
 #include <string_view>
 #include <fstream>
 #include <string>
 #include <cstdlib>
+#include <boost/json.hpp>
 #include <boost/beast/core/detail/base64.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
 
-namespace caosdb::utils {
+namespace caosdb::utility {
+using boost::filesystem::exists;
+using boost::filesystem::ifstream;
+using boost::filesystem::path;
+using boost::json::stream_parser;
+using boost::json::value;
 
 /**
  * @brief Read a text file into a string and return the file's content.
@@ -50,18 +59,25 @@ inline auto load_string_file(const std::string &path) -> std::string {
   return result;
 }
 
+inline auto get_env_var(const char *key, const char *fall_back) -> const
+  char * {
+  const char *val = getenv(key);
+  if (val == nullptr) {
+    return fall_back;
+  } else {
+    return val;
+  }
+}
+
 /**
  * @brief Return the value of an environment variable or - if undefined - the
  * fall_back value.
  */
 inline auto get_env_var(const std::string &key, const std::string &fall_back)
-  -> std::string {
-  const char *val = getenv(key.c_str());
-  if (val == nullptr) {
-    return fall_back;
-  }
+  -> const std::string {
+  const char *val = get_env_var(key.c_str(), fall_back.c_str());
 
-  auto result = std::string(val);
+  auto const result = std::string(val);
   return result;
 }
 
@@ -79,5 +95,30 @@ inline auto base64_encode(const std::string &plain) -> std::string {
   return std::string(encoded, encoded + size_encoded);
 }
 
-} // namespace caosdb::utils
+inline auto load_json_file(const path &json_file) -> value {
+  assert(exists(json_file));
+
+  constexpr auto buffer_size = std::size_t(4096);
+  auto stream = ifstream(json_file);
+  stream.exceptions(std::ios_base::badbit);
+
+  stream_parser parser;
+  auto result = std::string();
+  auto buffer = std::string(buffer_size, '\0');
+  while (stream.read(&buffer[0], buffer_size)) {
+    parser.write(buffer.c_str(), stream.gcount());
+  }
+  parser.write(buffer.c_str(), stream.gcount());
+
+  assert(parser.done());
+  return parser.release();
+}
+
+inline auto get_home_directory() -> const path {
+  const auto *const home = getenv("HOME");
+  // TODO(tf) Add windowsy way of determining the home directory
+  return home;
+}
+
+} // namespace caosdb::utility
 #endif
diff --git a/include/ccaosdb.h b/include/ccaosdb.h
new file mode 100644
index 0000000000000000000000000000000000000000..f9ed3bcc5725bbfd5cf8f6eec281f1a686e930f6
--- /dev/null
+++ b/include/ccaosdb.h
@@ -0,0 +1,236 @@
+#ifdef __cplusplus
+extern "C" {
+#else
+#include <stdbool.h>
+#endif
+
+/**
+ * Return the constant caosdb::LIBCAOSDB_VERSION_MAJOR.
+ */
+const int caosdb_constants_LIBCAOSDB_VERSION_MAJOR();
+/**
+ * Return the constant caosdb::LIBCAOSDB_VERSION_MINOR
+ */
+const int caosdb_constants_LIBCAOSDB_VERSION_MINOR();
+/**
+ * Return the constant caosdb::LIBCAOSDB_VERSION_PATCH.
+ */
+const int caosdb_constants_LIBCAOSDB_VERSION_PATCH();
+/**
+ * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_MAJOR.
+ */
+const int caosdb_constants_COMPATIBLE_SERVER_VERSION_MAJOR();
+/**
+ * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_MINOR.
+ */
+const int caosdb_constants_COMPATIBLE_SERVER_VERSION_MINOR();
+/**
+ * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_PATCH.
+ */
+const int caosdb_constants_COMPATIBLE_SERVER_VERSION_PATCH();
+/**
+ * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_PRE_RELEASE.
+ */
+const char *caosdb_constants_COMPATIBLE_SERVER_VERSION_PRE_RELEASE();
+
+/**
+ * A wrapper of the C++ Connection class.
+ *
+ * We use a wrapper for future extensibility and in order to have a minimal
+ * capability for type checking in C even though the C++ class
+ * Connection is opaque in C.
+ */
+typedef struct {
+  void *wrapped_connection;
+} caosdb_connection_connection;
+
+/**
+ * A wrapper of the C++ ConnectionConfiguration class.
+ *
+ * We use a wrapper for future extensibility and in order to have a minimal
+ * capability for type checking in C even though the C++ class
+ * Connection is opaque in C.
+ */
+typedef struct {
+  void *wrapped_connection_configuration;
+} caosdb_connection_connection_configuration;
+
+/**
+ * A wrapper of the C++ VersionInfo class.
+ *
+ * We use a wrapper for future extensibility and in order to have a minimal
+ * capability for type checking in C even though the C++ class
+ * Connection is opaque in C.
+ */
+typedef struct {
+  int major;
+  int minor;
+  int patch;
+  const char *pre_release;
+  const char *build;
+} caosdb_info_version_info;
+
+typedef struct {
+  void *wrapped_certificate_provider;
+} caosdb_connection_certificate_provider;
+
+typedef struct {
+  void *wrapped_authenticator;
+} caosdb_authentication_authenticator;
+
+/**
+ * Return the environment variable of the given name.
+ *
+ * If the environment variable is not set, return the fall_back instead.
+ */
+const char *caosdb_utility_get_env_var(const char *name, const char *fall_back);
+
+/**
+ * Create a pem-file certificate provider.
+ *
+ * Use the destructor function
+ * `caosdb_connection_delete_certificate_provider` to free the wrapped
+ * provider.
+ *
+ * EXPERT USE ONLY.  Memory management with this function is a bit tricky.
+ * Only use it when you know what you are doing.
+ */
+int caosdb_connection_create_pem_file_certificate_provider(
+  caosdb_connection_certificate_provider *out, const char *path);
+
+/**
+ * Destructor function for a certificate provider.
+ *
+ * EXPERT USE ONLY.  Only use it when you know what you are doing.
+ */
+int caosdb_connection_delete_certificate_provider(
+  caosdb_connection_certificate_provider *provider);
+
+/**
+ * Create a tls-secured connection configuration.
+ *
+ * The configuration is needed to instantiate a connection.
+ *
+ * Use the destructor function
+ * `caosdb_connection_delete_connection_configuration` to free the wrapped
+ * configuration.
+ *
+ * EXPERT USE ONLY.  Memory management with this function is a bit tricky.
+ * Only use it when you know what you are doing.
+ */
+int caosdb_connection_create_tls_connection_configuration(
+  caosdb_connection_connection_configuration *out, const char *host,
+  const int port, caosdb_authentication_authenticator *authenticator,
+  caosdb_connection_certificate_provider *provider);
+
+/**
+ * Create a tls-secured connection configuration.
+ *
+ * The configuration is needed to instantiate a connection.
+ *
+ * Use `caosdb_connection_create_tls_connection_configuration` for a
+ * tls-secured connection which also supports authentication.
+ *
+ * Use the destructor function
+ * `caosdb_connection_delete_connection_configuration` to free the wrapped
+ * configuration.
+ *
+ * EXPERT USE ONLY.  Memory management with this function is a bit tricky.
+ * Only use it when you know what you are doing.
+ */
+int caosdb_connection_create_insecure_connection_configuration(
+  caosdb_connection_connection_configuration *out, const char *host,
+  const int port);
+
+/**
+ * Destructor function for the caosdb_connection_connection_configuration
+ * struct.
+ *
+ * EXPERT USE ONLY.  Only use it when you know what you are doing.
+ */
+int caosdb_connection_delete_connection_configuration(
+  caosdb_connection_connection_configuration *configuration);
+
+/**
+ * Add a public certificate of a trusted certificate authority to an
+ * existing, tls-enabled connection configuration.
+ *
+ * @param cacert path to a pem-file.
+ */
+int caosdb_connection_configuration_add_cacert(
+  caosdb_connection_connection_configuration *configuration,
+  const char *cacert);
+
+/**
+ * Create a plain password authenticator.
+ *
+ * Use the destructor function
+ * `caosdb_authentication_delete_authenticator` to free the wrapped
+ * authenticator.
+ *
+ * EXPERT USE ONLY.  Memory management with this function is a bit tricky.
+ * Only use it when you know what you are doing.
+ */
+int caosdb_authentication_create_plain_password_authenticator(
+  caosdb_authentication_authenticator *out, const char *username,
+  const char *password);
+
+/**
+ * Destructor function for the caosdb_authentication_authenticator struct.
+ *
+ * EXPERT USE ONLY.  Only use it when you know what you are doing.
+ */
+int caosdb_authentication_delete_authenticator(
+  caosdb_authentication_authenticator *authenticator);
+
+/**
+ * Create a connection instance.
+ *
+ * The connection is needed to create transactions and to initiate any other
+ * interaction with a CaosDB server.
+ *
+ * Use the destructor function
+ * `caosdb_connection_delete_connection` to free the wrapped
+ * connection.
+ *
+ * EXPERT USE ONLY.  Memory management with this function is a bit tricky.
+ * Only use it when you know what you are doing.
+ */
+int caosdb_connection_create_connection(
+  caosdb_connection_connection *out,
+  const caosdb_connection_connection_configuration *configuration);
+
+/**
+ * Destructor function for the caosdb_connection_connection struct.
+ *
+ * EXPERT USE ONLY.  Only use it when you know what you are doing.
+ */
+int caosdb_connection_delete_connection(
+  caosdb_connection_connection *connection);
+
+/**
+ * Request the version of the server.
+ */
+int caosdb_connection_get_version_info(
+  caosdb_info_version_info *out,
+  const caosdb_connection_connection *connection);
+
+/**
+ * Get the default connection from the ConnectionManager.
+ *
+ * The default connection is to be specified in a configuration file.
+ */
+int caosdb_connection_connection_manager_get_default_connection(
+  caosdb_connection_connection *out);
+
+/**
+ * Get a named connection from the ConnectionManager.
+ *
+ * The named connection is to be specified in a configuration file.
+ */
+int caosdb_connection_connection_manager_get_connection(
+  caosdb_connection_connection *out, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/requirements.txt b/requirements.txt
index 9a211b1c527f8d5c885affe93af25f8368f3b2c5..2911ae69101c3c4bd492308915b7a5a653a2d170 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
+attrs==21.2.0
 bottle==0.12.19
 certifi==2021.5.30
 chardet==4.0.0
@@ -9,6 +10,7 @@ fasteners==0.16.3
 future==0.18.2
 idna==2.10
 Jinja2==2.11.3
+jsonschema==3.2.0
 MarkupSafe==2.0.1
 node-semver==0.6.1
 packaging==20.9
@@ -17,6 +19,7 @@ pluginbase==1.0.1
 Pygments==2.9.0
 PyJWT==1.7.1
 pyparsing==2.4.7
+pyrsistent==0.18.0
 python-dateutil==2.8.1
 PyYAML==5.4.1
 requests==2.25.1
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index eeabe29cce0d81e5515bd5510584502282e0dd1d..147bddc8312d260d1a00518ea781b5ca2aec330e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -23,6 +23,7 @@
 set(libcaosdb_SRC
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/authentication.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/connection.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/configuration.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction.cpp
     )
 
diff --git a/src/caosdb/authentication.cpp b/src/caosdb/authentication.cpp
index e7849829a58ea34159f960e483011498db21a745..7bc70de4efbd5a09bf96fbfb240110d5a8bb45b3 100644
--- a/src/caosdb/authentication.cpp
+++ b/src/caosdb/authentication.cpp
@@ -28,7 +28,7 @@
 #include "grpcpp/security/credentials.h"    // for MetadataCredentialsFromP...
 
 namespace caosdb::authentication {
-using caosdb::utils::base64_encode;
+using caosdb::utility::base64_encode;
 using grpc::AuthContext;
 using grpc::MetadataCredentialsPlugin;
 using grpc::Status;
@@ -52,7 +52,7 @@ PlainPasswordAuthenticator::PlainPasswordAuthenticator(
   this->basic = "Basic " + base64_encode(username + ":" + password);
 };
 
-auto PlainPasswordAuthenticator::getCallCredentials() const
+auto PlainPasswordAuthenticator::GetCallCredentials() const
   -> std::shared_ptr<grpc::CallCredentials> {
   auto call_creds = grpc::MetadataCredentialsFromPlugin(
     std::unique_ptr<grpc::MetadataCredentialsPlugin>(
diff --git a/src/caosdb/configuration.cpp b/src/caosdb/configuration.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fd62e24865feab9b09f79248c4aaa70e2ec7ee13
--- /dev/null
+++ b/src/caosdb/configuration.cpp
@@ -0,0 +1,290 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+#include "caosdb/configuration.h"
+#include <cstdlib>                            // for getenv
+#include <cassert>                            // for assert
+#include <string>                             // for char_traits, string
+#include "boost/iterator/iterator_facade.hpp" // for iterator_facade_base
+#include "boost/json/impl/object.hpp"         // for object::at, object::begin
+#include "boost/json/string.hpp"              // for string
+#include "boost/json/string_view.hpp"         // for string_view
+#include "caosdb/authentication.h"            // for PlainPasswordAuthentic...
+#include "caosdb/connection.h"                // for TlsConnectionConfiguration
+#include "caosdb/constants.h"                 // for LIBCAOSDB_CONFIGURATIO...
+#include "caosdb/exceptions.h"                // for ConfigurationError
+
+namespace caosdb::configuration {
+using boost::filesystem::exists;
+using boost::filesystem::path;
+using boost::json::object;
+using boost::json::value;
+using caosdb::authentication::Authenticator;
+using caosdb::authentication::PlainPasswordAuthenticator;
+using caosdb::connection::CertificateProvider;
+using caosdb::connection::ConnectionConfiguration;
+using caosdb::connection::InsecureConnectionConfiguration;
+using caosdb::connection::PemFileCertificateProvider;
+using caosdb::connection::TlsConnectionConfiguration;
+using caosdb::exceptions::ConfigurationError;
+using caosdb::utility::get_home_directory;
+using caosdb::utility::load_json_file;
+
+auto ConnectionConfigurationHelper::CreateCertificateProvider(
+  const object &from) const -> std::unique_ptr<CertificateProvider> {
+  std::unique_ptr<CertificateProvider> certificate_provider;
+  if (from.contains("server_certificate_path")) {
+    const value &path_str = from.at("server_certificate_path");
+    assert(path_str.is_string() == true);
+    certificate_provider = std::make_unique<PemFileCertificateProvider>(
+      std::string(path_str.as_string().c_str()));
+  }
+  return certificate_provider;
+};
+
+auto ConnectionConfigurationHelper::CreateAuthenticator(
+  const object &from) const -> std::unique_ptr<Authenticator> {
+  std::unique_ptr<Authenticator> authenticator;
+  if (from.contains("authentication")) {
+    assert(from.at("authentication").is_object());
+    auto authentication = from.at("authentication").as_object();
+    auto type = std::string("plain");
+    if (authentication.contains("type")) {
+      assert(authentication.at("type").is_string());
+      type = std::string(authentication.at("type").as_string().c_str());
+    }
+    if (type == "plain") {
+      assert(authentication.contains("username"));
+      auto username = authentication.at("username");
+      assert(username.is_string());
+
+      assert(authentication.contains("password"));
+      auto password = authentication.at("password");
+      assert(password.is_string());
+
+      authenticator = std::make_unique<PlainPasswordAuthenticator>(
+        std::string(username.as_string().c_str()),
+        std::string(password.as_string().c_str()));
+    } else {
+      throw ConfigurationError("Unknow authentication type: '" + type + "'.");
+    }
+  }
+  return authenticator;
+};
+
+auto ConnectionConfigurationHelper::CreateConnectionConfiguration(
+  const bool tls, const std::string &host, const int port,
+  const CertificateProvider *certificate_provider,
+  const Authenticator *authenticator) const
+  -> std::unique_ptr<ConnectionConfiguration> {
+  if (tls) {
+    if (certificate_provider != nullptr && authenticator != nullptr) {
+      // authenticated and special certificate
+      return std::make_unique<TlsConnectionConfiguration>(
+        host, port, *certificate_provider, *authenticator);
+    } else if (certificate_provider != nullptr) {
+      // unauthenticated, special certificate
+      return std::make_unique<TlsConnectionConfiguration>(
+        host, port, *certificate_provider);
+    } else if (authenticator != nullptr) {
+      // authenticated, no special certificate
+      return std::make_unique<TlsConnectionConfiguration>(host, port,
+                                                          *authenticator);
+    }
+    // unauthenticated, no special certificate
+    return std::make_unique<TlsConnectionConfiguration>(host, port);
+  } else {
+    return std::make_unique<InsecureConnectionConfiguration>(host, port);
+  }
+};
+
+auto ConnectionConfigurationHelper::IsTls(const object &from) const -> bool {
+  bool tls = true;
+  if (from.contains("tls")) {
+    auto tls_switch = from.at("tls");
+    assert(tls_switch.is_bool());
+    tls = tls_switch.as_bool();
+  }
+  return tls;
+};
+
+auto ConnectionConfigurationHelper::CreateConnectionConfiguration(
+  const object &from) const -> std::unique_ptr<ConnectionConfiguration> {
+  assert(from.contains("host"));
+  const auto &host = from.at("host");
+  assert(host.is_string());
+
+  assert(from.contains("port"));
+  const auto &port = from.at("port");
+  assert(port.is_int64());
+
+  auto tls = IsTls(from);
+
+  auto certificate_provider = CreateCertificateProvider(from);
+
+  auto authenticator = CreateAuthenticator(from);
+
+  return CreateConnectionConfiguration(
+    tls, std::string(host.as_string().c_str()),
+    static_cast<int>(port.as_int64()), certificate_provider.get(),
+    authenticator.get());
+};
+
+auto ConfigurationManager::mReset() -> void {
+  mClear();
+  InitializeDefaults();
+};
+
+auto ConfigurationManager::mClear() -> void {
+  json_configuration = value(nullptr);
+  ConnectionManager::Reset();
+}
+
+auto ConfigurationManager::mLoadSingleJSONConfiguration(const path &json_file)
+  -> void {
+  if (!json_configuration.is_null()) {
+    throw ConfigurationError("This CaosDB client has already been configured.");
+  }
+  if (!exists(json_file)) {
+    throw ConfigurationError("Configuration file does not exist.");
+  }
+
+  json_configuration = load_json_file(json_file);
+  // TODO(far future) validate against json-schema
+};
+
+auto ConfigurationManager::mGetConnectionConfiguration(
+  const std::string &name) const -> std::unique_ptr<ConnectionConfiguration> {
+  auto connection_json = GetConnection(name);
+  return connection_configuration_helper.CreateConnectionConfiguration(
+    connection_json);
+};
+
+auto ConfigurationManager::mGetDefaultConnectionName() const -> std::string {
+  auto connections = GetConnections();
+  if (connections.contains("default")) {
+    auto default_connection = connections.at("default");
+    if (default_connection.is_object()) {
+      // the name is actually "default"
+      return std::string("default");
+    } else {
+      assert(default_connection.is_string());
+      auto default_connection_name = default_connection.as_string();
+      // return the string value of connections.default
+      return std::string(default_connection_name.c_str());
+    }
+  }
+  if (connections.size() == 1) {
+    // return the key of the first and only sub-element of connections.
+    return connections.begin()->key().to_string();
+  }
+  throw ConfigurationError("Could not determine the default connection.");
+};
+
+auto ConfigurationManager::GetConfiguration() const -> const object & {
+  if (json_configuration.is_null()) {
+    throw ConfigurationError("This CaosDB client has not been configured.");
+  }
+  assert(json_configuration.is_object());
+  return json_configuration.as_object();
+};
+
+auto ConfigurationManager::GetConnections() const -> const object & {
+  const auto &configuration = GetConfiguration();
+  if (!configuration.contains("connections")) {
+    throw ConfigurationError(
+      "This CaosDB client hasn't any configured connections.");
+  }
+  const auto &connections_value = configuration.at("connections");
+  if (connections_value.is_null()) {
+    throw ConfigurationError(
+      "This CaosDB client hasn't any configured connections.");
+  }
+  assert(connections_value.is_object());
+  const auto &connections_object = connections_value.as_object();
+  if (connections_object.empty()) {
+    throw ConfigurationError(
+      "This CaosDB client hasn't any configured connections.");
+  }
+  return connections_object;
+};
+
+auto ConfigurationManager::GetConnection(const std::string &name) const
+  -> const object & {
+  const auto &connections = GetConnections();
+  if (connections.contains(name)) {
+    const auto &result_connection = connections.at(name);
+    assert(result_connection.is_object());
+    return result_connection.as_object();
+  }
+  throw ConfigurationError("The connection '" + name +
+                           "' has not been defined.");
+};
+
+auto ConfigurationManager::InitializeDefaults() -> void {
+
+  // find the configuration file...
+  std::unique_ptr<path> configuration_file_path;
+  for (const std::string &configuration_file :
+       caosdb::LIBCAOSDB_CONFIGURATION_FILES_PRECEDENCE) {
+    if (configuration_file == "$CAOSDB_CLIENT_CONFIGURATION") {
+      // user specified a file via the environment variable
+      const auto *from_env_var = getenv("CAOSDB_CLIENT_CONFIGURATION");
+      if (from_env_var != nullptr) {
+        configuration_file_path = std::make_unique<path>(from_env_var);
+        if (exists(*configuration_file_path)) {
+          break;
+        } else {
+          configuration_file_path = nullptr;
+          // TODO(tf) log warning: "Configuration file under
+          // $CAOSDB_CLIENT_CONFIGURATION does not exist.
+        }
+      }
+    } else {
+      // check standard locations
+      configuration_file_path = std::make_unique<path>();
+      const path raw(configuration_file);
+      // resolve home directory
+      for (auto segment = raw.begin(); segment != raw.end(); ++segment) {
+        if (segment->string() == "$HOME") {
+          path expanded_home(get_home_directory());
+          *configuration_file_path /= expanded_home;
+        } else {
+          *configuration_file_path /= *segment;
+        }
+      }
+      if (exists(*configuration_file_path)) {
+        break;
+      } else {
+        configuration_file_path = nullptr;
+      }
+    }
+  }
+
+  // ... and use the configuration file
+  if (configuration_file_path != nullptr) {
+    // TODO(tf): log which file has been used.
+    mLoadSingleJSONConfiguration(*configuration_file_path);
+  } else {
+    // TODO(tf): log warning: no configuration files has been found
+  }
+}
+
+} // namespace caosdb::configuration
diff --git a/src/caosdb/connection.cpp b/src/caosdb/connection.cpp
index 880245bce04dd8d177a0a2c67f28aa2aafbf098a..d2a2ae1fb2408362c18c5a7d5815ac75e3c91406 100644
--- a/src/caosdb/connection.cpp
+++ b/src/caosdb/connection.cpp
@@ -28,17 +28,19 @@
 #include <stdexcept>                            // for runtime_error
 #include <string>                               // for operator+, char_tr...
 #include <memory>
-#include "caosdb/authentication.h"                // for Authenticator
+#include "caosdb/authentication.h" // for Authenticator
+#include "caosdb/configuration.h"
 #include "caosdb/exceptions.h"                    // for AuthenticationError
 #include "caosdb/info/v1alpha1/main.grpc.pb.h"    // for GeneralInfoService
 #include "caosdb/info/v1alpha1/main.pb.h"         // for GetVersionInfoResp...
 #include "caosdb/transaction.h"                   // for Transaction
-#include "caosdb/utils.h"                         // for load_string_file
+#include "caosdb/utility.h"                       // for load_string_file
 #include "caosdb/info.h"                          // for VersionInfo
 #include "grpcpp/impl/codegen/status_code_enum.h" // for StatusCode, UNAUTH...
 
 namespace caosdb::connection {
 using caosdb::authentication::Authenticator;
+using caosdb::configuration::ConfigurationManager;
 using caosdb::entity::v1alpha1::EntityTransactionService;
 using caosdb::exceptions::AuthenticationError;
 using caosdb::exceptions::ConnectionError;
@@ -47,113 +49,128 @@ using caosdb::info::v1alpha1::GeneralInfoService;
 using caosdb::info::v1alpha1::GetVersionInfoRequest;
 using caosdb::info::v1alpha1::GetVersionInfoResponse;
 using caosdb::transaction::Transaction;
-using caosdb::utils::load_string_file;
+using caosdb::utility::load_string_file;
 using grpc::InsecureChannelCredentials;
 using grpc::SslCredentials;
 using grpc::SslCredentialsOptions;
 
-PemFileCACertProvider::PemFileCACertProvider(const std::string &path) {
-  this->cacert = load_string_file(path);
+PemFileCertificateProvider::PemFileCertificateProvider(
+  const std::string &path) {
+  this->certificate_provider = load_string_file(path);
 }
 
-auto PemFileCACertProvider::getCACertPem() const -> std::string {
-  return this->cacert;
+auto PemFileCertificateProvider::GetCertificatePem() const -> std::string {
+  return this->certificate_provider;
 }
 
-PemCACertProvider::PemCACertProvider(const std::string &cacert) {
-  this->cacert = cacert;
+PemCertificateProvider::PemCertificateProvider(
+  const std::string &certificate_provider) {
+  this->certificate_provider = certificate_provider;
 }
 
-auto PemCACertProvider::getCACertPem() const -> std::string {
-  return this->cacert;
+auto PemCertificateProvider::GetCertificatePem() const -> std::string {
+  return this->certificate_provider;
 }
 
-CaosDBConnectionConfig::CaosDBConnectionConfig(const std::string &host,
-                                               int port) {
+ConnectionConfiguration::ConnectionConfiguration(const std::string &host,
+                                                 int port) {
   this->host = host;
   this->port = port;
 }
 
-auto CaosDBConnectionConfig::getHost() const -> std::string {
+auto ConnectionConfiguration::GetHost() const -> std::string {
   return this->host;
 }
 
-auto CaosDBConnectionConfig::getPort() const -> int { return this->port; }
+auto ConnectionConfiguration::GetPort() const -> int { return this->port; }
 
-auto operator<<(std::ostream &out, const CaosDBConnectionConfig &config)
+auto operator<<(std::ostream &out, const ConnectionConfiguration &configuration)
   -> std::ostream & {
-  out << config.toString();
+  out << configuration.ToString();
   return out;
 }
 
-InsecureCaosDBConnectionConfig::InsecureCaosDBConnectionConfig(
+InsecureConnectionConfiguration::InsecureConnectionConfiguration(
   const std::string &host, int port)
-  : CaosDBConnectionConfig(host, port) {
+  : ConnectionConfiguration(host, port) {
   this->credentials = InsecureChannelCredentials();
 }
 
-auto InsecureCaosDBConnectionConfig::getChannelCredentials() const
+auto InsecureConnectionConfiguration::GetChannelCredentials() const
   -> std::shared_ptr<ChannelCredentials> {
   return this->credentials;
 }
 
-auto InsecureCaosDBConnectionConfig::toString() const -> std::string {
-  return "InsecureCaosDBConnectionConfig(" + this->getHost() + "," +
-         std::to_string(this->getPort()) + ")";
+auto InsecureConnectionConfiguration::ToString() const -> std::string {
+  return "InsecureConnectionConfiguration(" + this->GetHost() + "," +
+         std::to_string(this->GetPort()) + ")";
 }
 
-SslCaosDBConnectionConfig::SslCaosDBConnectionConfig(
+TlsConnectionConfiguration::TlsConnectionConfiguration(const std::string &host,
+                                                       int port)
+  : ConnectionConfiguration(host, port) {
+  SslCredentialsOptions options;
+  this->credentials = SslCredentials(options);
+}
+
+TlsConnectionConfiguration::TlsConnectionConfiguration(
   const std::string &host, int port,
-  const std::shared_ptr<CACertificateProvider> &cacert)
-  : CaosDBConnectionConfig(host, port) {
+  const CertificateProvider &certificate_provider)
+  : ConnectionConfiguration(host, port) {
   SslCredentialsOptions options;
-  options.pem_root_certs = cacert->getCACertPem();
-  this->cacert = cacert->getCACertPem();
+  options.pem_root_certs = certificate_provider.GetCertificatePem();
   this->credentials = SslCredentials(options);
 }
 
-SslCaosDBConnectionConfig::SslCaosDBConnectionConfig(
+TlsConnectionConfiguration::TlsConnectionConfiguration(
+  const std::string &host, int port, const Authenticator &authenticator)
+  : ConnectionConfiguration(host, port) {
+
+  SslCredentialsOptions options;
+  this->credentials = grpc::CompositeChannelCredentials(
+    SslCredentials(options), authenticator.GetCallCredentials());
+}
+
+TlsConnectionConfiguration::TlsConnectionConfiguration(
   const std::string &host, int port,
-  const std::shared_ptr<CACertificateProvider> &cacert,
-  const std::shared_ptr<Authenticator> &authenticator)
-  : CaosDBConnectionConfig(host, port) {
+  const CertificateProvider &certificate_provider,
+  const Authenticator &authenticator)
+  : ConnectionConfiguration(host, port) {
 
   SslCredentialsOptions options;
-  options.pem_root_certs = cacert->getCACertPem();
-  this->cacert = cacert->getCACertPem();
+  options.pem_root_certs = certificate_provider.GetCertificatePem();
   this->credentials = grpc::CompositeChannelCredentials(
-    SslCredentials(options), authenticator->getCallCredentials());
+    SslCredentials(options), authenticator.GetCallCredentials());
 }
 
-auto SslCaosDBConnectionConfig::getChannelCredentials() const
+auto TlsConnectionConfiguration::GetChannelCredentials() const
   -> std::shared_ptr<ChannelCredentials> {
   return this->credentials;
 }
 
-auto SslCaosDBConnectionConfig::toString() const -> std::string {
-  return "SslCaosDBConnectionConfig(" + this->getHost() + "," +
-         std::to_string(this->getPort()) + "," + this->cacert + ")";
+auto TlsConnectionConfiguration::ToString() const -> std::string {
+  return "TlsConnectionConfiguration(" + this->GetHost() + "," +
+         std::to_string(this->GetPort()) + "," + this->certificate_provider +
+         ")";
 }
 
-CaosDBConnection::CaosDBConnection(
-  const std::shared_ptr<CaosDBConnectionConfig> &config) {
-  this->config = config;
+Connection::Connection(const ConnectionConfiguration &configuration) {
   const std::string target =
-    this->config->getHost() + ":" + std::to_string(this->config->getPort());
+    configuration.GetHost() + ":" + std::to_string(configuration.GetPort());
   this->channel =
-    grpc::CreateChannel(target, this->config->getChannelCredentials());
+    grpc::CreateChannel(target, configuration.GetChannelCredentials());
   this->general_info_service = GeneralInfoService::NewStub(this->channel);
   this->entity_transaction_service =
     std::make_shared<EntityTransactionService::Stub>(this->channel);
 }
 
-auto operator<<(std::ostream &out, const CaosDBConnection &connection)
+auto operator<<(std::ostream &out, const Connection & /*connection*/)
   -> std::ostream & {
-  out << "CaosDBConnection(" << *(connection.config) << ")";
+  out << "Connection()";
   return out;
 }
 
-[[nodiscard]] auto CaosDBConnection::GetVersionInfo() const
+[[nodiscard]] auto Connection::GetVersionInfo() const
   -> std::unique_ptr<VersionInfo> {
   const GetVersionInfoRequest request;
   GetVersionInfoResponse response;
@@ -176,10 +193,42 @@ auto operator<<(std::ostream &out, const CaosDBConnection &connection)
   return std::make_unique<VersionInfo>(response.release_version_info());
 }
 
-[[nodiscard]] auto CaosDBConnection::CreateTransaction() const
+[[nodiscard]] auto Connection::CreateTransaction() const
   -> std::unique_ptr<Transaction> {
   auto service_stub = this->entity_transaction_service;
   return std::make_unique<Transaction>(service_stub);
 };
 
+auto ConnectionManager::mHasConnection(const std::string &name) const -> bool {
+  auto it = connections.find(name);
+  return it != connections.end();
+}
+
+auto ConnectionManager::mGetConnection(const std::string &name) const
+  -> const std::shared_ptr<Connection> & {
+  if (!HasConnection(name)) {
+    try {
+      auto connection = ConfigurationManager::GetConnectionConfiguration(name);
+      connections[name] = std::make_shared<Connection>(*connection.release());
+    } catch (const caosdb::exceptions::ConfigurationError &exc) {
+      throw caosdb::exceptions::UnknownConnectionError("No connection named '" +
+                                                       name + "' present.");
+    }
+  }
+  return this->connections.at(name);
+}
+
+auto ConnectionManager::mGetDefaultConnection() const
+  -> const std::shared_ptr<Connection> & {
+  if (!HasConnection(default_connection_name)) {
+    default_connection_name = ConfigurationManager::GetDefaultConnectionName();
+    auto default_connection =
+      ConfigurationManager::GetDefaultConnectionConfiguration();
+    connections[default_connection_name] =
+      std::make_shared<Connection>(*default_connection.release());
+  }
+
+  return connections.at(default_connection_name);
+}
+
 } // namespace caosdb::connection
diff --git a/src/ccaosdb.cpp b/src/ccaosdb.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2c9539af8036de10d36bb41b6ce188070cb21bcc
--- /dev/null
+++ b/src/ccaosdb.cpp
@@ -0,0 +1,184 @@
+#include <iostream>
+#include <stdio.h>
+#include <cassert>
+#include "caosdb/constants.h"
+#include "caosdb/utility.h"
+#include "caosdb/constants.h"
+#include "caosdb/connection.h"
+#include "ccaosdb.h"
+
+extern "C" {
+
+const int caosdb_constants_LIBCAOSDB_VERSION_MAJOR() {
+  return caosdb::LIBCAOSDB_VERSION_MAJOR;
+}
+
+const int caosdb_constants_LIBCAOSDB_VERSION_MINOR() {
+  return caosdb::LIBCAOSDB_VERSION_MINOR;
+}
+
+const int caosdb_constants_LIBCAOSDB_VERSION_PATCH() {
+  return caosdb::LIBCAOSDB_VERSION_PATCH;
+}
+
+const int caosdb_constants_COMPATIBLE_SERVER_VERSION_MAJOR() {
+  return caosdb::COMPATIBLE_SERVER_VERSION_MAJOR;
+}
+
+const int caosdb_constants_COMPATIBLE_SERVER_VERSION_MINOR() {
+  return caosdb::COMPATIBLE_SERVER_VERSION_MINOR;
+}
+
+const int caosdb_constants_COMPATIBLE_SERVER_VERSION_PATCH() {
+  return caosdb::COMPATIBLE_SERVER_VERSION_PATCH;
+}
+
+const char *caosdb_constants_COMPATIBLE_SERVER_VERSION_PRE_RELEASE() {
+  return caosdb::COMPATIBLE_SERVER_VERSION_PRE_RELEASE;
+}
+
+const char *caosdb_utility_get_env_var(const char *name,
+                                       const char *fall_back) {
+  return caosdb::utility::get_env_var(name, fall_back);
+}
+
+int caosdb_connection_create_pem_file_certificate_provider(
+  caosdb_connection_certificate_provider *out, const char *path) {
+  out->wrapped_certificate_provider =
+    new caosdb::connection::PemFileCertificateProvider(std::string(path));
+  return 0;
+}
+
+int caosdb_connection_delete_certificate_provider(
+  caosdb_connection_certificate_provider *provider) {
+  delete static_cast<caosdb::connection::CertificateProvider *>(
+    provider->wrapped_certificate_provider);
+  return 0;
+}
+
+int caosdb_authentication_create_plain_password_authenticator(
+  caosdb_authentication_authenticator *out, const char *username,
+  const char *password) {
+  out->wrapped_authenticator =
+    new caosdb::authentication::PlainPasswordAuthenticator(
+      std::string(username), std::string(password));
+  return 0;
+}
+
+int caosdb_authentication_delete_authenticator(
+  caosdb_authentication_authenticator *authenticator) {
+  delete static_cast<caosdb::authentication::Authenticator *>(
+    authenticator->wrapped_authenticator);
+  return 0;
+}
+
+int caosdb_connection_create_tls_connection_configuration(
+  caosdb_connection_connection_configuration *out, const char *host,
+  const int port, caosdb_authentication_authenticator *authenticator,
+  caosdb_connection_certificate_provider *provider) {
+
+  auto host_str = std::string(host);
+  if (authenticator != nullptr && provider != nullptr) {
+    auto wrapped_provider =
+      static_cast<caosdb::connection::CertificateProvider *>(
+        provider->wrapped_certificate_provider);
+    auto wrapped_authenticator =
+      static_cast<caosdb::authentication::Authenticator *>(
+        authenticator->wrapped_authenticator);
+    out->wrapped_connection_configuration =
+      new caosdb::connection::TlsConnectionConfiguration(
+        host_str, port, *wrapped_provider, *wrapped_authenticator);
+  } else if (authenticator != nullptr) {
+    auto wrapped_authenticator =
+      static_cast<caosdb::authentication::Authenticator *>(
+        authenticator->wrapped_authenticator);
+    out->wrapped_connection_configuration =
+      new caosdb::connection::TlsConnectionConfiguration(
+        host_str, port, *wrapped_authenticator);
+  } else if (provider != nullptr) {
+    auto wrapped_provider =
+      static_cast<caosdb::connection::CertificateProvider *>(
+        provider->wrapped_certificate_provider);
+    out->wrapped_connection_configuration =
+      new caosdb::connection::TlsConnectionConfiguration(host_str, port,
+                                                         *wrapped_provider);
+  } else {
+    out->wrapped_connection_configuration =
+      new caosdb::connection::TlsConnectionConfiguration(host_str, port);
+  }
+  return 0;
+}
+
+int caosdb_connection_create_insecure_connection_configuration(
+  caosdb_connection_connection_configuration *out, const char *host,
+  const int port) {
+  out->wrapped_connection_configuration =
+    new caosdb::connection::InsecureConnectionConfiguration(host, port);
+  return 0;
+}
+
+int caosdb_connection_delete_connection_configuration(
+  caosdb_connection_connection_configuration *configuration) {
+  delete static_cast<caosdb::connection::ConnectionConfiguration *>(
+    configuration->wrapped_connection_configuration);
+  return 0;
+}
+
+int caosdb_connection_create_connection(
+  caosdb_connection_connection *out,
+  const caosdb_connection_connection_configuration *configuration) {
+  caosdb::connection::ConnectionConfiguration *config =
+    static_cast<caosdb::connection::ConnectionConfiguration *>(
+      configuration->wrapped_connection_configuration);
+  out->wrapped_connection = new caosdb::connection::Connection(*config);
+  return 0;
+}
+
+int caosdb_connection_delete_connection(
+  caosdb_connection_connection *connection) {
+  delete static_cast<caosdb::connection::Connection *>(
+    connection->wrapped_connection);
+  return 0;
+}
+
+int caosdb_connection_get_version_info(
+  caosdb_info_version_info *out,
+  const caosdb_connection_connection *connection) {
+  auto *wrapped_connection = static_cast<caosdb::connection::Connection *>(
+    connection->wrapped_connection);
+  auto version_info = wrapped_connection->GetVersionInfo();
+
+  out->major = (int)version_info->GetMajor();
+  out->minor = (int)version_info->GetMinor();
+  out->patch = (int)version_info->GetPatch();
+
+  // copy pre_release, needs local variable because out->pre_release is const
+  char *pre_release =
+    (char *)malloc(sizeof(char) * (version_info->GetPreRelease().length() + 1));
+  strcpy(pre_release, version_info->GetPreRelease().c_str());
+  out->pre_release = pre_release;
+
+  // copy build, needs local variable because out->build is const
+  char *build =
+    (char *)malloc(sizeof(char) * (version_info->GetBuild().length() + 1));
+  strcpy(build, version_info->GetBuild().c_str());
+  out->build = build;
+
+  return 0;
+}
+
+int caosdb_connection_connection_manager_get_default_connection(
+  caosdb_connection_connection *out) {
+  out->wrapped_connection =
+    caosdb::connection::ConnectionManager::GetDefaultConnection().get();
+  return 0;
+}
+
+int caosdb_connection_connection_manager_get_connection(
+  caosdb_connection_connection *out, const char *name) {
+  out->wrapped_connection =
+    caosdb::connection::ConnectionManager::GetConnection(std::string(name))
+      .get();
+  return 0;
+}
+}
diff --git a/src/ccaosdbcli.c b/src/ccaosdbcli.c
new file mode 100644
index 0000000000000000000000000000000000000000..eaadc3c5822b1306140f530dddc7c4a49981e388
--- /dev/null
+++ b/src/ccaosdbcli.c
@@ -0,0 +1,21 @@
+#include <stdio.h>            // for printf
+#include "caosdb/constants.h" // for LIBCAOSDB_VERSION_MAJOR, LIBCAOSDB_VER...
+#include "ccaosdb.h"          // for caosdb_info_version_info, caosdb_conne...
+
+int main(void) {
+  printf(
+    "CaosDB C client (libcaosdb %d.%d.%d)\nWe don't miss the H of caos.\n\n",
+    LIBCAOSDB_VERSION_MAJOR, LIBCAOSDB_VERSION_MINOR, LIBCAOSDB_VERSION_PATCH);
+
+  caosdb_connection_connection connection;
+  caosdb_connection_connection_manager_get_default_connection(&connection);
+
+  caosdb_info_version_info version_info;
+  caosdb_connection_get_version_info(&version_info, &connection);
+
+  printf("Server version: %d.%d.%d-%s-%s\n", version_info.major,
+         version_info.minor, version_info.patch, version_info.pre_release,
+         version_info.build);
+
+  return 0;
+}
diff --git a/src/caosdbcli.cpp b/src/cxxcaosdbcli.cpp
similarity index 57%
rename from src/caosdbcli.cpp
rename to src/cxxcaosdbcli.cpp
index a128887ef7acc5004c60ffba4dbb9da2033f95ae..763ddb5177ae372a06767b721b909742624bc2f1 100644
--- a/src/caosdbcli.cpp
+++ b/src/cxxcaosdbcli.cpp
@@ -21,54 +21,35 @@
  */
 
 // A simple caosdb client
-#include <iostream>
-#include <memory>
-#include <string>
-#include "caosdb/constants.h"
-#include "caosdb/connection.h"
-#include "caosdb/authentication.h"
-#include "caosdb/utils.h"
-#include "caosdb/info.h"
-#include "caosdb/entity.h"      // for Entity, EntityID
-#include "caosdb/transaction.h" // for Transaction, UniqueResult
+#include <iostream>             // for operator<<, basic_ostream, basic_ost...
+#include <memory>               // for unique_ptr, allocator, __shared_ptr_...
+#include <string>               // for operator<<, char_traits
+#include "caosdb/connection.h"  // for Connection, ConnectionManager
+#include "caosdb/constants.h"   // for LIBCAOSDB_VERSION_MINOR, LIBCAOSDB_V...
+#include "caosdb/entity.h"      // for Entity
+#include "caosdb/info.h"        // for VersionInfo
+#include "caosdb/transaction.h" // for Transaction, UniqueResult, ResultSet
 
 auto main() -> int {
-
-  std::cout << "CaosDB (libcaosdb " << caosdb::LIBCAOSDB_VERSION_MINOR << "."
+  std::cout << "CaosDB C++ client (libcaosdb "
+            << caosdb::LIBCAOSDB_VERSION_MINOR << "."
             << caosdb::LIBCAOSDB_VERSION_MINOR << "."
             << caosdb::LIBCAOSDB_VERSION_PATCH << ")\n"
             << "We don't miss the H of caos.\n"
             << std::endl;
 
-  const auto pem_file =
-    caosdb::utils::get_env_var("CAOSDB_SERVER_CERT", std::string());
-  const auto host =
-    caosdb::utils::get_env_var("CAOSDB_SERVER_HOST", "localhost");
-  const auto port_str =
-    caosdb::utils::get_env_var("CAOSDB_SERVER_GRPC_PORT_HTTPS", "8443");
-  const auto port = std::stoi(port_str);
-  const auto user = caosdb::utils::get_env_var("CAOSDB_USER", "admin");
-  const auto password = caosdb::utils::get_env_var("CAOSDB_PASSWORD", "caosdb");
-
-  // setup the connection
-  auto auth =
-    std::make_shared<caosdb::authentication::PlainPasswordAuthenticator>(
-      user, password);
-  auto cacert =
-    std::make_shared<caosdb::connection::PemFileCACertProvider>(pem_file);
-  auto config = std::make_shared<caosdb::connection::SslCaosDBConnectionConfig>(
-    host, port, cacert, auth);
-  caosdb::connection::CaosDBConnection connection(config);
+  const auto &connection =
+    caosdb::connection::ConnectionManager::GetDefaultConnection();
 
   // get version info of the server
-  const auto &v_info = connection.GetVersionInfo();
+  const auto &v_info = connection->GetVersionInfo();
   std::cout << "Server Version: " << v_info->GetMajor() << "."
             << v_info->GetMinor() << "." << v_info->GetPatch() << "-"
             << v_info->GetPreRelease() << "-" << v_info->GetBuild()
             << std::endl;
 
   // retrieve an entity
-  auto transaction(connection.CreateTransaction());
+  auto transaction(connection->CreateTransaction());
   transaction->RetrieveById("20");
   transaction->Execute();
   const auto &result_set =
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 2616296f18b66d16d1054da5e89bb3fa7b9921da..17613e450d2cf7c66fd41d215d0640c2df3d0292 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -20,10 +20,12 @@
 
 # append all the test cases here (file name without the ".cpp" suffix)
 set(test_cases
+    test_configuration
     test_connection
     test_info
     test_transaction
-    test_utils
+    test_utility
+    test_ccaosdb
     )
 
 ###################################################
@@ -38,6 +40,7 @@ set(_CMAKE_CXX_CLANG_TIDY_TEST_CHECKS
 # add special cmake functions for gtest
 include(GoogleTest)
 
+
 # loop over all test cases and add them to the test runner
 list(LENGTH test_cases len_test_cases)
 math(EXPR len_test_cases "${len_test_cases} - 1")
@@ -47,9 +50,9 @@ foreach (i RANGE "${len_test_cases}")
     set(libcaosdb_TEST_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${test_case_name}.cpp
         ${libcaosdb_TEST_SRC}")
     target_link_libraries(${test_case_name}
-        PRIVATE ${LIBCAOSDB} ${CONAN_LIBS_GTEST} ${CONAN_LIBS_BOOST})
+        PRIVATE ${LIBCAOSDB} ccaosdb ${CONAN_LIBS_GTEST} ${CONAN_LIBS_BOOST})
     target_include_directories(${test_case_name}
-      PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+      PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
     if(_LINTING)
     set_target_properties(${test_case_name}
         PROPERTIES
@@ -57,10 +60,18 @@ foreach (i RANGE "${len_test_cases}")
         CXX_INCLUDE_WHAT_YOU_USE "${_CMAKE_CXX_INCLUDE_WHAT_YOU_USE}")
     endif()
     gtest_discover_tests(${test_case_name}
+        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
         PROPERTIES
             LABELS "caosdb-cpplib-unit-tests")
 endforeach ()
 
+# copy test data to build dir
+set(TEST_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test_data")
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/caosdb_test_utility.h.in
+  ${CMAKE_CURRENT_BINARY_DIR}/caosdb_test_utility.h)
+
+
+
 
 ###################################################
 ### Set up test coverage repor (Gcov + Lcov)
@@ -75,12 +86,12 @@ if (LCOV_PATH)
         NAME unit_test_coverage
         EXECUTABLE ctest -L caosdb-cpplib-unit-tests
         EXCLUDE "${CMAKE_BINARY_DIR}/*"
-        DEPENDENCIES caosdb ${test_cases}
+        DEPENDENCIES caosdb ccaosdb ${test_cases}
         LCOV_ARGS --rc lcov_branch_coverage=1 --no-external
         GENHTML_ARGS --rc lcov_branch_coverage=1
         )
     message(STATUS "Adding COMPILE_FLAGS for coverage: ${COVERAGE_COMPILER_FLAGS}")
-    set_target_properties(caosdb PROPERTIES
+    set_target_properties(caosdb ccaosdb PROPERTIES
         COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}")
 else ()
     message(WARNING "Could not generate code coverage report. Please install lcov.")
diff --git a/test/caosdb_test_utility.h b/test/caosdb_test_utility.h.in
similarity index 70%
rename from test/caosdb_test_utility.h
rename to test/caosdb_test_utility.h.in
index 981c458f86e7658d68e4a032eb8f1408fa655dda..24d088bb9ff8a0c82d117da35241f1d8acc18012 100644
--- a/test/caosdb_test_utility.h
+++ b/test/caosdb_test_utility.h.in
@@ -42,4 +42,18 @@
       throw;                                                                   \
     },                                                                         \
     exeption_type)
+#define EXPECT_NULL(statement)                                                 \
+  if (statement != nullptr) {                                                  \
+    FAIL() << "Should be a nullptr";                                           \
+  } else {                                                                     \
+    SUCCEED();                                                                 \
+  }
+#define ASSERT_NULL(statement)                                                 \
+  if (statement != nullptr) {                                                  \
+    ADD_FAIL() << "Should be a nullptr";                                       \
+  } else {                                                                     \
+    SUCCEED();                                                                 \
+  }
 #endif
+
+const std::string TEST_DATA_DIR = "@TEST_DATA_DIR@";
diff --git a/test/test_ccaosdb.cpp b/test/test_ccaosdb.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b32ace97cbd888d7f60134c995cefaa1483fa79c
--- /dev/null
+++ b/test/test_ccaosdb.cpp
@@ -0,0 +1,63 @@
+/*
+ *
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <gtest/gtest-message.h>   // for Message
+#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl
+#include <gtest/gtest_pred_impl.h> // for Test, TestInfo, EXPECT_EQ, TEST
+#include <string>                  // for allocator
+#include "caosdb_test_utility.h"   // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR
+#include "ccaosdb.h"               // for caosdb_utility_get_env_var
+#include "caosdb/configuration.h"
+
+class test_ccaosdb : public ::testing::Test {
+protected:
+  void SetUp() override {
+    caosdb::configuration::ConfigurationManager::Clear();
+    caosdb::configuration::ConfigurationManager::LoadSingleJSONConfiguration(
+      TEST_DATA_DIR + "/test_caosdb_client.json");
+  }
+
+  void TearDown() override {
+    caosdb::configuration::ConfigurationManager::Clear();
+  }
+};
+
+TEST_F(test_ccaosdb, test_get_env_var) {
+  const char *const some_var =
+    caosdb_utility_get_env_var("SOME_ENV_VAR", "fall-back");
+  EXPECT_EQ("fall-back", some_var);
+}
+
+TEST_F(test_ccaosdb, test_get_default_connection) {
+  caosdb_connection_connection out;
+
+  caosdb_connection_connection_manager_get_default_connection(&out);
+  EXPECT_TRUE(out.wrapped_connection);
+}
+
+TEST_F(test_ccaosdb, test_get_connection) {
+  caosdb_connection_connection out;
+
+  caosdb_connection_connection_manager_get_connection(&out,
+                                                      "local-caosdb-admin");
+  EXPECT_TRUE(out.wrapped_connection);
+}
diff --git a/test/test_configuration.cpp b/test/test_configuration.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c7db0390fa220a60daa746f88900c0c6c3f33b87
--- /dev/null
+++ b/test/test_configuration.cpp
@@ -0,0 +1,79 @@
+/*
+ *
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+#include <gtest/gtest-message.h>   // for Message
+#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver
+#include <gtest/gtest_pred_impl.h> // for Test, TestInfo, TEST
+#include <string>                  // for operator+, allocator, string
+#include "caosdb/configuration.h"  // for ConfigurationManager, Configurati...
+#include "caosdb/exceptions.h"     // for ConfigurationError
+#include "caosdb_test_utility.h"   // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR
+
+namespace caosdb::configuration {
+
+class test_configuration : public ::testing::Test {
+protected:
+  void SetUp() override { ConfigurationManager::Clear(); }
+  void TearDown() override { ConfigurationManager::Clear(); }
+};
+
+TEST_F(test_configuration, load_json) {
+  ConfigurationManager::LoadSingleJSONConfiguration(TEST_DATA_DIR +
+                                                    "/test_caosdb_client.json");
+  EXPECT_THROW_MESSAGE(
+    ConfigurationManager::LoadSingleJSONConfiguration("anything"),
+    ConfigurationError, "This CaosDB client has already been configured.");
+
+  ConfigurationManager::Clear();
+  EXPECT_THROW_MESSAGE(
+    ConfigurationManager::LoadSingleJSONConfiguration("anything"),
+    ConfigurationError, "Configuration file does not exist.");
+  ConfigurationManager::Clear();
+}
+
+TEST_F(test_configuration, get_default_connection_configuration_error) {
+  EXPECT_THROW_MESSAGE(ConfigurationManager::GetDefaultConnectionName(),
+                       ConfigurationError,
+                       "This CaosDB client has not been configured.");
+
+  ConfigurationManager::LoadSingleJSONConfiguration(
+    TEST_DATA_DIR + "/test_broken_caosdb_client_no_connections1.json");
+  EXPECT_THROW_MESSAGE(ConfigurationManager::GetDefaultConnectionName(),
+                       ConfigurationError,
+                       "This CaosDB client hasn't any configured connections.");
+  ConfigurationManager::Clear();
+
+  ConfigurationManager::LoadSingleJSONConfiguration(
+    TEST_DATA_DIR + "/test_broken_caosdb_client_no_connections2.json");
+  EXPECT_THROW_MESSAGE(ConfigurationManager::GetDefaultConnectionName(),
+                       ConfigurationError,
+                       "This CaosDB client hasn't any configured connections.");
+  ConfigurationManager::Clear();
+
+  ConfigurationManager::LoadSingleJSONConfiguration(
+    TEST_DATA_DIR + "/test_broken_caosdb_client_no_connections3.json");
+  EXPECT_THROW_MESSAGE(ConfigurationManager::GetDefaultConnectionName(),
+                       ConfigurationError,
+                       "This CaosDB client hasn't any configured connections.");
+  ConfigurationManager::Clear();
+}
+
+} // namespace caosdb::configuration
diff --git a/test/test_connection.cpp b/test/test_connection.cpp
index 34abc0e4493cd6223339d30d2dba541b5530ea8c..556b49e3cb01cce82a91f10a4b20b6ae7e84ee82 100644
--- a/test/test_connection.cpp
+++ b/test/test_connection.cpp
@@ -23,30 +23,59 @@
 #include <gtest/gtest-message.h>   // for Message
 #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver
 #include <memory>                  // for allocator, operator!=, shared_ptr
-#include "caosdb/connection.h"     // for PemCACertProvider, InsecureCaosDB...
-#include "gtest/gtest_pred_impl.h" // for Test, AssertionResult, EXPECT_EQ
+#include <string>                  // for operator+, string
+#include "caosdb/configuration.h"  // for ConfigurationManager
+#include "caosdb/connection.h"     // for ConnectionManager, InsecureConnec...
+#include "caosdb/exceptions.h"     // for UnknownConnectionError
+#include "caosdb_test_utility.h"   // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR
+#include "gtest/gtest_pred_impl.h" // for Test, AssertionResult, TestInfo
 
 namespace caosdb::connection {
+using caosdb::configuration::ConfigurationManager;
 
-TEST(test_connection, configure_insecure_localhost_8080) {
-  InsecureCaosDBConnectionConfig config("localhost", 8000);
+class test_connection : public ::testing::Test {
+protected:
+  void SetUp() override {
+    ConfigurationManager::Clear();
+    ConfigurationManager::LoadSingleJSONConfiguration(
+      TEST_DATA_DIR + "/test_caosdb_client.json");
+  };
+  void TearDown() override { ConfigurationManager::Clear(); };
+};
 
-  EXPECT_EQ("localhost", config.getHost());
-  EXPECT_EQ(8000, config.getPort());
-  std::shared_ptr<grpc::ChannelCredentials> icc =
-    config.getChannelCredentials();
+TEST_F(test_connection, configure_insecure_localhost_8080) {
+  InsecureConnectionConfiguration configuration("localhost", 8000);
+
+  EXPECT_EQ("localhost", configuration.GetHost());
+  EXPECT_EQ(8000, configuration.GetPort());
+  auto icc = configuration.GetChannelCredentials();
   EXPECT_TRUE(icc != nullptr);
 }
 
-TEST(test_connection, configure_ssl_localhost_8080) {
-  auto cacert = std::make_shared<PemCACertProvider>("ca chain");
-  SslCaosDBConnectionConfig config("localhost", 44300, cacert);
+TEST_F(test_connection, configure_ssl_localhost_8080) {
+  auto cacert = PemCertificateProvider("ca chain");
+  TlsConnectionConfiguration configuration("localhost", 44300, cacert);
 
-  EXPECT_EQ("localhost", config.getHost());
-  EXPECT_EQ(44300, config.getPort());
-  std::shared_ptr<grpc::ChannelCredentials> sslcc =
-    config.getChannelCredentials();
+  EXPECT_EQ("localhost", configuration.GetHost());
+  EXPECT_EQ(44300, configuration.GetPort());
+  auto sslcc = configuration.GetChannelCredentials();
   EXPECT_TRUE(sslcc != nullptr);
 }
 
+TEST_F(test_connection, connection_manager_unknown_connection) {
+  EXPECT_THROW_MESSAGE(ConnectionManager::GetConnection("test"),
+                       caosdb::exceptions::UnknownConnectionError,
+                       "No connection named 'test' present.");
+}
+
+TEST_F(test_connection, connection_manager_get_default_connection) {
+  auto connection = ConnectionManager::GetDefaultConnection();
+  EXPECT_EQ(connection, ConnectionManager::GetConnection("local-caosdb"));
+}
+
+TEST_F(test_connection, connection_manager_get_connection) {
+
+  EXPECT_TRUE(ConnectionManager::GetConnection("local-caosdb-admin"));
+}
+
 } // namespace caosdb::connection
diff --git a/test/test_data/test.json b/test/test_data/test.json
new file mode 100644
index 0000000000000000000000000000000000000000..9f938f4520487219d62ef3b421c0fe938a709a55
--- /dev/null
+++ b/test/test_data/test.json
@@ -0,0 +1,8 @@
+{
+  "this": [ "is", "a", "test" ],
+  "it" : "tests",
+  "numbers": [ 1, 2, 3.3 ],
+  "null values": null,
+  "arrays and objects":
+    { "see?": [ true, false ] }
+}
diff --git a/test/test_data/test_broken_caosdb_client_no_connections1.json b/test/test_data/test_broken_caosdb_client_no_connections1.json
new file mode 100644
index 0000000000000000000000000000000000000000..2c63c0851048d8f7bff41ecf0f8cee05f52fd120
--- /dev/null
+++ b/test/test_data/test_broken_caosdb_client_no_connections1.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/test/test_data/test_broken_caosdb_client_no_connections2.json b/test/test_data/test_broken_caosdb_client_no_connections2.json
new file mode 100644
index 0000000000000000000000000000000000000000..ab2b5b7bb94095706a95faaa55d2b5ae91dc7bff
--- /dev/null
+++ b/test/test_data/test_broken_caosdb_client_no_connections2.json
@@ -0,0 +1,3 @@
+{
+  "connections": null
+}
diff --git a/test/test_data/test_broken_caosdb_client_no_connections3.json b/test/test_data/test_broken_caosdb_client_no_connections3.json
new file mode 100644
index 0000000000000000000000000000000000000000..a4db11b7beac7f67fb01bd28fc062d2fc66ef722
--- /dev/null
+++ b/test/test_data/test_broken_caosdb_client_no_connections3.json
@@ -0,0 +1,4 @@
+{
+  "connections": {
+  }
+}
diff --git a/test/test_data/test_caosdb_client.json b/test/test_data/test_caosdb_client.json
new file mode 100644
index 0000000000000000000000000000000000000000..832b0a62b674af5500815ca1726ccddf9802d757
--- /dev/null
+++ b/test/test_data/test_caosdb_client.json
@@ -0,0 +1,28 @@
+{
+  "connections": {
+    "default": "local-caosdb",
+    "local-caosdb-admin": {
+      "host": "localhost",
+      "port": 8443,
+      "server_certificate_path": "some/path/cacert.pem",
+      "authentication": {
+        "type": "plain",
+        "username": "admin",
+        "password": "caosdb"
+      }
+    },
+    "local-caosdb": {
+      "host": "localhost",
+      "port": 8443,
+      "server_certificate_path": "some/path/cacert.pem",
+      "authentication": {
+        "type": "plain",
+        "username": "me",
+        "password": "secret!"
+      }
+    }
+  },
+  "extension": {
+    "this is my": "special option"
+  }
+}
diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp
index 4dcc1378193f33032757463aa85bdbaef968346c..b528ae2ca01c53f2053002f7958a9a0cc466c50d 100644
--- a/test/test_transaction.cpp
+++ b/test/test_transaction.cpp
@@ -21,7 +21,7 @@
  */
 
 #include <memory>                           // for allocator, make_shared
-#include "caosdb/connection.h"              // for InsecureCaosDBConnection...
+#include "caosdb/connection.h"              // for InsecureConnection...
 #include "caosdb/entity.h"                  // for Entity
 #include "caosdb/entity/v1alpha1/main.pb.h" // for Entity
 #include "caosdb/exceptions.h"              // for ConnectionError
@@ -32,16 +32,16 @@
 #include "gtest/gtest_pred_impl.h"          // for Test, TestInfo, TEST
 
 namespace caosdb::transaction {
-using caosdb::connection::CaosDBConnection;
-using caosdb::connection::InsecureCaosDBConnectionConfig;
+using caosdb::connection::Connection;
+using caosdb::connection::InsecureConnectionConfiguration;
 using caosdb::exceptions::ConnectionError;
 using caosdb::transaction::UniqueResult;
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
 
 TEST(test_transaction, create_transaction) {
-  const auto *pHost = "localhost";
-  auto config = std::make_shared<InsecureCaosDBConnectionConfig>(pHost, 8000);
-  CaosDBConnection connection(config);
+  const auto *host = "localhost";
+  auto configuration = InsecureConnectionConfiguration(host, 8000);
+  Connection connection(configuration);
   auto transaction = connection.CreateTransaction();
 
   transaction->RetrieveById("100");
diff --git a/test/test_utility.cpp b/test/test_utility.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..400e0a6e05a079b0d1eb63ef894789ea249a2fea
--- /dev/null
+++ b/test/test_utility.cpp
@@ -0,0 +1,56 @@
+/*
+ *
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <gtest/gtest-message.h>              // for Message
+#include <gtest/gtest-test-part.h>            // for TestPartResult, SuiteA...
+#include <gtest/gtest_pred_impl.h>            // for Test, EXPECT_EQ, TestInfo
+#include <boost/beast/core/detail/base64.hpp> // for encoded_size
+#include <string>                             // for allocator, string, ope...
+#include "boost/json/object.hpp"              // for object
+#include "boost/json/value.hpp"               // for value
+#include "caosdb/utility.h"                   // for base64_encode, load_js...
+#include "caosdb_test_utility.h"              // for TEST_DATA_DIR
+#include "gmock/gmock-matchers.h"             // for ElementsAre, EXPECT_THAT
+
+namespace caosdb::utility {
+using ::testing::ElementsAre;
+
+TEST(test_utility, base64_encode) {
+  auto test_plain = std::string("admin:caosdb");
+  auto test_encoded = std::string("YWRtaW46Y2Fvc2Ri");
+  EXPECT_EQ(12, test_plain.size());
+  EXPECT_EQ(16, boost::beast::detail::base64::encoded_size(test_plain.size()));
+  EXPECT_EQ(test_encoded, base64_encode(test_plain));
+}
+
+TEST(test_utility, test_load_json_file) {
+  auto json = load_json_file(TEST_DATA_DIR + "/test.json").as_object();
+
+  EXPECT_EQ(json["it"], "tests");
+  EXPECT_EQ(json["null values"], nullptr);
+  EXPECT_THAT(json["this"].as_array(), ElementsAre("is", "a", "test"));
+  EXPECT_THAT(json["numbers"].as_array(), ElementsAre(1, 2, 3.3));
+  auto sub = json["arrays and objects"].as_object();
+  EXPECT_THAT(sub["see?"].as_array(), ElementsAre(true, false));
+}
+
+} // namespace caosdb::utility
diff --git a/test/test_utils.cpp b/test/test_utils.cpp
deleted file mode 100644
index 619caee06a4d1d6981e863058302ee15042f7b3e..0000000000000000000000000000000000000000
--- a/test/test_utils.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
- * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-#include "caosdb/utils.h"
-#include <string>
-#include <gtest/gtest-message.h>
-#include <gtest/gtest-test-part.h>
-#include "gtest/gtest_pred_impl.h"
-#include <boost/beast/core/detail/base64.hpp>
-
-TEST(test_utils, base64_encode) {
-  auto test_plain = std::string("admin:caosdb");
-  auto test_encoded = std::string("YWRtaW46Y2Fvc2Ri");
-  ASSERT_EQ(12, test_plain.size());
-  ASSERT_EQ(16, boost::beast::detail::base64::encoded_size(test_plain.size()));
-  ASSERT_EQ(test_encoded, caosdb::utils::base64_encode(test_plain));
-}