Repository: ORNL/DataFed (language: python)

Issue:
Blake nedved upgrade libcurl openssl



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 8a0b30651..97ed71946 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,14 @@
 /.idea/
 build
+cmake/sodium_version
+cmake/curl_version
+cmake/zlib_version
 common/proto/common/Version.proto
 compose/.env
+compose/globus
+compose/keys
+compose/logs
+compose/unset_env.sh
 config/datafed.sh
 config/datafed-authz.cfg
 config/datafed-core.cfg
@@ -10,9 +17,20 @@ config/datafed-ws.cfg
 core/database/foxx/api/version_router.js
 core/database/foxx/manifest.json
 core/server/Version.hpp
+dependencies
 docs/.buildinfo
 docs/.doctrees/
 docs/.nojekyll
+external/cppzmq/
+external/globus-connect-server-deploy
+external/json-schema-validator/
+external/json/
+external/libcurl/
+external/libsodium/
+external/libzmq/
+external/openssl/
+external/protobuf/
+external/zlib/
 python/datafed_pkg/datafed/VERSION.py
 python/datafed_pkg/datafed.egg-info/
 python/datafed_pkg/datafed/SDMS_Anon_pb2.py
@@ -29,7 +47,9 @@ scripts/globus/datafed-home-repo-form.json
 scripts/globus/mapping.json
 scripts/admin_datafed_backup.sh
 scripts/admin_refresh_certs.sh
+scripts/globus/__pycache__
 services/
+tmp/
 web/SDMS.proto
 web/SDMS_Anon.proto
 web/SDMS_Auth.proto
@@ -37,6 +57,7 @@ web/version.js
 web/Version.proto
 web/datafed-core-key.pub
 web/node_modules/
+web/package-lock.json
 web/static/datafed-core-key.pub
 *.swp
 *.swo
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 999e29b16..d57dd3c2d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -127,10 +127,11 @@ build-ws-base:
     - ci-datafed-core
     - docker
   script:
+    - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - docker system prune -f
-    - docker build -f web/docker/Dockerfile.web-base.ubuntu -t "${REGISTRY}/${IMAGE_TAG}" .
+    - docker build -f web/docker/Dockerfile.web-base.ubuntu -t "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}:latest" .
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - docker push "${REGISTRY}/${IMAGE_TAG}"
+    - docker push "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}:latest"
 
 build-core-base:
   needs: ["clear-core-cache"]
@@ -142,10 +143,11 @@ build-core-base:
     - ci-datafed-core
     - docker
   script:
+    - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - docker system prune -f
-    - docker build -f core/docker/Dockerfile.core-base.ubuntu -t "${REGISTRY}/${IMAGE_TAG}" .
+    - docker build -f core/docker/Dockerfile.core-base.ubuntu -t "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}:latest" .
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - docker push "${REGISTRY}/${IMAGE_TAG}"
+    - docker push "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}:latest"
 
 build-repo-base:
   needs: ["clear-repo-cache"]
@@ -157,10 +159,11 @@ build-repo-base:
     - ci-datafed-repo
     - docker
   script:
+    - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - docker system prune -f
-    - docker build -f repository/docker/Dockerfile.repo-base.ubuntu -t "${REGISTRY}/${IMAGE_TAG}" .
+    - docker build -f repository/docker/Dockerfile.repo-base.ubuntu -t "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}:latest" .
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - docker push "${REGISTRY}/${IMAGE_TAG}"
+    - docker push "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}:latest"
 
 build-python-client-base:
   needs: ["clear-python-client-cache"]
@@ -169,13 +172,14 @@ build-python-client-base:
     GIT_STRATEGY: clone
   stage: build-deploy-base
   tags:
-    - ci-datafed-repo
     - docker
+    - ci-datafed-client
   script:
+    - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - docker system prune -f
-    - docker build -f repository/docker/Dockerfile.repo-base.ubuntu -t "${REGISTRY}/${IMAGE_TAG}" .
+    - docker build -f python/docker/Dockerfile.python-client-base.ubuntu -t "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}:latest" .
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - docker push "${REGISTRY}/${IMAGE_TAG}"
+    - docker push "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}:latest"
 
 build-gcs-base:
   needs: ["clear-repo-cache"]
@@ -186,24 +190,31 @@ build-gcs-base:
     GCS_TAG: "${REGISTRY}/datafed/gcs-ubuntu-focal"
     IMAGE_TAG2: "datafed/gcs-authz-base"
     IMAGE_TAG3: "datafed/gcs-authz"
-    GCS_SUBMODULE_VERSION: "v2.6.0"
   stage: build-deploy-base
   tags:
     - ci-datafed-globus
     - docker
+  rules:
+    - changes:
+        - scripts/dependency_versions.sh
+    - when: manual
   script:
+    - version=$(cat ./scripts/dependency_versions.sh | grep DATAFED_GCS_SUBMODULE_VERSION)
+    - version=${version//\"/}
+    - export $version
+    - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - docker system prune -f
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
     - >
-      if ! docker pull "${GCS_TAG}:${GCS_SUBMODULE_VERSION}"; then
+      if ! docker pull "${GCS_TAG}:${DATAFED_GCS_SUBMODULE_VERSION}-${BRANCH_LOWER}:latest"; then
         cd external/globus-connect-server-deploy/docker
-        git checkout "${GCS_SUBMODULE_VERSION}"
-        ./docker-build-gcs.sh "${DISTRO}"
-        LATEST_IMAGE=$(docker images | grep ubuntu-focal | head -n1 | awk '{print $3}')
-        docker tag "$LATEST_IMAGE" "$GCS_TAG:${GCS_SUBMODULE_VERSION}" 
-        docker push "$GCS_TAG:${GCS_SUBMODULE_VERSION}"
-        docker tag "$GCS_TAG:${GCS_SUBMODULE_VERSION}" "$GCS_TAG" 
-        docker push "$GCS_TAG"
+        git checkout "${DATAFED_GCS_SUBMODULE_VERSION}"
+        docker build --progress plain --tag "$GCS_TAG:${DATAFED_GCS_SUBMODULE_VERSION}" - < "./docker-files/Dockerfile.${DISTRO}"
+        #LATEST_IMAGE=$(docker images | grep ${DISTRO} | head -n1 | awk '{print $3}')
+        #docker tag "$LATEST_IMAGE" "$GCS_TAG:${DATAFED_GCS_SUBMODULE_VERSION}" 
+        docker push "$GCS_TAG:${DATAFED_GCS_SUBMODULE_VERSION}"
+        docker tag "$GCS_TAG:${DATAFED_GCS_SUBMODULE_VERSION}" "${GCS_TAG}-${BRANCH_LOWER}:latest" 
+        docker push "${GCS_TAG}-${BRANCH_LOWER}:latest"
         cd ../../../ # back too root of project
       else
         echo "Docker image already exists in the registry. Skipping build."
@@ -213,12 +224,15 @@ build-gcs-base:
 # STAGE: provision client
 ################################################################################
 provision-client:
+
   needs: ["signal"]
   variables:
     GIT_STRATEGY: clone
   stage: provision-client
   tags:
     - ci-datafed-client
+  before_script:
+    - export PATH=/opt/datafed/dependencies/bin:$PATH
   script:
     - ./scripts/generate_datafed.sh
     - ./scripts/install_client_dependencies.sh
@@ -237,47 +251,52 @@ build-ws:
   needs: ["build-ws-base"]
   stage: build
   variables:
-    IMAGE_TAG: "datafed/ws-"
+    IMAGE_TAG: "datafed/ws"
     GIT_STRATEGY: clone
   tags:
     - ci-datafed-core
     - docker
   rules:
     - changes:
-        - dockerfiles/*
-        - scripts/*
-        - web/*
-        - common/proto/*
+        - dockerfiles/**/*
+        - scripts/**/*
+        - web/**/*
+        - common/proto/**/*
         - .gitlab-ci.yml
       when: on_success
     - exists:
-        - deploy-ws-base
+      - "build-ws-base"
       when: on_success
   script:
     - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - echo "$BRANCH_LOWER"
     - ./scripts/generate_datafed.sh
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - docker build -f web/docker/Dockerfile.web.ubuntu -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" .
-    - docker push "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}"
+    - docker build --build-arg BASE_WEB_IMAGE="${REGISTRY}/${IMAGE_TAG}-base-${BRANCH_LOWER}:latest" -f web/docker/Dockerfile.web.ubuntu -t "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}" .
+    - docker push "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}"
 
 build-core:
   needs: ["build-core-base"]
   stage: build
   variables:
-    IMAGE_TAG: "datafed/core-"
+    IMAGE_TAG: "datafed/core"
     GIT_STRATEGY: clone
   tags:
     - ci-datafed-core
     - docker
   rules:
     - changes:
-        - dockerfiles/*
-        - scripts/*
-        - common/*
-        - core/*
+        - dockerfiles/**/*
+        - scripts/**/*
+        - common/**/*
+        - core/**/*
+        - CMakeLists.txt
+        - cmake/**/*
         - .gitlab-ci.yml
       when: on_success
+    - exists:
+      - "build-core-base"
+      when: on_success
   script:
     - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - echo "$BRANCH_LOWER"
@@ -285,8 +304,8 @@ build-core:
     - ./scripts/generate_datafed.sh
     - cat ./config/datafed.sh
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - docker build -f core/docker/Dockerfile.core.ubuntu -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" .
-    - docker push "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}"
+    - docker build --build-arg BASE_CORE_IMAGE="${REGISTRY}/${IMAGE_TAG}-base-${BRANCH_LOWER}:latest" -f core/docker/Dockerfile.core.ubuntu -t "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}" .
+    - docker push "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}"
 
 build-foxx:
   stage: build
@@ -319,7 +338,7 @@ build-foxx:
 
 build-repo:
   variables:
-    IMAGE_TAG: "datafed/repo-"
+    IMAGE_TAG: "datafed/repo"
     GIT_STRATEGY: clone
   needs: ["build-repo-base"]
   stage: build
@@ -328,39 +347,59 @@ build-repo:
     - docker
   rules:
     - changes:
-        - dockerfiles/*
-        - scripts/*
-        - common/*
-        - repository/*
+        - dockerfiles/**/*
+        - scripts/**/*
+        - common/**/*
+        - repository/**/*
+        - CMakeLists.txt
+        - cmake/**/*
         - .gitlab-ci.yml
       when: on_success
+    - exists:
+      - "build-repo-base"
+      when: on_success
   script:
     - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - echo "$BRANCH_LOWER"
     - ./scripts/generate_datafed.sh
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - docker build -f repository/docker/Dockerfile.repo.ubuntu -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" .
-    - docker push "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}"
+    - docker build --build-arg BASE_REPO_IMAGE="${REGISTRY}/${IMAGE_TAG}-base-${BRANCH_LOWER}:latest" -f repository/docker/Dockerfile.repo.ubuntu -t "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}" .
+    - docker push "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}"
 
 build-gcs-authz:
   stage: build
+  needs: ["build-gcs-base"]
   variables:
-    IMAGE_TAG: "datafed/gcs-authz-"
+    IMAGE_TAG: "datafed/gcs-authz"
     GIT_STRATEGY: clone
   tags:
     - ci-datafed-globus
     - docker
+  rules:
+    - changes:
+        - repository/gridftp/**/*
+        - scripts/**/*
+        - common/**/*
+        - .gitlab-ci.yml
+        - CMakeLists.txt
+        - cmake/**/*
+      when: always
+    - exists:
+      - "build-gcs-base"
+      when: on_success
+    - when: manual
   script:
     - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - echo "$BRANCH_LOWER"
     - ./scripts/generate_datafed.sh
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - docker build -f repository/docker/Dockerfile.gcs-authz.ubuntu -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" .
-    - docker push "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}"
+    - docker build --build-arg BASE_GCS_IMAGE="${REGISTRY}/datafed/gcs-ubuntu-focal-${BRANCH_LOWER}:latest" -f repository/docker/Dockerfile.gcs-authz.ubuntu -t "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}" .
+    - docker push "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}"
 
 build-python-client:
+  needs: ["build-python-client-base"]
   variables:
-    IMAGE_TAG: "datafed/python-client-"
+    IMAGE_TAG: "datafed/python-client"
     GIT_STRATEGY: clone
   stage: build
   tags:
@@ -368,18 +407,23 @@ build-python-client:
     - ci-datafed-client
   rules:
     - changes:
-        - python/*
-        - scripts/*
-        - common/*
+        - python/**/*
+        - scripts/**/*
+        - common/**/*
         - .gitlab-ci.yml
+        - CMakeLists.txt
+        - cmake/**/*
       when: always
+    - exists:
+      - "build-python-client-base"
+      when: on_success
   script:
     - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - echo "$BRANCH_LOWER"
     - ./scripts/generate_datafed.sh
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - docker build -f python/docker/Dockerfile.python-client.ubuntu -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" .
-    - docker push "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}"
+    - docker build --build-arg BASE_PYTHON_CLIENT_IMAGE="${REGISTRY}/${IMAGE_TAG}-base-${BRANCH_LOWER}:latest" -f python/docker/Dockerfile.python-client.ubuntu -t "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}" .
+    - docker push "${REGISTRY}/${IMAGE_TAG}-${BRANCH_LOWER}"
 
 ################################################################################
 # STAGE: Integration
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0e7e96143..c878eca29 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
    smaller images
 4. [912] - Adding working compose instance for core metadata services.
 5. [937] - Working metadata services running together as part of CI
+6. [946] - Added docker compose for DataFed Repository and for Metadata Services
 
 ## PATCH Bug fixes/Technical Debt/Documentation
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cf0910e17..5e1ca2e68 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,6 +16,7 @@ if( NOT DEFINED GLOBUS_VERSION )
   set(GLOBUS_VERSION  5)
 endif()
 OPTION(BUILD_AUTHZ "Build DataFed Authz library" FALSE)
+OPTION(BUILD_AUTHZ_WITH_SYSLOG "Build Authz without syslog" TRUE)
 OPTION(BUILD_CORE_SERVER "Build DataFed Core Server" TRUE)
 OPTION(BUILD_COMMON "Build DataFed common library" TRUE)
 OPTION(BUILD_DOCS "Build documentation" TRUE)
@@ -25,6 +26,13 @@ OPTION(BUILD_PYTHON_CLIENT "Build python client" TRUE)
 OPTION(BUILD_TESTS "Build Tests" TRUE)
 OPTION(BUILD_WEB_SERVER "Build DataFed Web Server" TRUE)
 OPTION(ENABLE_UNIT_TESTS "Enable unit tests" TRUE)
+OPTION(BUILD_SHARED_LIBS "By default DataFed tries to build static libraries
+with the exception of libdatafed-authz which must always be a shared library,
+it will also try to link with as many static libraries as possible. However,
+building with static depencies is not completely possible because some system
+libraries must be shared libraries for DataFed to be interoperable. If this
+setting is turned on DataFed will build it's libraries as shared and try to
+link to shared libraries." OFF)
 
 set(INSTALL_REPO_SERVER ${BUILD_REPO_SERVER})
 set(INSTALL_AUTHZ ${BUILD_AUTHZ})
@@ -49,9 +57,15 @@ get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_JSON_SCHEMA_VALIDATOR_VE
 get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_OPENSSL" LOCAL_OPENSSL_VERSION)
 get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_LIBCURL" LOCAL_CURL_VERSION)
 get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_BOOST" LOCAL_BOOST_VERSION)
+get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_ZLIB_VERSION" LOCAL_ZLIB_VERSION)
+# Prioritize the CMD line argument over config file
+if(NOT DEFINED DATAFED_INSTALL_PATH)
+  get_value_from_datafed_sh("DATAFED_INSTALL_PATH" DATAFED_INSTALL_PATH)
+endif()
 
-get_value_from_datafed_sh("DATAFED_DEPENDENCIES_INSTALL_PATH" DEPENDENCY_INSTALL_PATH)
-get_value_from_datafed_sh("DATAFED_INSTALL_PATH" DATAFED_INSTALL_PATH)
+if(NOT DEFINED DATAFED_DEPENDENCIES_INSTALL_PATH)
+  get_value_from_datafed_sh("DATAFED_DEPENDENCIES_INSTALL_PATH" DEPENDENCY_INSTALL_PATH)
+endif()
 
 set(CMAKE_PREFIX_PATH "${DEPENDENCY_INSTALL_PATH}")
 
@@ -91,16 +105,7 @@ configure_file(
 file( GLOB ProtoFiles "${PROJECT_SOURCE_DIR}/common/proto/common/*.proto" )
 
 if( BUILD_WEB_SERVER )
-  configure_file(
-    "${CMAKE_CURRENT_SOURCE_DIR}/web/version.js.in"
-    "${CMAKE_CURRENT_SOURCE_DIR}/web/version.js"
-    @ONLY)
-
-  configure_file(
-    "${CMAKE_CURRENT_SOURCE_DIR}/web/package.json.in"
-    "${CMAKE_CURRENT_SOURCE_DIR}/web/package.json"
-    @ONLY)
-
+  include(./cmake/Web.cmake)
   file(COPY ${ProtoFiles} DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/web/")
 endif()
 
@@ -112,16 +117,7 @@ if ( BUILD_REPO_SERVER OR BUILD_CORE_SERVER OR BUILD_AUTHZ OR BUILD_COMMON)
 
   include_directories( "/usr/include/globus" )
 
-  if ( DEFINED ENV{BOOST_INC} AND DEFINED ENV{BOOST_LIB} )
-          message( "BOOST_INC:" $ENV{BOOST_INC} )
-          message( "BOOST_LIB:" $ENV{BOOST_LIB} )
-          include_directories( $ENV{BOOST_INC} )
-          link_directories( $ENV{BOOST_LIB} )
-  else()
-    #set(Boost_USE_STATIC_LIBS   ON)
-    find_package(Boost ${LOCAL_BOOST_VERSION} REQUIRED COMPONENTS date_time unit_test_framework system filesystem
-      program_options)
-  endif()
+  include(./cmake/Boost.cmake)
 
   find_package(Threads REQUIRED)
   ## load in pkg-config support
@@ -152,8 +148,9 @@ endif()
 if( BUILD_CORE_SERVER )
   include(./cmake/JSON.cmake)
   include(./cmake/JSONSchema.cmake)
-  find_package(OpenSSL "${LOCAL_OPENSSL_VERSION}" REQUIRED )
-  find_package(CURL "${LOCAL_CURL_VERSION}" REQUIRED )
+  include(./cmake/Zlib.cmake)
+  include(./cmake/OpenSSL.cmake)
+  include(./cmake/CURL.cmake)
 endif()
 
 if( BUILD_FOXX OR BUILD_CORE_SERVER )
@@ -228,6 +225,7 @@ message("json_schema_validator Version: ${JSON_SCHEMA_VALIDATOR_VERSION}")
 message("                 curl Version: ${LOCAL_CURL_VERSION}")
 message("              openssl Version: ${LOCAL_OPENSSL_VERSION}")
 message("                boost Version: ${LOCAL_BOOST_VERSION}")
+message("                 zlib Version: ${LOCAL_ZLIB_VERSION}")
 message("")
 message("Found Dependency Versions")
 message("             protobuf Version: ${DATAFED_PROTOBUF_VERSION_ACTUAL}")
@@ -236,9 +234,10 @@ message("               libzmq Version: ${DATAFED_ZEROMQ_VERSION_ACTUAL}")
 message("            libsodium Version: ${DATAFED_ZEROMQ_SODIUM_VERSION}")
 message("         nlohman json Version: ${DATAFED_JSON_VERSION_ACTUAL}")
 message("json_schema_validator Version: ${DATAFED_JSON_SCHEMA_VERSION_ACTUAL}")
-message("                 curl Version: ${CURL_VERSION_STRING}")
+message("                 curl Version: ${DATAFED_CURL_VERSION_ACTUAL}")
 message("              openssl Version: ${OPENSSL_VERSION}")
-message("                boost Version: ${Boost_VERSION}")
+message("                boost Version: ${DATAFED_BOOST_VERSION_ACTUAL}")
+message("                 zlib Version: ${DATAFED_ZLIB_VERSION_ACTUAL}")
 message("")
 message("Dependency Paths")
 message("                 protobuf Library: ${DATAFED_PROTOBUF_LIBRARY_PATH}")
@@ -249,9 +248,14 @@ message("                libsodium Library: ${DATAFED_ZEROMQ_SODIUM_LIBRARY_PATH
 message("         nlohman json Include Dir: ${DATAFED_JSON_INCLUDE_PATH}")
 message("json_schema_validator Include Dir: ${DATAFED_JSON_SCHEMA_INCLUDE_PATH}")
 message("    json_schema_validator Library: ${DATAFED_JSON_SCHEMA_LIBRARY_PATH}")
-message("                     curl Library: ")
-message("                  openssl Library: ")
-message("                    boost Library: ")
+message("                   crypto Library: ${OPENSSL_CRYPTO_LIBRARY}")
+message("                     curl Library: ${DATAFED_CURL_LIBRARIES}")
+message("                  openssl Library: ${OPENSSL_SSL_LIBRARY}")
+message("          boost date_time Library: ${DATAFED_BOOST_DATE_TIME_LIBRARY_PATH}")
+message("boost unit_test_framework Library: ${DATAFED_BOOST_UNIT_TEST_FRAMEWORK_LIBRARY_PATH}")
+message("             boost system Library: ${DATAFED_BOOST_SYSTEM_LIBRARY_PATH}")
+message("         boost filesystem Library: ${DATAFED_BOOST_FILESYSTEM_LIBRARY_PATH}")
+message("    boost program_options Library: ${DATAFED_BOOST_PROGRAM_OPTIONS_LIBRARY_PATH}")
+message("                     zlib Library: ${DATAFED_ZLIB_LIBRARIES}")
 message("")
 
-
diff --git a/cmake/Boost.cmake b/cmake/Boost.cmake
new file mode 100644
index 000000000..f32fb16f1
--- /dev/null
+++ b/cmake/Boost.cmake
@@ -0,0 +1,33 @@
+
+function(find_boost_library)
+  if ( DEFINED ENV{BOOST_INC} AND DEFINED ENV{BOOST_LIB} )
+    message( "BOOST_INC:" $ENV{BOOST_INC} )
+    message( "BOOST_LIB:" $ENV{BOOST_LIB} )
+    include_directories( $ENV{BOOST_INC} )
+    link_directories( $ENV{BOOST_LIB} )
+  else()
+    if(NOT BUILD_SHARED_LIBS)
+      set(Boost_USE_STATIC_LIBS ON)
+    endif()
+    find_package(Boost ${LOCAL_BOOST_VERSION} REQUIRED COMPONENTS date_time unit_test_framework system filesystem program_options)
+
+    include_directories( ${Boost_INCLUDE_PATH} )
+    get_target_property(Boost_DATE_TIME_LIBRARY_PATH ${Boost_DATE_TIME_LIBRARY} LOCATION)
+    get_target_property(Boost_UNIT_TEST_FRAMEWORK_LIBRARY_PATH ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} LOCATION)
+    get_target_property(Boost_SYSTEM_LIBRARY_PATH ${Boost_SYSTEM_LIBRARY} LOCATION)
+    get_target_property(Boost_FILESYSTEM_LIBRARY_PATH ${Boost_FILESYSTEM_LIBRARY} LOCATION)
+    get_target_property(Boost_PROGRAM_OPTIONS_LIBRARY_PATH ${Boost_PROGRAM_OPTIONS_LIBRARY} LOCATION)
+
+    set(DATAFED_BOOST_INCLUDE_PATH "${Boost_INCLUDE_PATH}" PARENT_SCOPE)
+    set(DATAFED_BOOST_VERSION_ACTUAL "${Boost_VERSION}" PARENT_SCOPE)
+    set(DATAFED_BOOST_LIBRARIES "${Boost_LIBRARIES}" PARENT_SCOPE)
+    set(DATAFED_BOOST_SYSTEM_LIBRARY_PATH "${Boost_SYSTEM_LIBRARY_PATH}" PARENT_SCOPE)
+    set(DATAFED_BOOST_FILESYSTEM_LIBRARY_PATH "${Boost_FILESYSTEM_LIBRARY_PATH}" PARENT_SCOPE)
+    set(DATAFED_BOOST_DATE_TIME_LIBRARY_PATH "${Boost_DATE_TIME_LIBRARY_PATH}" PARENT_SCOPE)
+    set(DATAFED_BOOST_UNIT_TEST_FRAMEWORK_LIBRARY_PATH "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY_PATH}" PARENT_SCOPE)
+    set(DATAFED_BOOST_PROGRAM_OPTIONS_LIBRARY_PATH "${Boost_PROGRAM_OPTIONS_LIBRARY_PATH}" PARENT_SCOPE)
+  endif()
+
+endfunction()
+
+find_boost_library()
diff --git a/cmake/CURL.cmake b/cmake/CURL.cmake
new file mode 100644
index 000000000..4fef10bc8
--- /dev/null
+++ b/cmake/CURL.cmake
@@ -0,0 +1,98 @@
+function(find_curl_library)
+
+  set(CURL_SSH_LIBRARY "")
+
+  if(EXISTS ${DEPENDENCY_INSTALL_PATH})
+    if(EXISTS ${DEPENDENCY_INSTALL_PATH}/include)
+      set(CURL_INCLUDE_DIR "${DEPENDENCY_INSTALL_PATH}/include" )
+      include_directories(${CURL_INCLUDE_DIR})
+    endif()
+    if(EXISTS ${DEPENDENCY_INSTALL_PATH}/lib/libcurl.a)
+      set(CURL_LIBRARIES "${DEPENDENCY_INSTALL_PATH}/lib/libcurl.a" )
+    endif()
+  endif()
+
+  if(NOT EXISTS ${CURL_LIBRARIES})
+
+    if(BUILD_SHARED_LIBS)
+      SET(CMAKE_FIND_LIBRARY_SUFFIXES ".so")
+      set(CURL_USE_STATIC_LIBS FALSE)
+    else()
+      SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
+      set(CURL_USE_STATIC_LIBS TRUE)
+  endif()
+    find_package(CURL REQUIRED )
+
+    # NOTE - depending on what libraries the system curl library was built with
+    # it may be very difficult to link all the other third party libraries
+    if(CURL_FOUND)
+      include_directories(${CURL_INCLUDE_DIR})
+
+      # Sometimes curl is built with support for ssh, we will go ahead and see
+      # if we can locat the ssh library
+      get_filename_component(FOLDER_WHERE_CURL_LIB_FOUND ${CURL_LIBRARIES} DIRECTORY)
+
+      find_library(SSH_LIBRARY_FOUND
+        NAMES libssh libssh.a libssh.so libssh.so.*
+        PATHS /lib /usr/lib /usr/local/lib ${FOLDER_WHERE_CURL_LIB_FOUND})
+
+      if(SSH_LIBRARY_FOUND)
+        set(CURL_SSH_LIBRARY ${SSH_LIBRARY_FOUND})
+      endif()
+
+    endif()
+
+  endif()
+
+  set(version_file "${CMAKE_CURRENT_LIST_DIR}/curl_version")
+  if(EXISTS "${version_file}")
+    file(REMOVE "${version_file}")
+  endif()
+
+  # Keep the two processes separate, cmake seems to want to run them in parallel
+  # for some reason if they are in the same execute_process call.
+  execute_process(
+    COMMAND ${CMAKE_CXX_COMPILER} -o ${version_file} "${version_file}.cpp"
+    ${CURL_LIBRARIES} ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}
+    ${DATAFED_ZLIB_LIBRARIES} ${CURL_SSH_LIBRARY} -I${CURL_INCLUDE_DIR}
+    -lpthread  -ldl
+    OUTPUT_VARIABLE CURL_BUILD_VERSION_OUTPUT
+    ERROR_VARIABLE CURL_BUILD_VERSION_ERROR
+    RESULT_VARIABLE CURL_BUILD_VERSION_RESULT
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_STRIP_TRAILING_WHITESPACE
+  )
+
+  # NOTE if the build was success the return code should be 0.
+  if(NOT ${CURL_BUILD_VERSION_RESULT} EQUAL 0 )
+    message(FATAL_ERROR "Unable to build with provided curl ssl and crypto libraries:
+    CURL_LIBRARIES: ${CURL_LIBRARIES} 
+    OPENSSL_SSL_LIBRARY: ${OPENSSL_SSL_LIBRARY}
+    OPENSSL_CRYPTO_LIBRARY: ${OPENSSL_CRYPTO_LIBRARY}
+    Compiling test code fails.\n
+    NOTE - it is possible that curl was built with some other libraries that
+    have not been found. i.e.
+    ${CURL_SSH_LIBRARY}
+    \n
+    ERROR message is \n${CURL_BUILD_VERSION_ERROR}\n")
+  endif()
+
+
+  execute_process(
+    COMMAND ${version_file}
+    OUTPUT_VARIABLE CURL_VERSION_OUTPUT
+    ERROR_VARIABLE CURL_VERSION_ERROR
+    RESULT_VARIABLE CURL_VERSION_RESULT
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_STRIP_TRAILING_WHITESPACE
+  )
+
+  # Note parent scoped variables are not defined locally they only exist in
+  # the parent script.
+  set(DATAFED_CURL_VERSION_ACTUAL "${CURL_VERSION_OUTPUT}" PARENT_SCOPE)
+  set(DATAFED_CURL_INCLUDE_DIR "${CURL_INCLUDE_DIR}" PARENT_SCOPE)
+  set(DATAFED_CURL_LIBRARIES "${CURL_LIBRARIES}" PARENT_SCOPE)
+
+endfunction()
+
+find_curl_library()
diff --git a/cmake/OpenSSL.cmake b/cmake/OpenSSL.cmake
new file mode 100644
index 000000000..b95e332d5
--- /dev/null
+++ b/cmake/OpenSSL.cmake
@@ -0,0 +1,36 @@
+function(find_openssl_library)
+
+  if(EXISTS "${DEPENDENCY_INSTALL_PATH}/lib/libssl.a")
+    set(OPENSSL_FOUND "TRUE" PARENT_SCOPE)
+    set(OPENSSL_INCLUDE_DIR "${DEPENDENCY_INSTALL_PATH}/include" PARENT_SCOPE)
+    set(OPENSSL_CRYPTO_LIBRARY "${DEPENDENCY_INSTALL_PATH}/lib/libcrypto.a" PARENT_SCOPE)
+    set(OPENSSL_CRYPTO_LIBRARIES "${DEPENDENCY_INSTALL_PATH}/lib/libcrypto.a" PARENT_SCOPE)
+    set(OPENSSL_SSL_LIBRARY "${DEPENDENCY_INSTALL_PATH}/lib/libssl.a" PARENT_SCOPE)
+    set(OPENSSL_SSL_LIBRARIES "${DEPENDENCY_INSTALL_PATH}/lib/libssl.a" PARENT_SCOPE)
+    set(OPENSSL_LIBRARIES "${DEPENDENCY_INSTALL_PATH}/lib/libssl.a ${DEPENDENCY_INSTALL_PATH}/lib/libcrypto.a" PARENT_SCOPE)
+    set(OPENSSL_VERSION "1.1.1w" PARENT_SCOPE)
+  else()
+    set(OPENSSL_ROOT_DIR "${DEPENDENCY_INSTALL_PATH}")
+    
+    if(BUILD_SHARED_LIBS)
+      set(OPENSSL_USE_STATIC_LIBS FALSE)
+      SET(CMAKE_FIND_LIBRARY_SUFFIXES ".so")
+    else()
+      set(OPENSSL_USE_STATIC_LIBS TRUE)
+      SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
+    endif()
+    find_package(OpenSSL REQUIRED)
+
+    set(OPENSSL_FOUND "${OPENSSL_FOUND}" PARENT_SCOPE)
+    set(OPENSSL_INCLUDE_DIR "${OPENSSL_INCLUDE_DIR}" PARENT_SCOPE)
+    set(OPENSSL_CRYPTO_LIBRARY "${OPENSSL_CRYPTO_LIBRARY}" PARENT_SCOPE)
+    set(OPENSSL_CRYPTO_LIBRARIES "${OPENSSL_CRYPTO_LIBRARIES}" PARENT_SCOPE)
+    set(OPENSSL_SSL_LIBRARY "${OPENSSL_SSL_LIBRARY}" PARENT_SCOPE)
+    set(OPENSSL_SSL_LIBRARIES "${OPENSSL_SSL_LIBRARIES}" PARENT_SCOPE)
+    set(OPENSSL_LIBRARIES "${OPENSSL_LIBRARIES}" PARENT_SCOPE)
+    set(OPENSSL_VERSION "${OPENSSL_VERSION}" PARENT_SCOPE)
+  endif()
+
+endfunction()
+
+find_openssl_library()
diff --git a/cmake/Web.cmake b/cmake/Web.cmake
new file mode 100644
index 000000000..dbaf66496
--- /dev/null
+++ b/cmake/Web.cmake
@@ -0,0 +1,19 @@
+cmake_minimum_required (VERSION 3.17.0)
+
+# DataFed Version numbers need to be defined before running configure scripts
+include (${CMAKE_CURRENT_LIST_DIR}/Version.cmake)
+
+# Prepare web files
+# This script can be run as part of the cmake include process or as it's own
+# script
+configure_file(
+  "${CMAKE_CURRENT_LIST_DIR}/../web/version.js.in"
+  "${CMAKE_CURRENT_LIST_DIR}/../web/version.js"
+  @ONLY)
+
+configure_file(
+  "${CMAKE_CURRENT_LIST_DIR}/../web/package.json.in"
+  "${CMAKE_CURRENT_LIST_DIR}/../web/package.json"
+  @ONLY)
+
+
diff --git a/cmake/ZeroMQ.cmake b/cmake/ZeroMQ.cmake
index 676fd6f0c..3b0ad50ba 100644
--- a/cmake/ZeroMQ.cmake
+++ b/cmake/ZeroMQ.cmake
@@ -4,9 +4,13 @@ function(find_zeromq_library)
   set(ZEROMQ_LIB_CMAKE_DIR "" )
   set(ZEROMQ_LIBRARY_PATH "" )
 
-  if(EXISTS ${DEPENDENCY_INSTALL_PATH})
+  if(EXISTS ${DEPENDENCY_INSTALL_PATH}/lib/cmake)
     set(ZEROMQ_LIB_CMAKE_DIR "${DEPENDENCY_INSTALL_PATH}/lib/cmake")
+  endif()
+  if(EXISTS ${DEPENDENCY_INSTALL_PATH}/include)
     set(ZEROMQ_INCLUDE_DIR "${DEPENDENCY_INSTALL_PATH}/include")
+  endif()
+  if(EXISTS ${DEPENDENCY_INSTALL_PATH}/lib)
     set(ZEROMQ_LIB_DIR "${DEPENDENCY_INSTALL_PATH}/lib")
   endif()
 
@@ -31,15 +35,39 @@ function(find_zeromq_library)
     endif()
   endforeach()
 
+  set(version_file "${CMAKE_CURRENT_LIST_DIR}/sodium_version")
+  if(EXISTS "${version_file}")
+    file(REMOVE "${version_file}")
+  endif()
+
+  # Keep the two processes separate, cmake seems to want to run them in parallel
+  # for some reason if they are in the same execute_process call.
+  execute_process(
+    COMMAND ${CMAKE_CXX_COMPILER} -o ${version_file}
+    "${version_file}.cpp" ${ZEROMQ_SODIUM_LIBRARY_PATH} -I${ZEROMQ_INCLUDE_DIR}
+    OUTPUT_VARIABLE SODIUM_BUILD_VERSION_OUTPUT
+    ERROR_VARIABLE SODIUM_BUILD_VERSION_ERROR
+    RESULT_VARIABLE SODIUM_BUILD_VERSION_RESULT
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_STRIP_TRAILING_WHITESPACE
+  )
+
+  # NOTE if the build was success the return code should be 0.
+  if(NOT ${SODIUM_BUILD_VERSION_RESULT} EQUAL 0 )
+    message(FATAL_ERROR "Unable to build with provided ZeroMQ sodium library:
+    ${ZEROMQ_SODIUM_LIBRARY_PATH} . Compiling test code fails.
+    ${SODIUM_BUILD_VERSION_ERROR}
+    ")
+  endif()
+
   execute_process(
-    COMMAND ${CMAKE_CXX_COMPILER} -o ${CMAKE_CURRENT_LIST_DIR}/sodium_version ${CMAKE_CURRENT_LIST_DIR}/sodium_version.cpp ${DEPENDENCY_INSTALL_PATH}/lib/libsodium.a 
-    COMMAND ${CMAKE_CURRENT_LIST_DIR}/sodium_version
+    COMMAND ${version_file}
     OUTPUT_VARIABLE SODIUM_VERSION_OUTPUT
     ERROR_VARIABLE SODIUM_VERSION_ERROR
     RESULT_VARIABLE SODIUM_VERSION_RESULT
     OUTPUT_STRIP_TRAILING_WHITESPACE
     ERROR_STRIP_TRAILING_WHITESPACE
-    )
+  )
 
   set(DATAFED_ZEROMQ_INCLUDE_DIR "${ZEROMQ_INCLUDE_DIR}" PARENT_SCOPE)
   set(DATAFED_ZEROMQ_LIB_DIR "${ZEROMQ_LIB_DIR}"  PARENT_SCOPE)
diff --git a/cmake/Zlib.cmake b/cmake/Zlib.cmake
new file mode 100644
index 000000000..b87e037da
--- /dev/null
+++ b/cmake/Zlib.cmake
@@ -0,0 +1,48 @@
+
+function(find_zlib_library)
+  # Find zlib
+  if(EXISTS ${DEPENDENCY_INSTALL_PATH})
+    if(EXISTS ${DEPENDENCY_INSTALL_PATH}/include)
+      set(ZLIB_INCLUDE_DIR "${DEPENDENCY_INSTALL_PATH}/include" )
+      include_directories(${ZLIB_INCLUDE_DIR})
+    endif()
+    if(EXISTS ${DEPENDENCY_INSTALL_PATH}/lib/libz.a)
+      set(ZLIB_LIBRARIES "${DEPENDENCY_INSTALL_PATH}/lib/libz.a" )
+    endif()
+  endif()
+
+  if(NOT EXISTS ${ZLIB_LIBRARIES})
+    SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
+    find_package(ZLIB REQUIRED)
+  endif()
+
+  set(ZLIB_INCLUDE_DIR "${DEPENDENCY_INSTALL_PATH}/include" )
+
+  set(version_file "${CMAKE_CURRENT_LIST_DIR}/zlib_version")
+  if(EXISTS "${version_file}")
+    file(REMOVE "${version_file}")
+  endif()
+
+  # Keep the two processes separate, cmake seems to want to run them in parallel
+  # for some reason if they are in the same execute_process call.
+  execute_process(
+    COMMAND ${CMAKE_CXX_COMPILER} -o ${version_file} "${version_file}.cpp" ${ZLIB_LIBRARIES} 
+  )
+  execute_process(
+    COMMAND ${version_file}
+    OUTPUT_VARIABLE ZLIB_VERSION_OUTPUT
+    ERROR_VARIABLE ZLIB_VERSION_ERROR
+    RESULT_VARIABLE ZLIB_VERSION_RESULT
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_STRIP_TRAILING_WHITESPACE
+  )
+
+  # Note parent scoped variables are not defined locally they only exist in
+  # the parent script.
+  set(DATAFED_ZLIB_VERSION_ACTUAL "${ZLIB_VERSION_OUTPUT}" PARENT_SCOPE)
+  set(DATAFED_ZLIB_INCLUDE_DIR "${ZLIB_INCLUDE_DIR}" PARENT_SCOPE)
+  set(DATAFED_ZLIB_LIBRARIES "${ZLIB_LIBRARIES}" PARENT_SCOPE)
+
+endfunction()
+
+find_zlib_library()
diff --git a/cmake/curl_version.cpp b/cmake/curl_version.cpp
new file mode 100644
index 000000000..18ad1ab6c
--- /dev/null
+++ b/cmake/curl_version.cpp
@@ -0,0 +1,12 @@
+#include <curl/curl.h>
+#include <iostream>
+
+/**
+ * This script is used to show what vesion of curl is being used with the
+ * rest of the build process and will print the version number of the curl
+ * library.
+ **/
+int main() {
+  std::cout << curl_version() << std::endl;
+  return 0;
+}
diff --git a/cmake/zlib_version.cpp b/cmake/zlib_version.cpp
new file mode 100644
index 000000000..9544bd0a2
--- /dev/null
+++ b/cmake/zlib_version.cpp
@@ -0,0 +1,12 @@
+#include <iostream>
+#include <zlib.h>
+
+/**
+ * This little file is used to make sure that we are compiling with the version
+ * of a library that we think we are. This one when run will print the zlib
+ * version numbers.
+ **/
+int main() {
+  std::cout << zlibVersion() << std::endl;
+  return 0;
+}
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 10c0c3c04..ab7244a97 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -17,9 +17,15 @@ if( BUILD_COMMON )
     "source/servers/*.cpp"
     "source/support/zeromq/*.cpp"
     "source/sockets/*.cpp")
-  add_library( common STATIC ${Sources})
+  if(BUILD_SHARED_LIBS)
+    add_library( common SHARED ${Sources})
+    target_link_libraries( common PUBLIC ${DATAFED_BOOST_DATE_TIME_LIBRARY_PATH} protobuf::libprotobuf libzmq datafed-protobuf) 
+  else()
+    add_library( common STATIC ${Sources})
+    target_link_libraries( common PUBLIC ${DATAFED_BOOST_DATE_TIME_LIBRARY_PATH} protobuf::libprotobuf libzmq-static datafed-protobuf) 
+  endif()
+  
   set_target_properties(common PROPERTIES POSITION_INDEPENDENT_CODE ON SOVERSION ${DATAFED_COMMON_LIB_MAJOR} VERSION ${DATAFED_COMMON_LIB_MAJOR}.${DATAFED_COMMON_LIB_MINOR}.${DATAFED_COMMON_LIB_PATCH} )
-  target_link_libraries( common Boost::date_time protobuf::libprotobuf libzmq-static datafed-protobuf) 
   target_include_directories( common PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include )
   target_include_directories( common PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include )
   target_include_directories( common PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/source )
diff --git a/common/include/common/CommunicatorFactory.hpp b/common/include/common/CommunicatorFactory.hpp
index 6dac56f24..8b5c66c7a 100644
--- a/common/include/common/CommunicatorFactory.hpp
+++ b/common/include/common/CommunicatorFactory.hpp
@@ -18,7 +18,8 @@ class CommunicatorFactory {
   LogContext m_log_context;
 
 public:
-  CommunicatorFactory(LogContext log_context) : m_log_context(log_context){};
+  explicit CommunicatorFactory(const LogContext &log_context)
+      : m_log_context(log_context){};
 
   std::unique_ptr<ICommunicator> create(const SocketOptions &socket_options,
                                         const ICredentials &credentials,
diff --git a/common/include/common/DynaLog.hpp b/common/include/common/DynaLog.hpp
index ba755b44f..0f8814a98 100644
--- a/common/include/common/DynaLog.hpp
+++ b/common/include/common/DynaLog.hpp
@@ -73,7 +73,25 @@
 
 namespace SDMS {
 
-enum class LogLevel { CRITICAL, ERROR, WARNING, INFO, DEBUG, TRACE };
+/**
+ * Here unsigned int is used so that we can compare the numeric values when
+ * choosing whether to print a log message.
+ *
+ * CRITICAL = 0
+ * ERROR    = 1
+ * WARNING  = 2
+ * INFO     = 3
+ * DEBUG    = 4
+ * TRACE    = 5
+ **/
+enum class LogLevel : unsigned int {
+  CRITICAL,
+  ERROR,
+  WARNING,
+  INFO,
+  DEBUG,
+  TRACE
+};
 
 std::string toString(const LogLevel level);
 int toSysLog(const LogLevel level);
diff --git a/common/include/common/GSSAPI_Utils.hpp b/common/include/common/GSSAPI_Utils.hpp
index 331e9a15e..f8e654a89 100644
--- a/common/include/common/GSSAPI_Utils.hpp
+++ b/common/include/common/GSSAPI_Utils.hpp
@@ -21,21 +21,21 @@ class gssString {
     m_gss_buf.length = 0;
   }
 
-  gssString(const std::string &a_src) {
+  explicit gssString(const std::string &a_src) {
     m_gss_buf.value = 0;
     m_gss_buf.length = 0;
 
     set(a_src);
   }
 
-  gssString(const char *a_src) {
+  explicit gssString(const char *a_src) {
     m_gss_buf.value = 0;
     m_gss_buf.length = 0;
 
     set(a_src);
   }
 
-  gssString(gss_name_t a_src) {
+  explicit gssString(gss_name_t a_src) {
     m_gss_buf.value = 0;
     m_gss_buf.length = 0;
 
diff --git a/common/include/common/ServerFactory.hpp b/common/include/common/ServerFactory.hpp
index f469377e5..7398cb272 100644
--- a/common/include/common/ServerFactory.hpp
+++ b/common/include/common/ServerFactory.hpp
@@ -30,7 +30,8 @@ class ServerFactory {
   LogContext m_log_context;
 
 public:
-  ServerFactory(LogContext log_context) : m_log_context(log_context){};
+  explicit ServerFactory(const LogContext &log_context)
+      : m_log_context(log_context){};
 
   std::unique_ptr<IServer> create(
       ServerType server_type,
diff --git a/common/include/common/SmartTokenizer.hpp b/common/include/common/SmartTokenizer.hpp
index 01de3f22c..8608a3949 100644
--- a/common/include/common/SmartTokenizer.hpp
+++ b/common/include/common/SmartTokenizer.hpp
@@ -12,7 +12,7 @@ template <char delim = ' ', bool keep_empty = false> class SmartTokenizer {
 
   SmartTokenizer() {}
 
-  SmartTokenizer(const std::string &a_input_str) {
+  explicit SmartTokenizer(const std::string &a_input_str) {
     m_buffer.reserve(a_input_str.size());
     _parse(a_input_str.c_str(), a_input_str.size());
   }
@@ -133,4 +133,4 @@ template <char delim = ' ', bool keep_empty = false> class SmartTokenizer {
   std::vector<const char *> m_tokens;
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/common/include/common/libjson.hpp b/common/include/common/libjson.hpp
index 4c353a663..b12a30f79 100644
--- a/common/include/common/libjson.hpp
+++ b/common/include/common/libjson.hpp
@@ -389,17 +389,17 @@ class Value {
 
   Value() : m_type(VT_NULL), m_value({0}) {}
 
-  Value(bool a_value) : m_type(VT_BOOL) { m_value.b = a_value; }
+  explicit Value(bool a_value) : m_type(VT_BOOL) { m_value.b = a_value; }
 
-  Value(double a_value) : m_type(VT_NUMBER) { m_value.n = a_value; }
+  explicit Value(double a_value) : m_type(VT_NUMBER) { m_value.n = a_value; }
 
-  Value(int a_value) : m_type(VT_NUMBER) { m_value.n = a_value; }
+  explicit Value(int a_value) : m_type(VT_NUMBER) { m_value.n = a_value; }
 
-  Value(const std::string &a_value) : m_type(VT_STRING) {
+  explicit Value(const std::string &a_value) : m_type(VT_STRING) {
     m_value.s = new String(a_value);
   }
 
-  Value(const char *a_value) : m_type(VT_STRING) {
+  explicit Value(const char *a_value) : m_type(VT_STRING) {
     m_value.s = new String(a_value);
   }
 
@@ -410,7 +410,7 @@ class Value {
     a_source.m_value.o = 0;
   }
 
-  Value(ValueType a_type) : m_type(a_type) {
+  explicit Value(ValueType a_type) : m_type(a_type) {
     if (m_type == VT_OBJECT) {
       m_value.o = new Object();
     } else if (m_type == VT_ARRAY) {
@@ -837,7 +837,7 @@ class Value {
     switch (m_type) {
     case VT_OBJECT:
       a_buffer.append("{");
-      for (ObjectIter i = m_value.o->begin(); i != m_value.o->end(); i++) {
+      for (ObjectIter i = m_value.o->begin(); i != m_value.o->end(); ++i) {
         if (i != m_value.o->begin())
           a_buffer.append(",\"");
         else
@@ -851,7 +851,7 @@ class Value {
       break;
     case VT_ARRAY:
       a_buffer.append("[");
-      for (ArrayIter i = m_value.a->begin(); i != m_value.a->end(); i++) {
+      for (ArrayIter i = m_value.a->begin(); i != m_value.a->end(); ++i) {
         if (i != m_value.a->begin())
           a_buffer.append(",");
         i->toStringRecurse(a_buffer);
@@ -883,7 +883,7 @@ class Value {
 
     a_buffer.append("\"");
 
-    for (c = a_value.begin(); c != a_value.end(); c++) {
+    for (c = a_value.begin(); c != a_value.end(); ++c) {
       if (*c < 0x20) {
         a_buffer.append(a, c);
         a = c + 1;
diff --git a/common/proto/common/CMakeLists.txt b/common/proto/common/CMakeLists.txt
index e63f4e533..ac65a08de 100644
--- a/common/proto/common/CMakeLists.txt
+++ b/common/proto/common/CMakeLists.txt
@@ -11,7 +11,11 @@ protobuf_generate(
 )
 
 # make sure that datafed-protobuf is dependent on the cpp files when it compiles
-add_library( datafed-protobuf STATIC ${protobuf-generated-files} )
+if(BUILD_SHARED_LIBS)
+  add_library( datafed-protobuf SHARED ${protobuf-generated-files} )
+else()
+  add_library( datafed-protobuf STATIC ${protobuf-generated-files} )
+endif()
 set_target_properties(datafed-protobuf PROPERTIES POSITION_INDEPENDENT_CODE ON SOVERSION ${DATAFED_COMMON_PROTOCOL_API_MAJOR} VERSION ${DATAFED_COMMON_PROTOCOL_API_MAJOR}.${DATAFED_COMMON_PROTOCOL_API_MINOR}.${DATAFED_COMMON_PROTOCOL_API_PATCH} )
 target_link_libraries( datafed-protobuf protobuf::libprotobuf protobuf::libprotoc protobuf::libprotobuf-lite)
 target_include_directories( datafed-protobuf INTERFACE ${PROJECT_BINARY_DIR}/common/proto)
diff --git a/common/source/DynaLog.cpp b/common/source/DynaLog.cpp
index 255611070..f89ccee95 100644
--- a/common/source/DynaLog.cpp
+++ b/common/source/DynaLog.cpp
@@ -114,37 +114,43 @@ void Logger::addStream(std::ostream &stream) {
 
 void Logger::trace(std::string file, std::string func, int line_num,
                    const LogContext &context, const std::string &message) {
-  if (m_log_level >= LogLevel::TRACE) {
+  if (static_cast<unsigned int>(m_log_level) >=
+      static_cast<unsigned int>(LogLevel::TRACE)) {
     output(LogLevel::TRACE, file, func, line_num, context, message);
   }
 }
 void Logger::debug(std::string file, std::string func, int line_num,
                    const LogContext &context, const std::string &message) {
-  if (m_log_level >= LogLevel::DEBUG) {
+  if (static_cast<unsigned int>(m_log_level) >=
+      static_cast<unsigned int>(LogLevel::DEBUG)) {
     output(LogLevel::DEBUG, file, func, line_num, context, message);
   }
 }
 void Logger::info(std::string file, std::string func, int line_num,
                   const LogContext &context, const std::string &message) {
-  if (m_log_level >= LogLevel::INFO) {
+  if (static_cast<unsigned int>(m_log_level) >=
+      static_cast<unsigned int>(LogLevel::INFO)) {
     output(LogLevel::INFO, file, func, line_num, context, message);
   }
 }
 void Logger::warning(std::string file, std::string func, int line_num,
                      const LogContext &context, const std::string &message) {
-  if (m_log_level >= LogLevel::WARNING) {
+  if (static_cast<unsigned int>(m_log_level) >=
+      static_cast<unsigned int>(LogLevel::WARNING)) {
     output(LogLevel::WARNING, file, func, line_num, context, message);
   }
 }
 void Logger::error(std::string file, std::string func, int line_num,
                    const LogContext &context, const std::string &message) {
-  if (m_log_level >= LogLevel::ERROR) {
+  if (static_cast<unsigned int>(m_log_level) >=
+      static_cast<unsigned int>(LogLevel::ERROR)) {
     output(LogLevel::ERROR, file, func, line_num, context, message);
   }
 }
 void Logger::critical(std::string file, std::string func, int line_num,
                       const LogContext &context, const std::string &message) {
-  if (m_log_level >= LogLevel::CRITICAL) {
+  if (static_cast<unsigned int>(m_log_level) >=
+      static_cast<unsigned int>(LogLevel::CRITICAL)) {
     output(LogLevel::CRITICAL, file, func, line_num, context, message);
   }
 }
diff --git a/common/source/Util.cpp b/common/source/Util.cpp
index 0ed047fc3..1170e265d 100644
--- a/common/source/Util.cpp
+++ b/common/source/Util.cpp
@@ -79,11 +79,11 @@ void hexDump(const char *a_buffer, const char *a_buffer_end, ostream &a_out) {
   const unsigned char *e = (unsigned char *)a_buffer_end;
   bool done = false;
 
-  int l = 0, i = 0;
+  int l = 0;
   while (!done) {
     a_out << setw(4) << dec << l << ": ";
 
-    for (i = 0; i < 16; ++i) {
+    for (int i = 0; i < 16; ++i) {
       if (i == 8)
         a_out << "  ";
 
@@ -102,7 +102,7 @@ void hexDump(const char *a_buffer, const char *a_buffer_end, ostream &a_out) {
 
     a_out << "  ";
 
-    for (i = 0; i < 16; ++i) {
+    for (int i = 0; i < 16; ++i) {
       if (p + i != e) {
         if (isprint(*(p + i)))
           a_out << *(p + i);
diff --git a/common/source/communicators/ZeroMQCommunicator.hpp b/common/source/communicators/ZeroMQCommunicator.hpp
index 3dd063ae0..ac131d5b1 100644
--- a/common/source/communicators/ZeroMQCommunicator.hpp
+++ b/common/source/communicators/ZeroMQCommunicator.hpp
@@ -25,7 +25,6 @@ namespace SDMS {
 class ZeroMQCommunicator : public ICommunicator {
 protected:
   std::unique_ptr<ISocket> m_socket;
-  uint16_t m_zmq_context;
   void *m_zmq_socket = nullptr;
   int m_zmq_socket_type;
   void *m_zmq_ctx = nullptr;
@@ -43,7 +42,7 @@ class ZeroMQCommunicator : public ICommunicator {
 
 public:
   /** To be used by children*/
-  ZeroMQCommunicator(const LogContext &log_context)
+  explicit ZeroMQCommunicator(const LogContext &log_context)
       : m_log_context(log_context){};
 
   ZeroMQCommunicator(const SocketOptions &socket_options,
diff --git a/common/source/servers/Proxy.cpp b/common/source/servers/Proxy.cpp
index e3905d9fc..11894df0a 100644
--- a/common/source/servers/Proxy.cpp
+++ b/common/source/servers/Proxy.cpp
@@ -123,8 +123,16 @@ void Proxy::run() {
       // public
       if (resp_from_client_socket.error == false and
           resp_from_client_socket.time_out == false) {
-        m_communicators[SocketRole::SERVER]->send(
-            *resp_from_client_socket.message);
+        if (not resp_from_client_socket.message) {
+          DL_ERROR(
+              m_log_context,
+              "Proxy::run - Something is wrong, message "
+                  << "response is not defined but no timeouts or errors were "
+                  << "triggered, unable to send to server.");
+        } else {
+          m_communicators[SocketRole::SERVER]->send(
+              *resp_from_client_socket.message);
+        }
       }
 
       // If there are operations that need to happen on incoming messages,
@@ -137,11 +145,19 @@ void Proxy::run() {
       // ... - Serv Sock - Proxy ------ Client Sock - Serv Sock - Inter App
       if (resp_from_server_socket.error == false and
           resp_from_server_socket.time_out == false) {
-        for (auto &in_operator : m_incoming_operators) {
-          in_operator->execute(*resp_from_server_socket.message);
+        if (not resp_from_server_socket.message) {
+          DL_ERROR(
+              m_log_context,
+              "Proxy::run - Something is wrong, message "
+                  << "response is not defined but no timeouts or errors were "
+                  << "triggered, unable to operate and send to client.");
+        } else {
+          for (auto &in_operator : m_incoming_operators) {
+            in_operator->execute(*resp_from_server_socket.message);
+          }
+          m_communicators[SocketRole::CLIENT]->send(
+              *resp_from_server_socket.message);
         }
-        m_communicators[SocketRole::CLIENT]->send(
-            *resp_from_server_socket.message);
       }
 
     } catch (TraceException &e) {
diff --git a/common/source/servers/ProxyBasicZMQ.cpp b/common/source/servers/ProxyBasicZMQ.cpp
index 7b30bf34c..eda7f3bfe 100644
--- a/common/source/servers/ProxyBasicZMQ.cpp
+++ b/common/source/servers/ProxyBasicZMQ.cpp
@@ -19,8 +19,8 @@
 
 namespace SDMS {
 
-std::string sanitize(std::string val, const std::string pattern,
-                     const std::string replacement) {
+std::string sanitize(std::string val, const std::string &pattern,
+                     const std::string &replacement) {
   for (auto at = val.find(pattern, 0); at != std::string::npos;
        at = val.find(pattern, at + replacement.length())) {
 
@@ -186,7 +186,7 @@ void ProxyBasicZMQ::run() {
    * loop.
    **/
   auto terminate_call = [](std::chrono::duration<double> duration,
-                           const std::string address, int thread_id,
+                           const std::string &address, int thread_id,
                            LogContext log_context) {
     log_context.thread_name += "-terminate_after_timeout";
     log_context.thread_id = thread_id;
diff --git a/compose/README.md b/compose/README.md
index ecbd66109..0a291bd10 100644
--- a/compose/README.md
+++ b/compose/README.md
@@ -1,35 +1,222 @@
 # Compose Dev environment
 
-The files in this folder are incomplete but are the start for setting up a full
-docker compose instance of datafed.
+The Compose Dev environment is split into two different Compose files. The
+"core metadata services" which comprise the web server, core server and database
+and the "repo services" which comprise Globus Connect Server running with the
+authz library and the DataFed repo service.
 
-```bash
-./build_images_for_compose.sh
-```
+NOTE Standing up the repo services has been separated because of Globus. You
+will need a machine with firewall exceptions to use it.
+
+## Core Compose Services
+
+The following steps are used to stand up the core Compose file from scratch.
+Some of steps you only have to do once.
+
+1. Generating the env variables.
+2. Opening the .env file and entering your configuration
+3. Building the images
+4. Running the Compose file
+5. Bringing down the Compose file. 
+
+Core services only need an external port 443 for https access. I have been 
+unable to get this to work with other ports due to the redirect URL required
+by Globus for authentication. It does not seem to support ports outside of 443.
+
+### 1. Generating .env configuration varaibles for Core Services
 
 Create the .env file fill in the missing components that are required.
 
 ```bash
 ./generate_env.sh
 ```
+### 2. Fill in the needed .env variables for the Core Services
+
+The .env file will be created in the DataFed/Compose folder and will be hidden.
+The .env file variables can be changed at this point to your configuration.
+
+NOTE the .env file will be read verbatim by Compose including any spaces or
+"#" comments so do not includ anything but the exact text that needs to be
+included in the variables.
+
+### 3. Building Core Services 
+
+The following command will build all the images to run the core metadata 
+services.
+
+```bash
+./build_images_for_compose.sh
+```
+
+### 4. Running the core Compose file
 
 Stand up the core services.
 
 ```bash
+source ./unset_env.sh
 docker compose -f ./compose_core.yml up
 ```
 
+NOTE The unset_env.sh script is to make sure you are not accidentially
+overwriting what is in the .env with your local shell env. You can check the
+configuration before hand by running.
+
+```bash
+docker compose -f compose_core.yml config
+```
+
+WARNING - Docker Compose will prioritize env variables in the following priority
+1. From you shell env
+2. From the .env file
+3. Internally from winthin the image
+Be sure that you are not accidentally overwriting .env variables.
+
 At this point you should be able to navigate in your browser to
 <https://localhost>
 
 NOTE we are using a self signed certificate so you will have to force your
 browser to allow you to see the page.
 
-Standing up the repo services has been separated because of Globus. You will
-need a machine with firewall exceptions to use it.
+### 5. Bringing down the core Compose file
+
+To completely remove the Compose instance and all state the following should
+be run.
+
+```bash
+docker compose -f ./compose_core.yml down --volumes
+```
+
+NOTE the volumes will remove all cached state. If the '--volumes' flag is
+not added then on a subsequent "Compose up" the database will not be a clean
+install but contain state from previous runs.
+
+## Repo Compose Services
+
+The following steps are used to stand up the repo Compose file. NOTE, that
+because of how Globus is configured, there is an additional configuration 
+and teardown step.
+
+1. Generating the env variables.
+2. Opening the .env file and entering your configuration
+3. Running the generate_globus_files.sh script
+4. Building the images
+5. Running the Compose file
+6. Bringing down the Compose file.
+7. Running the cleanup_globus_files.sh if you want to remove the deployment key
+   and start completely from scratch.
+
+### 1. Generating .env configuration varaibles for the Repo Services
+
+Create the .env file fill in the missing components that are required.
+
+```bash
+./generate_env.sh
+```
+
+### 2. Enter the .env varaibles for the Repo Services
+
+The .env file will be created in the DataFed/Compose folder and will be hidden.
+The .env file variables can be changed at this point to your configuration.
+
+NOTE the .env file will be read verbatim by Compose including any spaces or
+"#" comments so do not include anything but the exact text that needs to be
+included in the variables.
+
+### 3. Globus configuration
+
+This step is only required once, after which the necessary files should exist
+in DataFed/Compose/globus. These files will contain the Globus configuration 
+needed for additional cycles of "docker compose up" and "docker compose down".
+
+### 4. Building Repo Services 
+
+The following command will build all the images to run the core metadata 
+services.
+
+```bash
+./build_repo_images_for_compose.sh
+```
+
+### 5. Running the Repo Compose file
+
+Stand up the repo services.
+
+```bash
+source ./unset_env.sh
+docker compose -f ./compose_repo.yml up
+```
+
+Be aware, the 'source' is to apply changes to the environment of your current
+terminal session.
+
+NOTE The unset_env.sh script is to make sure you are not accidentially
+overwriting what is in the .env with your local shell env. You can check the
+configuration before hand by running. 
+
+```bash
+docker compose -f compose_repo.yml config
+```
+
+WARNING - Docker Compose will prioritize env variables in the following priority
+1. From you shell env
+2. From the .env file
+3. Internally from winthin the image
+Be sure that you are not accidentally overwriting .env variables.
+
+### 6. Bringing down the Compose file 
 
 ## Cleaning up
 
 ```bash
 docker compose -f ./compose_core.yml down
 ```
+
+## Running isolated containers
+
+If you just want to run a single container at a time with the same configuration
+this can also be doen using commands like the following.
+
+### DataFed Web 
+
+```bash
+source ./unset_env.sh
+docker run --env-file .env \
+  -e UID=$(id -u) \
+  -p 443:443 \
+  -p 7513:7513 \
+  -t datafed-web:latest
+```
+
+### DataFed GCS
+
+```bash
+docker run --env-file .env \
+  --network=host \
+  -v /home/cloud/compose_collection:/mnt/datafed \
+  -v ./globus:/opt/datafed/globus \
+  -v ./logs:/datafed/logs \
+  -v ./keys:/opt/datafed/keys \
+  -t datafed-gcs:latest
+```
+
+To interact more directly with the container the '-i' flag can be added and the
+entrypoint file can be overwritten by including '--entrypoint /bin/bash'
+
+## Common Errors
+
+### Errors during Compose up
+
+Make sure all the ports that are needed are open on the local host. These
+include, ports
+
+443 for the datafed-web and datafed-gcs container
+7512 for the datafed-core container
+50000-51000 for the datafed-gcs container
+9000 for the datafed-repo container
+80 for the datafed-gcs container
+8512 for arangodb web server interface
+
+Make sure port 80 is not already bound on the host. Also note that the repo
+server keys should exist in the keys folder before running the gcs instance.
+
+
diff --git a/compose/build_images_for_compose.sh b/compose/build_images_for_compose.sh
index 2ee0ee0e8..7b155465f 100755
--- a/compose/build_images_for_compose.sh
+++ b/compose/build_images_for_compose.sh
@@ -26,16 +26,10 @@ docker build -f \
   --build-arg RUNTIME="datafed-runtime" \
   "${PROJECT_ROOT}" \
   -t datafed-web:latest
-#docker build -f \
-#  "${PROJECT_ROOT}/repository/docker/Dockerfile" \
-#  --build-arg DEPENDENCIES="datafed-dependencies" \
-#  --build-arg RUNTIME="datafed-runtime" \
-#  "${PROJECT_ROOT}" \
-#  -t datafed-repo:latest
-#docker build -f \
-#  "${PROJECT_ROOT}/docker/Dockerfile.foxx" \
-#  --build-arg DEPENDENCIES="datafed-dependencies" \
-#  --build-arg RUNTIME="datafed-runtime" \
-#  "${PROJECT_ROOT}" \
-#  -t datafed-foxx:latest
-#
+docker build -f \
+  "${PROJECT_ROOT}/docker/Dockerfile.foxx" \
+  --build-arg DEPENDENCIES="datafed-dependencies" \
+  --build-arg RUNTIME="datafed-runtime" \
+  "${PROJECT_ROOT}" \
+  -t datafed-foxx:latest
+
diff --git a/compose/build_repo_images_for_compose.sh b/compose/build_repo_images_for_compose.sh
new file mode 100755
index 000000000..3ab2994ce
--- /dev/null
+++ b/compose/build_repo_images_for_compose.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+set -euf -o pipefail
+
+SCRIPT=$(realpath "$0")
+SOURCE=$(dirname "$SCRIPT")
+PROJECT_ROOT=$(realpath "${SOURCE}/../")
+. "${PROJECT_ROOT}/scripts/dependency_versions.sh"
+
+cd "${PROJECT_ROOT}/external/globus-connect-server-deploy/docker"
+git checkout "$DATAFED_GCS_SUBMODULE_VERSION"
+docker build --progress plain --tag "gcs-ubuntu-base:latest" - < "./docker-files/Dockerfile.ubuntu-20.04"
+cd "${PROJECT_ROOT}"
+docker build \
+  -f "${PROJECT_ROOT}/docker/Dockerfile.dependencies" \
+  "${PROJECT_ROOT}" \
+  -t datafed-dependencies:latest
+docker build \
+  -f "${PROJECT_ROOT}/docker/Dockerfile.runtime" \
+  "${PROJECT_ROOT}" \
+  -t datafed-runtime:latest
+docker build -f \
+  "${PROJECT_ROOT}/repository/docker/Dockerfile" \
+  --build-arg DEPENDENCIES="datafed-dependencies" \
+  --build-arg RUNTIME="datafed-runtime" \
+  "${PROJECT_ROOT}" \
+  -t datafed-repo:latest
+docker build -f \
+  "${PROJECT_ROOT}/repository/docker/Dockerfile.gcs" \
+  --build-arg DEPENDENCIES="datafed-dependencies" \
+  --build-arg RUNTIME="datafed-runtime" \
+  --build-arg GCS_IMAGE="gcs-ubuntu-base" \
+  "${PROJECT_ROOT}" \
+  -t datafed-gcs:latest
diff --git a/compose/cleanup_globus_files.sh b/compose/cleanup_globus_files.sh
new file mode 100755
index 000000000..f262cef05
--- /dev/null
+++ b/compose/cleanup_globus_files.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+SCRIPT=$(realpath "$0")
+SOURCE=$(dirname "$SCRIPT")
+PROJECT_ROOT=$(realpath "${SOURCE}/..")
+
+# This script should be run after generating the .env file as it will pull
+# values from the .env file
+
+if [ ! -f "${PROJECT_ROOT}/compose/.env" ]
+then
+  echo "Missing . ${PROJECT_ROOT}/compose/.env file. This file needs to be"
+  echo "created first"
+  exit 1
+fi
+
+. "${PROJECT_ROOT}/compose/.env"
+
+export DATAFED_GLOBUS_DEPLOYMENT_KEY_PATH="$DATAFED_HOST_DEPLOYMENT_KEY_PATH"
+export DATAFED_GLOBUS_CRED_FILE_PATH="$DATAFED_HOST_CRED_FILE_PATH"
+
+if [ -f "$DATAFED_HOST_CRED_FILE_PATH" ]
+then
+  export GCS_CLI_CLIENT_ID=$(jq -r .client < "${DATAFED_HOST_CRED_FILE_PATH}")
+  export GCS_CLI_CLIENT_SECRET=$(jq -r .secret < "${DATAFED_HOST_CRED_FILE_PATH}")
+fi
+
+if [ -f "$DATAFED_GLOBUS_DEPLOYMENT_KEY_PATH" ]
+then
+  export GCS_CLI_ENDPOINT_ID=$(jq -r .client_id < "${DATAFED_GLOBUS_DEPLOYMENT_KEY_PATH}")
+fi
+
+sudo globus-connect-server node cleanup
+
+DATAFED_GCS_ROOT_NAME="$DATAFED_GCS_ROOT_NAME" \
+python3 "${PROJECT_ROOT}/scripts/globus/globus_cleanup.py"
diff --git a/compose/compose_core.yml b/compose/compose_core.yml
index 18d9b6c62..c7bb2e1da 100644
--- a/compose/compose_core.yml
+++ b/compose/compose_core.yml
@@ -14,6 +14,7 @@ services:
       DATAFED_HTTPS_SERVER_PORT: "${DATAFED_HTTPS_SERVER_PORT}"
       DATAFED_WEB_CERT_PATH: "${DATAFED_WEB_CERT_PATH}"
       DATAFED_WEB_KEY_PATH: "${DATAFED_WEB_KEY_PATH}"
+      DATAFED_WEB_USER: "datafed"
       DATAFED_DEFAULT_LOG_PATH: "${DATAFED_CONTAINER_LOG_PATH}"
       DATAFED_CORE_ADDRESS_PORT_INTERNAL: "datafed-core:7513"
       UID: "${DATAFED_UID}"
@@ -22,6 +23,7 @@ services:
       - 443:443 # This must be the same port that is mapped to the host for redirects to work
     volumes:
       - ./keys:/opt/datafed/keys
+      - ./logs:${DATAFED_CONTAINER_LOG_PATH}
     networks:
       - datafed-internal
 
@@ -45,11 +47,11 @@ services:
       DATAFED_DATABASE_IP_ADDRESS_PORT: "${DATAFED_DATABASE_IP_ADDRESS}:${DATAFED_DATABASE_PORT}"
     ports:
       - 7513 # Communication web server
-      - 7512 # Secure core server communication
+      - 7512:7512 # Secure core server communication must be exposed outside of the container
     volumes:
       - ./keys:/opt/datafed/keys
+      - ./logs:${DATAFED_CONTAINER_LOG_PATH}
     networks:
-      - datafed-core-secure-api
       - datafed-internal
 
   datafed-foxx:
@@ -67,7 +69,9 @@ services:
       test: ["CMD", "/bin/bash", "-c", "[ -f /tmp/.foxx_is_installed ]"]
       interval: 10s
       timeout: 5s
-      retries: 10
+      retries: 20
+    volumes:
+      - foxx_tmp:/tmp 
     networks:
       - datafed-internal
 
@@ -75,17 +79,24 @@ services:
     image: arangodb
     environment:
       ARANGO_ROOT_PASSWORD: "${DATAFED_DATABASE_PASSWORD}"
+    volumes:
+      - arango_db:/var/lib/arangodb3
     ports:
       - 8529:8529 # Arangodb web UI
     networks:
       - datafed-internal
 
+# Fox tmp is used so that if docker compose up is run a second time it won't
+# need to overwrite the database it will simply use the existing one hence
+# skipping some of the setup steps. The foxx entrypoint script will create
+# a file in tmp to indicate that this is the case.
+# 
+# arango_db is used to cache the state of the arangodb database
 volumes:
-  keys:
+  foxx_tmp:
+  arango_db:
 
 networks:
-  datafed-core-secure-api:
-    driver: bridge
   datafed-internal:
     driver: bridge
   
diff --git a/compose/compose_repo.yml b/compose/compose_repo.yml
new file mode 100644
index 000000000..3527eefca
--- /dev/null
+++ b/compose/compose_repo.yml
@@ -0,0 +1,54 @@
+
+# WARNING
+#
+# Any env variable that must be provided and overwrite what is in the container
+# Must be explicitly listed in the environment section of the specific service
+# --env file variables will not be default exist in the container.
+services:
+
+  datafed-repo:
+    environment:
+      DATAFED_ZEROMQ_SESSION_SECRET: "${DATAFED_ZEROMQ_SESSION_SECRET}"
+      DATAFED_ZEROMQ_SYSTEM_SECRET: "${DATAFED_ZEROMQ_SYSTEM_SECRET}"
+      DATAFED_DOMAIN: "${DATAFED_DOMAIN}"
+      DATAFED_HTTPS_SERVER_PORT: "${DATAFED_HTTPS_SERVER_PORT}"
+      DATAFED_DEFAULT_LOG_PATH: "${DATAFED_CONTAINER_LOG_PATH}"
+      DATAFED_CORE_ADDRESS_PORT_INTERNAL: "${DATAFED_DOMAIN}:7513"
+      DATAFED_GCS_COLLECTION_ROOT_PATH: "${DATAFED_GCS_COLLECTION_ROOT_PATH}"
+      UID: "${DATAFED_UID}"
+      HOST_HOSTNAME: "localhost"
+    image: datafed-repo:latest
+    volumes:
+      - ./keys:/opt/datafed/keys
+      - ./logs:${DATAFED_CONTAINER_LOG_PATH}
+      - ${DATAFED_HOST_COLLECTION_MOUNT}:${DATAFED_GCS_COLLECTION_ROOT_PATH}
+    ports:
+      - 9000:9000 # Communication core server
+
+# Needs host port 80 for apache
+# Needs ports 50000 - 51000 for GridFTP
+# Needs port 443 for control port
+  datafed-gcs:
+    environment:
+      DATAFED_ZEROMQ_SESSION_SECRET: "${DATAFED_ZEROMQ_SESSION_SECRET}"
+      DATAFED_ZEROMQ_SYSTEM_SECRET: "${DATAFED_ZEROMQ_SYSTEM_SECRET}"
+      DATAFED_DOMAIN: "${DATAFED_DOMAIN}"
+      DATAFED_HTTPS_SERVER_PORT: "${DATAFED_HTTPS_SERVER_PORT}"
+      DATAFED_DEFAULT_LOG_PATH: "${DATAFED_CONTAINER_LOG_PATH}"
+      DATAFED_CORE_ADDRESS_PORT_INTERNAL: "datafed-core:7513"
+      DATAFED_GCS_ROOT_NAME: "${DATAFED_GCS_ROOT_NAME}"
+      DATAFED_REPO_ID_AND_DIR: "${DATAFED_REPO_ID_AND_DIR}"
+      DATAFED_GLOBUS_SUBSCRIPTION: "${DATAFED_GLOBUS_SUBSCRIPTION}"
+      DATAFED_GLOBUS_CONTROL_PORT: "${DATAFED_GLOBUS_CONTROL_PORT}"
+      DATAFED_GCS_COLLECTION_ROOT_PATH: "${DATAFED_GCS_COLLECTION_ROOT_PATH}"
+      DATAFED_REPO_USER: "${DATAFED_REPO_USER}"
+      UID: "${DATAFED_UID}"
+      HOST_HOSTNAME: "localhost"
+      DATAFED_AUTHZ_USER: "datafed"
+    network_mode: host
+    image: datafed-gcs:latest
+    volumes:
+      - ./keys:/opt/datafed/keys
+      - ./globus:/opt/datafed/globus
+      - ./logs:${DATAFED_CONTAINER_LOG_PATH}
+      - ${DATAFED_HOST_COLLECTION_MOUNT}:${DATAFED_GCS_COLLECTION_ROOT_PATH}
diff --git a/compose/generate_env.sh b/compose/generate_env.sh
index b09f76da1..7e608b6d0 100755
--- a/compose/generate_env.sh
+++ b/compose/generate_env.sh
@@ -122,20 +122,85 @@ else
   local_DATAFED_COMPOSE_DATABASE_PORT=$(printenv DATAFED_COMPOSE_DATABASE_PORT)
 fi
 
+if [ -z "${DATAFED_COMPOSE_HOST_COLLECTION_MOUNT}" ]
+then
+  local_DATAFED_HOST_COLLECTION_MOUNT="$HOME/compose_collection"
+else
+  local_DATAFED_HOST_COLLECTION_MOUNT=$(printenv DATAFED_COMPOSE_HOST_COLLECTION_MOUNT)
+fi
+
+if [ -z "${DATAFED_COMPOSE_HOST_DEPLOYMENT_KEY_PATH}" ]
+then
+  local_DATAFED_COMPOSE_HOST_DEPLOYMENT_KEY_PATH="${local_DATAFED_GLOBUS_KEY_DIR}/deployment-key.json"
+else
+  local_DATAFED_COMPOSE_HOST_DEPLOYMENT_KEY_PATH=$(printenv DATAFED_COMPOSE_HOST_DEPLOYMENT_KEY_PATH)
+fi
+
+if [ -z "${DATAFED_COMPOSE_HOST_CRED_FILE_PATH}" ]
+then
+  local_DATAFED_HOST_CRED_FILE_PATH="${local_DATAFED_GLOBUS_KEY_DIR}/client_cred.json"
+else
+  local_DATAFED_HOST_CRED_FILE_PATH=$(printenv DATAFED_COMPOSE_HOST_CRED_FILE_PATH)
+fi
+
+if [ -z "${DATAFED_GLOBUS_CONTROL_PORT}" ]
+then
+  # For compose will set by default to run on a port other than 443 because 
+  # the core metadata services use 443 for the web server 7510
+  local_DATAFED_GLOBUS_CONTROL_PORT="443"
+else
+  local_DATAFED_GLOBUS_CONTROL_PORT=$(printenv DATAFED_GLOBUS_CONTROL_PORT)
+fi
+
+if [ -z "${DATAFED_GLOBUS_SUBSCRIPTION}" ]
+then
+  # For compose will set by default to run on a port other than 443 because 
+  # the core metadata services use 443 for the web server
+  local_DATAFED_GLOBUS_SUBSCRIPTION=""
+else
+  local_DATAFED_GLOBUS_SUBSCRIPTION=$(printenv DATAFED_GLOBUS_SUBSCRIPTION)
+fi
+
+
+# Make the logs folder if it doesn't exist
+mkdir -p "${PROJECT_ROOT}/compose/logs"
+
+# Do not put " around anything and do not add comments in the .env file
 cat << EOF > ".env"
-DATAFED_USER89_PASSWORD="${local_DATAFED_COMPOSE_USER89_PASSWORD}" # For End to end testing
-DATAFED_REPO_FORM_PATH="${local_DATAFED_COMPOSE_REPO_FORM_PATH}" # Where the repo form is located also needed for testing
-DATAFED_GLOBUS_APP_SECRET="${local_DATAFED_COMPOSE_GLOBUS_APP_SECRET}"
-DATAFED_GLOBUS_APP_ID="${local_DATAFED_COMPOSE_GLOBUS_APP_ID}"
-DATAFED_ZEROMQ_SESSION_SECRET="${local_DATAFED_COMPOSE_ZEROMQ_SESSION_SECRET}"
-DATAFED_ZEROMQ_SYSTEM_SECRET="${local_DATAFED_COMPOSE_ZEROMQ_SYSTEM_SECRET}"
-DATAFED_DOMAIN="${local_DATAFED_COMPOSE_DOMAIN}"
-DATAFED_HTTPS_SERVER_PORT="${local_DATAFED_COMPOSE_HTTPS_SERVER_PORT}"
-DATAFED_WEB_CERT_PATH="/opt/datafed/keys/${local_DATAFED_WEB_CERT_NAME}"
-DATAFED_WEB_KEY_PATH="/opt/datafed/keys/${local_DATAFED_WEB_KEY_NAME}"
-DATAFED_CONTAINER_LOG_PATH="${local_DATAFED_COMPOSE_CONTAINER_LOG_PATH}"
-DATAFED_DATABASE_PASSWORD="${local_DATAFED_COMPOSE_DATABASE_PASSWORD}"
-DATAFED_DATABASE_IP_ADDRESS="${local_DATAFED_COMPOSE_DATABASE_IP_ADDRESS}"
-DATAFED_DATABASE_PORT="${local_DATAFED_COMPOSE_DATABASE_PORT}"
-DATAFED_UID="$(id -u)"
+DATAFED_REPO_USER=datafed
+DATAFED_USER89_PASSWORD=${local_DATAFED_COMPOSE_USER89_PASSWORD}
+DATAFED_REPO_FORM_PATH=${local_DATAFED_COMPOSE_REPO_FORM_PATH}
+DATAFED_GLOBUS_APP_SECRET=${local_DATAFED_COMPOSE_GLOBUS_APP_SECRET}
+DATAFED_GLOBUS_APP_ID=${local_DATAFED_COMPOSE_GLOBUS_APP_ID}
+DATAFED_ZEROMQ_SESSION_SECRET=${local_DATAFED_COMPOSE_ZEROMQ_SESSION_SECRET}
+DATAFED_ZEROMQ_SYSTEM_SECRET=${local_DATAFED_COMPOSE_ZEROMQ_SYSTEM_SECRET}
+DATAFED_DOMAIN=${local_DATAFED_COMPOSE_DOMAIN}
+DATAFED_HTTPS_SERVER_PORT=${local_DATAFED_COMPOSE_HTTPS_SERVER_PORT}
+DATAFED_WEB_CERT_PATH=/opt/datafed/keys/${local_DATAFED_WEB_CERT_NAME}
+DATAFED_WEB_KEY_PATH=/opt/datafed/keys/${local_DATAFED_WEB_KEY_NAME}
+DATAFED_CONTAINER_LOG_PATH=${local_DATAFED_COMPOSE_CONTAINER_LOG_PATH}
+DATAFED_DATABASE_PASSWORD=${local_DATAFED_COMPOSE_DATABASE_PASSWORD}
+DATAFED_DATABASE_IP_ADDRESS=${local_DATAFED_COMPOSE_DATABASE_IP_ADDRESS}
+DATAFED_DATABASE_PORT=${local_DATAFED_COMPOSE_DATABASE_PORT}
+DATAFED_GCS_ROOT_NAME=DataFed_Compose
+DATAFED_REPO_ID_AND_DIR=compose-home
+DATAFED_UID=$(id -u)
+DATAFED_HOST_COLLECTION_MOUNT=${local_DATAFED_HOST_COLLECTION_MOUNT}
+DATAFED_HOST_DEPLOYMENT_KEY_PATH=${local_DATAFED_COMPOSE_HOST_DEPLOYMENT_KEY_PATH}
+DATAFED_HOST_CRED_FILE_PATH=${local_DATAFED_HOST_CRED_FILE_PATH}
+DATAFED_GLOBUS_CONTROL_PORT=${local_DATAFED_GLOBUS_CONTROL_PORT}
+DATAFED_GLOBUS_SUBSCRIPTION=${local_DATAFED_GLOBUS_SUBSCRIPTION}
 EOF
+
+unset_env_file_name="unset_env.sh"
+echo "#!/bin/bash" > "${unset_env_file_name}"
+echo "# Was auto generated by $SCRIPT" >> "${unset_env_file_name}"
+while IFS='=' read -r key value; do
+    # Check if the line contains the '=' sign
+    if [ -n "$value" ]; then
+        # Print the content before the '=' sign
+        echo "unset $key" >> "${unset_env_file_name}"
+    fi
+done < ".env"
+
+chmod +x "$unset_env_file_name"
diff --git a/compose/generate_globus_files.sh b/compose/generate_globus_files.sh
new file mode 100755
index 000000000..82f5dba89
--- /dev/null
+++ b/compose/generate_globus_files.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+SCRIPT=$(realpath "$0")
+SOURCE=$(dirname "$SCRIPT")
+PROJECT_ROOT=$(realpath "${SOURCE}/..")
+
+# This script should be run after generating the .env file as it will pull
+# values from the .env file
+
+if [ ! -f "${PROJECT_ROOT}/compose/.env" ]
+then
+  echo "Missing . ${PROJECT_ROOT}/compose/.env file. This file needs to be"
+  echo "created first"
+  exit 1
+fi
+
+local_DATAFED_GLOBUS_KEY_DIR="${PROJECT_ROOT}/compose/globus"
+if [ ! -d "$local_DATAFED_GLOBUS_KEY_DIR" ]
+then
+  mkdir -p "$local_DATAFED_GLOBUS_KEY_DIR"
+fi
+
+. "${PROJECT_ROOT}/compose/.env"
+
+DATAFED_GLOBUS_DEPLOYMENT_KEY_PATH="$DATAFED_HOST_DEPLOYMENT_KEY_PATH" \
+DATAFED_GLOBUS_CRED_FILE_PATH="$DATAFED_HOST_CRED_FILE_PATH" \
+DATAFED_GLOBUS_CONTROL_PORT="$DATAFED_GLOBUS_CONTROL_PORT" \
+DATAFED_GLOBUS_SUBSCRIPTION="$DATAFED_GLOBUS_SUBSCRIPTION" \
+DATAFED_GCS_ROOT_NAME="$DATAFED_GCS_ROOT_NAME" \
+   python3 "${PROJECT_ROOT}/scripts/globus/initialize_globus_endpoint.py"
diff --git a/core/database/backup/datafed-backup.sh b/core/database/backup/datafed-backup.sh
index 50e7e04f3..2d8a3276d 100755
--- a/core/database/backup/datafed-backup.sh
+++ b/core/database/backup/datafed-backup.sh
@@ -12,10 +12,10 @@ systemctl stop arangodb3.service
 backup_file=DataFed_DB_Backup_$(date +"%Y_%m_%d").tar.gz
 
 # Tar contents of arangodb directory without full path
-tar -C /var/lib/arangodb3 -cvzf ${backup_file} .
+tar -C /var/lib/arangodb3 -cvzf "${backup_file}" .
 
 # Move backup file to storage location
-mv ${backup_file} /data/backups
+mv "${backup_file}" /data/backups
 
 # Restart DataFed services
 systemctl start arangodb3.service
diff --git a/core/database/foxx/api/authz_router.js b/core/database/foxx/api/authz_router.js
index c8771a003..a74f4f7fb 100644
--- a/core/database/foxx/api/authz_router.js
+++ b/core/database/foxx/api/authz_router.js
@@ -15,23 +15,16 @@ router.get('/gridftp', function(req, res) {
 
             const client = g_lib.getUserFromClientID_noexcept(req.queryParams.client);
 
-            console.log("1");
             var idx = req.queryParams.file.lastIndexOf("/");
-            console.log("2");
             var data_key = req.queryParams.file.substr(idx + 1);
-            console.log("3");
             var data_id = "d/" + data_key;
 
-            console.log("4");
-            console.log("client is ", client);
             // Special case - allow unknown client to read a publicly accessible record
-            console.log(req.queryParams);
             if (!client) {
                 if (req.queryParams.act != "read" || !g_lib.hasPublicRead(data_id)) {
                     console.log("Permission to read denied!");
                     throw g_lib.ERR_PERM_DENIED;
                 }
-                console.log("5");
                 console.log("allow anon read of public record");
             } else {
                 console.log("client:", client);
@@ -62,13 +55,10 @@ router.get('/gridftp', function(req, res) {
                         throw [g_lib.ERR_INVALID_PARAM, "Invalid gridFTP action: ", req.queryParams.act];
                 }
 
-                console.log("6 client: ", client, " data_id: ", data_id);
+                console.log("client: ", client, " data_id: ", data_id);
                 if (!g_lib.hasAdminPermObject(client, data_id)) {
-                    console.log("7");
                     var data = g_db.d.document(data_id);
-                    console.log("8");
                     if (!g_lib.hasPermissions(client, data, req_perm)) {
-                        console.log("9");
                         console.log("Client: ", client, " does not have permission!");
                         throw g_lib.ERR_PERM_DENIED;
                     }
@@ -78,9 +68,7 @@ router.get('/gridftp', function(req, res) {
             // Verify repo and path are correct for record
             // Note: only managed records have an allocations and this gridftp auth call is only made for managed records
             //var path = req.queryParams.file.substr( req.queryParams.file.indexOf("/",8));
-            console.log("10");
             var path = req.queryParams.file;
-            console.log("11");
             console.log("data_id is, ", data_id);
             var loc = g_db.loc.firstExample({
                 _from: data_id
@@ -91,7 +79,6 @@ router.get('/gridftp', function(req, res) {
                 console.log("Permission denied data is not managed by DataFed. This can happen if you try to do a transfer directly from Globus.")
                 throw g_lib.ERR_PERM_DENIED;
             }
-            console.log("12");
             var alloc = g_db.alloc.firstExample({
                 _from: loc.uid,
                 _to: loc._to
@@ -107,22 +94,25 @@ router.get('/gridftp', function(req, res) {
             }
 
             console.log("path:", path, " alloc path:", alloc.path + data_key, " loc: ", loc);
-            console.log("13");
             if (alloc.path + data_key != path) {
                 // This may be due to an alloc/owner change
-                // Allow IF new path matches
+                // Allow If new path matches
                 console.log("authz loc info:", loc);
 
                 if (!loc.new_repo) {
+                    console.log("Throw a permission denied error");
                     throw g_lib.ERR_PERM_DENIED;
                 }
 
+                console.log("Creating alloc");
                 alloc = g_db.alloc.firstExample({
                     _from: loc.new_owner ? loc.new_owner : loc.uid,
                     _to: loc.new_repo
                 });
 
 
+                console.log("alloc is ");
+                console.log(alloc);
                 if (!alloc || (alloc.path + data_key != path)) {
                     throw g_lib.ERR_PERM_DENIED;
                 }
diff --git a/core/database/foxx/api/user_router.js b/core/database/foxx/api/user_router.js
index a0e53344a..111e379a4 100644
--- a/core/database/foxx/api/user_router.js
+++ b/core/database/foxx/api/user_router.js
@@ -74,7 +74,7 @@ router.get('/create', function(req, res) {
                         throw [g_lib.ERR_AUTHN_FAILED, "Invalid system credentials"];
                     }
 
-                    var i, j, c,
+                    var i,
                         time = Math.floor(Date.now() / 1000),
                         name = req.queryParams.name.trim(),
                         idx = name.lastIndexOf(" ");
@@ -85,12 +85,20 @@ router.get('/create', function(req, res) {
                     var lname = name.substr(idx + 1),
                         fname = name.substr(0, idx).trim();
 
+                    var is_admin = req.queryParams.is_admin;
+                    // It is assumed that if this is the first user to ever log
+                    // into the database they are by default made the admin.
+                    // This will simplify the setup process.
+                    if ( g_db.u.count() === 0 ) {
+                      is_admin = true;
+                    }
+
                     var user_data = {
                         _key: req.queryParams.uid,
                         name: name.toLowerCase() + " " + req.queryParams.uid,
                         name_first: fname,
                         name_last: lname,
-                        is_admin: req.queryParams.is_admin,
+                        is_admin: is_admin,
                         max_coll: g_lib.DEF_MAX_COLL,
                         max_proj: g_lib.DEF_MAX_PROJ,
                         max_sav_qry: g_lib.DEF_MAX_SAV_QRY,
diff --git a/core/docker/Dockerfile b/core/docker/Dockerfile
index bfd0ea97c..bab738f2a 100644
--- a/core/docker/Dockerfile
+++ b/core/docker/Dockerfile
@@ -64,16 +64,7 @@ ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
 ENV          DATAFED_DIR="$DATAFED_DIR"
 ENV            BUILD_DIR="$BUILD_DIR"
 ENV              LIB_DIR="$LIB_DIR"
-
-# copy necessary shared libraries
-#COPY --from=core-build /libraries/libprotobuf.a           /libraries/libprotobuf.a
-#COPY --from=core-build /libraries/libzmq.a                /libraries/libzmq.a
-#COPY --from=core-build /libraries/libsodium.a             /libraries/libsodium.a
-COPY --from=core-build /libraries/libboost_program_options.so /libraries/libboost_program_options.so
-#RUN ${BUILD_DIR}/scripts/copy_dependency.sh protobuf to
-#RUN ${BUILD_DIR}/scripts/copy_dependency.sh libzmq to
-#RUN ${BUILD_DIR}/scripts/copy_dependency.sh libsodium to
-RUN ${BUILD_DIR}/scripts/copy_dependency.sh boost_program_options to
+ENV          DATAFED_DEFAULT_LOG_PATH="$DATAFED_INSTALL_PATH/logs"
 
 RUN ldconfig
 
@@ -91,7 +82,6 @@ USER root
 RUN chown -R datafed:root /datafed
 RUN chown -R datafed:root /opt
 
-#SHELL ["/bin/bash"]
 # NOTE docker does not recognize curly braces around variables in entrypoint
 # and CMD
 ENTRYPOINT [ "/datafed/source/core/docker/entrypoint.sh", "/opt/datafed/core/datafed-core","--cfg", "/opt/datafed/core/datafed-core.cfg"]
diff --git a/core/docker/Dockerfile.core-base.ubuntu b/core/docker/Dockerfile.core-base.ubuntu
index 393fad82b..ae790e3b7 100644
--- a/core/docker/Dockerfile.core-base.ubuntu
+++ b/core/docker/Dockerfile.core-base.ubuntu
@@ -5,7 +5,11 @@ ARG BUILD_DIR="/datafed/source"
 ARG DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
 
 ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
-ENV PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:${PATH}"
+
+# NOTE - it does not look like using $PATH variable inside this definition
+# resolves correctly as such they system paths are included explicitly when
+# changes are made to ENV PATH
+ENV PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
 RUN mkdir -p ${BUILD_DIR}
 
 WORKDIR ${BUILD_DIR}
diff --git a/core/docker/Dockerfile.core.ubuntu b/core/docker/Dockerfile.core.ubuntu
index 8f8d6ae98..5edaccf7b 100644
--- a/core/docker/Dockerfile.core.ubuntu
+++ b/core/docker/Dockerfile.core.ubuntu
@@ -10,10 +10,12 @@ ARG DATAFED_INSTALL_PATH="/datafed/install"
 ENV DATAFED_DIR="$DATAFED_DIR"
 ENV BUILD_DIR="$BUILD_DIR"
 ENV DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH"
+ENV DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
 
 RUN mkdir -p ${BUILD_DIR}/logs
 RUN mkdir -p ${DATAFED_INSTALL_PATH}
 RUN mkdir -p ${DATAFED_INSTALL_PATH}/keys
+RUN mkdir -p ${DATAFED_DEPENDENCIES_INSTALL_PATH}
 
 # For communicating with repo server
 EXPOSE 7512
@@ -45,12 +47,12 @@ RUN chown -R datafed:datafed ${DATAFED_DIR}
 USER datafed
 
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh &&\
- cmake -S. -B build -DBUILD_REPO_SERVER=False -DBUILD_AUTHZ=False \
+ ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake -S. -B build -DBUILD_REPO_SERVER=False -DBUILD_AUTHZ=False \
                     -DBUILD_CORE_SERVER=True -DBUILD_WEB_SERVER=False \
                     -DBUILD_DOCS=False -DBUILD_PYTHON_CLIENT=False \
                     -DBUILD_FOXX=False &&\
- cmake --build build -j 8
-RUN cmake --build build --target install
+ ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build -j 8
+RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build --target install
 
 WORKDIR ${DATAFED_INSTALL_PATH}/core
 
diff --git a/core/server/AuthenticationManager.cpp b/core/server/AuthenticationManager.cpp
index 8617904a4..44bb35542 100644
--- a/core/server/AuthenticationManager.cpp
+++ b/core/server/AuthenticationManager.cpp
@@ -104,9 +104,9 @@ std::string AuthenticationManager::getUID(const std::string &public_key) const {
   EXCEPT(1, "Unrecognized public_key during execution of getUID.");
 }
 
-void AuthenticationManager::addKey(const PublicKeyType pub_key_type,
-                                   const std::string public_key,
-                                   const std::string uid) {
+void AuthenticationManager::addKey(const PublicKeyType &pub_key_type,
+                                   const std::string &public_key,
+                                   const std::string &uid) {
   std::lock_guard<std::mutex> lock(m_lock);
   m_auth_mapper.addKey(pub_key_type, public_key, uid);
 }
diff --git a/core/server/AuthenticationManager.hpp b/core/server/AuthenticationManager.hpp
index f0ae28ae6..d648da8ab 100644
--- a/core/server/AuthenticationManager.hpp
+++ b/core/server/AuthenticationManager.hpp
@@ -79,8 +79,8 @@ class AuthenticationManager : public IAuthenticationManager {
    **/
   virtual bool hasKey(const std::string &pub_key) const final;
 
-  void addKey(const PublicKeyType pub_key_type, const std::string public_key,
-              const std::string uid);
+  void addKey(const PublicKeyType &pub_key_type, const std::string &public_key,
+              const std::string &uid);
 
   /**
    * Will the id or throw an error
diff --git a/core/server/CMakeLists.txt b/core/server/CMakeLists.txt
index a4625a3ee..22e4c1b5d 100644
--- a/core/server/CMakeLists.txt
+++ b/core/server/CMakeLists.txt
@@ -8,15 +8,24 @@ configure_file(
 file( GLOB Sources "*.cpp" )
 file( GLOB Main "main.cpp")
 list(REMOVE_ITEM Sources files ${Main})
-add_library( datafed-core-lib STATIC ${Sources} )
-set_target_properties(datafed-core-lib PROPERTIES POSITION_INDEPENDENT_CODE ON)
 
 # Must be public for unit tests to import them
+if(BUILD_SHARED_LIBS)
+  add_library( datafed-core-lib SHARED ${Sources} )
+  target_link_libraries( datafed-core-lib PRIVATE protobuf::libprotobuf Threads::Threads
+    "${DATAFED_CURL_LIBRARIES}" "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}"
+    "${DATAFED_ZLIB_LIBRARIES}"  ${DATAFED_BOOST_LIBRARIES} libzmq nlohmann_json_schema_validator -ldl )
+else()
+  add_library( datafed-core-lib STATIC ${Sources} )
+  target_link_libraries( datafed-core-lib PRIVATE protobuf::libprotobuf Threads::Threads
+    "${DATAFED_CURL_LIBRARIES}" "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}"
+    "${DATAFED_ZLIB_LIBRARIES}"  "${DATAFED_BOOST_LIBRARIES}" libzmq-static
+    "${DATAFED_JSON_SCHEMA_LIBRARY_PATH}" -ldl )
+endif()
 target_include_directories( datafed-core-lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} )
-target_link_libraries( datafed-core-lib OpenSSL::Crypto common datafed-protobuf protobuf::libprotobuf
-	Threads::Threads ${CURL_LIBRARIES} ${Boost_LIBRARIES} libzmq-static nlohmann_json_schema_validator )
-
-add_executable( datafed-core ${Main})
+set_target_properties(datafed-core-lib PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_link_libraries( datafed-core-lib PUBLIC common datafed-protobuf )
+add_executable( datafed-core ${Main} )
 target_link_libraries( datafed-core datafed-core-lib )
 
 add_subdirectory(tests)
diff --git a/core/server/ClientWorker.cpp b/core/server/ClientWorker.cpp
index 91ba2f5e1..2c3084ee0 100644
--- a/core/server/ClientWorker.cpp
+++ b/core/server/ClientWorker.cpp
@@ -346,6 +346,11 @@ void ClientWorker::workerThread(LogContext log_context) {
       ICommunicator::Response response =
           client->receive(MessageType::GOOGLE_PROTOCOL_BUFFER);
       if (response.time_out == false and response.error == false) {
+        if (not response.message) {
+          DL_ERROR(
+              message_log_context,
+              "No timeout or error was reported but message is not defined.")
+        }
 
         IMessage &message = *response.message;
         uint16_t msg_type = std::get<uint16_t>(
diff --git a/core/server/DatabaseAPI.cpp b/core/server/DatabaseAPI.cpp
index 5cdb0c1ae..98890d7ee 100644
--- a/core/server/DatabaseAPI.cpp
+++ b/core/server/DatabaseAPI.cpp
@@ -1727,9 +1727,8 @@ void DatabaseAPI::queryCreate(const Auth::QueryCreateRequest &a_request,
   options.always_print_enums_as_ints = true;
   options.preserve_proto_field_names = true;
 
-  auto stat =
-      google::protobuf::util::MessageToJsonString(a_request.query(),
-                                                  &query_json, options);
+  auto stat = google::protobuf::util::MessageToJsonString(a_request.query(),
+                                                          &query_json, options);
   if (!stat.ok()) {
     EXCEPT(1, "Invalid search request");
   }
@@ -1767,9 +1766,8 @@ void DatabaseAPI::queryUpdate(const Auth::QueryUpdateRequest &a_request,
     options.always_print_enums_as_ints = true;
     options.preserve_proto_field_names = true;
 
-    auto stat =
-        google::protobuf::util::MessageToJsonString(a_request.query(),
-                                                    &query_json, options);
+    auto stat = google::protobuf::util::MessageToJsonString(
+        a_request.query(), &query_json, options);
     if (!stat.ok()) {
       EXCEPT(1, "Invalid search request");
     }
@@ -1844,9 +1842,8 @@ void DatabaseAPI::setQueryData(QueryDataReply &a_reply,
   a_reply.set_ct(obj.getNumber("ct"));
   a_reply.set_ut(obj.getNumber("ut"));
 
-  auto stat =
-      google::protobuf::util::JsonStringToMessage(
-          obj.getValue("query").toString(), a_reply.mutable_query());
+  auto stat = google::protobuf::util::JsonStringToMessage(
+      obj.getValue("query").toString(), a_reply.mutable_query());
   if (!stat.ok()) {
     EXCEPT(1, "Query data reply parse error!");
   }
@@ -2510,6 +2507,9 @@ void DatabaseAPI::repoAuthz(const Auth::RepoAuthzRequest &a_request,
   (void)a_reply;
   Value result;
 
+  DL_INFO(log_context, "authz/gridftp repo: " << a_request.repo() << " file "
+                                              << a_request.file() << " act "
+                                              << a_request.action());
   dbGet("authz/gridftp",
         {{"repo", a_request.repo()},
          {"file", a_request.file()},
@@ -3106,7 +3106,7 @@ void DatabaseAPI::taskAbort(const std::string &a_task_id,
                             const std::string &a_msg,
                             libjson::Value &a_task_reply,
                             LogContext log_context) {
-  libjson::Value doc = a_msg;
+  libjson::Value doc(a_msg);
   string body = doc.toString();
 
   dbPost("task/abort", {{"task_id", a_task_id}}, &body, a_task_reply,
diff --git a/core/server/TaskWorker.cpp b/core/server/TaskWorker.cpp
index 3660c8308..95c55ead2 100644
--- a/core/server/TaskWorker.cpp
+++ b/core/server/TaskWorker.cpp
@@ -339,6 +339,10 @@ TaskWorker::cmdRawDataUpdateSize(TaskWorker &me, const Value &a_task_params,
   if (response.time_out == true) {
     return response;
   } else if (response.error == false) {
+    if (not response.message) {
+      DL_ERROR(log_context,
+               "No timeout or error was reported but no message was defined.");
+    }
     auto proto_msg =
         std::get<google::protobuf::Message *>(response.message->getPayload());
     auto size_reply = dynamic_cast<Auth::RepoDataSizeReply *>(proto_msg);
diff --git a/doc_source/source/admin/general.rst b/doc_source/source/admin/general.rst
index 1b9062e20..6f4a57fed 100644
--- a/doc_source/source/admin/general.rst
+++ b/doc_source/source/admin/general.rst
@@ -98,7 +98,7 @@ of the configuration options:
 10. DATAFED_SERVER_PORT - Needed by repo server
 11. DATAFED_DOMAIN - Needed by repo, web and authz
 12. DATAFED_GCS_ROOT_NAME - Needed by repo and authz by Globus
-13. GCS_COLLECTION_ROOT_PATH - Needed by repo and authz for Globus 
+13. DATAFED_GCS_COLLECTION_ROOT_PATH - Needed by repo and authz for Globus 
 14. DATAFED_REPO_ID_AND_DIR - Needed for repo and authz
 
 Descriptions of what these variables are can also be found in the ./config/datafed.sh file. Once the 
diff --git a/docker/Dockerfile.dependencies b/docker/Dockerfile.dependencies
index af17d0890..2120c96cc 100644
--- a/docker/Dockerfile.dependencies
+++ b/docker/Dockerfile.dependencies
@@ -32,19 +32,33 @@ COPY ./scripts/dependency_install_functions.sh	${BUILD_DIR}/scripts/
 COPY ./scripts/dependency_versions.sh						${BUILD_DIR}/scripts/
 COPY ./scripts/install_dependencies.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/utils.sh ${BUILD_DIR}/scripts/utils.sh
-COPY ./scripts/install_core_dependencies.sh ${BUILD_DIR}/scripts/
-COPY ./scripts/install_repo_dependencies.sh ${BUILD_DIR}/scripts/
-COPY ./scripts/install_ws_dependencies.sh ${BUILD_DIR}/scripts/
-COPY ./scripts/install_authz_dependencies.sh ${BUILD_DIR}/scripts/
+COPY ./scripts/install_docs_dependencies.sh ${BUILD_DIR}/scripts/
+COPY ./cmake/Web.cmake ${BUILD_DIR}/cmake/Web.cmake
+COPY ./cmake/Version.cmake ${BUILD_DIR}/cmake/Version.cmake
+COPY ./web/package.json.in ${BUILD_DIR}/web/package.json.in
+COPY ./web/version.js.in ${BUILD_DIR}/web/version.js.in
 COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/generate_datafed.sh
-COPY ./.gitmodules ${BUILD_DIR}/.gitmodules
-COPY ./.git ${BUILD_DIR}/.git
 
 RUN echo "#!/bin/bash\n\$@" > /usr/bin/sudo && chmod +x /usr/bin/sudo
 
 # run build scripts
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh
-RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ${BUILD_DIR}/scripts/install_dependencies.sh
+
+# Web dependencies
+COPY ./scripts/install_ws_dependencies.sh ${BUILD_DIR}/scripts/
+RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ${BUILD_DIR}/scripts/install_dependencies.sh -a -r -z -c
+
+# Core dependencies
+COPY ./scripts/install_core_dependencies.sh ${BUILD_DIR}/scripts/
+RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ${BUILD_DIR}/scripts/install_dependencies.sh -a -r -z -w
+
+# Repo dependencies
+COPY ./scripts/install_repo_dependencies.sh ${BUILD_DIR}/scripts/
+RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ${BUILD_DIR}/scripts/install_dependencies.sh -a -z -w -c
+
+# Authz dependencies
+COPY ./scripts/install_authz_dependencies.sh ${BUILD_DIR}/scripts/
+RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ${BUILD_DIR}/scripts/install_dependencies.sh -a -w -c -r 
 
 COPY ./scripts/copy_dependency.sh ${BUILD_DIR}/scripts/
 RUN ${BUILD_DIR}/scripts/copy_dependency.sh protobuf from
@@ -60,5 +74,3 @@ RUN mkdir -p ${DATAFED_INSTALL_PATH}/keys
 WORKDIR ${BUILD_DIR}
 
 COPY ./common ${BUILD_DIR}/common
-
-# RUN cp -R $HOME/.nvm ${NVM_DIR}
diff --git a/docker/Dockerfile.foxx b/docker/Dockerfile.foxx
index 6a36a5780..c1a7726c8 100644
--- a/docker/Dockerfile.foxx
+++ b/docker/Dockerfile.foxx
@@ -2,29 +2,32 @@
 # cd ${PROJECT_ROOT} or cd DataFed
 # docker build -f core/docker/Dockerfile .
 
-ARG           BUILD_BASE="ubuntu:focal"
-ARG         DEPENDENCIES="dependencies"
-ARG              RUNTIME="runtime"
-ARG          DATAFED_DIR="/datafed"
-ARG DATAFED_INSTALL_PATH="/opt/datafed"
+ARG                        BUILD_BASE="ubuntu:focal"
+ARG                      DEPENDENCIES="dependencies"
+ARG                           RUNTIME="runtime"
+ARG                       DATAFED_DIR="/datafed"
+ARG              DATAFED_INSTALL_PATH="/opt/datafed"
 ARG DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
-ARG            GCS_IMAGE="code.ornl.gov:4567/dlsw/datafed/gcs-ubuntu-focal"
-ARG            BUILD_DIR="$DATAFED_DIR/source"
-ARG              NVM_DIR="$DATAFED_DIR/.nvm"
-ARG              NVM_INC="$DATAFED_DIR/.nvm/versions/node/v13.14.0/include/node"
-ARG              NVM_BIN="$DATAFED_DIR/.nvm/versions/node/v13.14.0/bin"
-ARG              LIB_DIR="/usr/local/lib"
+ARG                         GCS_IMAGE="code.ornl.gov:4567/dlsw/datafed/gcs-ubuntu-focal"
+ARG                         BUILD_DIR="$DATAFED_DIR/source"
+ARG                           NVM_DIR="$DATAFED_DIR/.nvm"
+ARG                           NVM_INC="$DATAFED_DIR/.nvm/versions/node/v13.14.0/include/node"
+ARG                           NVM_BIN="$DATAFED_DIR/.nvm/versions/node/v13.14.0/bin"
+ARG                           LIB_DIR="/usr/local/lib"
 
 FROM ${DEPENDENCIES} AS foxx-build
 
 ARG DATAFED_DIR
 ARG BUILD_DIR
 ARG DATAFED_INSTALL_PATH
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH
 
-ENV BUILD_DIR="${BUILD_DIR}"
-ENV DATAFED_DIR="${DATAFED_DIR}"
+ENV                         BUILD_DIR="${BUILD_DIR}"
+ENV                       DATAFED_DIR="${DATAFED_DIR}"
 ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
-ENV PATH=${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:${PATH}
+ENV                              PATH=${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:${PATH}
+ENV              DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH"
+ENV          DATAFED_DEFAULT_LOG_PATH="$DATAFED_INSTALL_PATH/logs"
 
 COPY ./core/CMakeLists.txt             ${BUILD_DIR}/core/CMakeLists.txt
 COPY ./CMakeLists.txt                  ${BUILD_DIR}
@@ -35,28 +38,26 @@ COPY ./docker/entrypoint_foxx.sh       ${BUILD_DIR}/docker/entrypoint_foxx.sh
 COPY ./core/database                   ${BUILD_DIR}/core/database
 COPY ./scripts/install_foxx.sh         ${BUILD_DIR}/scripts/
 
-COPY --from=foxx-build ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin
 # Create datafed user, prefer more secure login options than password
 # Recommended to mount ssh public key on run
 RUN adduser --disabled-password --gecos "" datafed
 
 RUN mkdir -p ${DATAFED_DIR}
-RUN mkdir -p /opt/datafed
-RUN mkdir -p /var/log/datafed
-RUN chown -R datafed:root /opt/datafed
-RUN chown -R datafed:root /var/log/datafed
-RUN chown -R datafed:root ${DATAFED_DIR}
-RUN chown -R datafed:root ${DATAFED_DEPENDENCIES_INSTALL_PATH}
+RUN mkdir -p ${DATAFED_INSTALL_PATH}
+RUN mkdir -p ${DATAFED_DEFAULT_LOG_PATH}
 
 RUN curl -OL https://download.arangodb.com/arangodb38/DEBIAN/Release.key; apt-key add - < Release.key
-#RUN wget -q https://www.arangodb.com/repositories/arangodb38/DEBIAN/Release.key -O- | apt-key add -
-RUN echo 'deb https://download.arangodb.com/arangodb38/DEBIAN/ /' | tee /etc/apt/sources.list.d/arangodb.list
+
+# RUN echo 'deb https://download.arangodb.com/arangodb38/DEBIAN/ /' | tee /etc/apt/sources.list.d/arangodb.list
+# If trust the site but the gpg key has expired can add [trusted=yes]
+# i.e.
+RUN echo 'deb [trusted=yes] https://download.arangodb.com/arangodb38/DEBIAN/ /' | tee /etc/apt/sources.list.d/arangodb.list
 RUN apt update
 RUN apt install arangodb3-client -y
 RUN arangosh --version
 
 USER root
 
-WORKDIR ${DATAFED_DIR}
+WORKDIR ${BUILD_DIR}
 
 ENTRYPOINT ${BUILD_DIR}/docker/entrypoint_foxx.sh
diff --git a/docker/entrypoint_foxx.sh b/docker/entrypoint_foxx.sh
index df20df616..ed8387eb9 100755
--- a/docker/entrypoint_foxx.sh
+++ b/docker/entrypoint_foxx.sh
@@ -2,28 +2,34 @@
 
 set -euf -o pipefail
 
-if [ -n "$UID" ]; then
-    usermod -u $UID datafed
-fi
+# NOTE We do not need to change the user in this container because we should not have
+# any log output, and running chmod to update all the folders during runtime is
+# expensive!
+# 
+# The following lines are not needed
+# if [ -n "$UID" ]; then
+#     usermod -u $UID datafed
+# fi
 
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 PROJECT_ROOT=$(realpath "${SOURCE}/../")
 
-log_path="$DATAFED_DEFAULT_LOG_PATH"
-if [ ! -d "${log_path}" ]
-then
-  su -c "mkdir -p ${log_path}" datafed
-fi
-
-cd "${PROJECT_ROOT}"
-# Check to see if foxx has previously been installed
 install_flag="/tmp/.foxx_is_installed"
 if [ ! -f "$install_flag" ]
 then
-  su datafed -c "${PROJECT_ROOT}/scripts/generate_datafed.sh"
-
-  su datafed -c "cmake -S. -B build						\
+  log_path="$DATAFED_DEFAULT_LOG_PATH"
+  if [ ! -d "${log_path}" ]
+  then
+    su -c "mkdir -p ${log_path}" datafed
+  fi
+
+  # It should be fine to run this as root because it is an ephemeral container anyway
+  cd "${PROJECT_ROOT}"
+  # Check to see if foxx has previously been installed
+  "${PROJECT_ROOT}/scripts/generate_datafed.sh"
+
+  "${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake" -S. -B build						\
     -DBUILD_REPO_SERVER=False		\
     -DBUILD_COMMON=False        \
     -DBUILD_AUTHZ=False					\
@@ -32,14 +38,15 @@ then
     -DBUILD_DOCS=False					\
     -DBUILD_PYTHON_CLIENT=False	\
     -DBUILD_FOXX=True           \
-    -DINSTALL_FOXX=True"
+    -DINSTALL_FOXX=True
+
 
-  su datafed -c "cmake --build build"
+  "${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake" --build build
 
   # Give arango container a minute to initialize
   # should be replaced with health check at some point
   sleep 5
-  su datafed -c "cmake --build build --target install"
+  "${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake" --build build --target install
   
   touch "$install_flag"
 fi
diff --git a/docs/_sources/admin/general.rst.txt b/docs/_sources/admin/general.rst.txt
index 9b33e9d1e..c780e1c4e 100644
--- a/docs/_sources/admin/general.rst.txt
+++ b/docs/_sources/admin/general.rst.txt
@@ -98,7 +98,7 @@ of the configuration options:
 10. DATAFED_SERVER_PORT - Needed by repo server
 11. DATAFED_DOMAIN - Needed by repo, web and authz
 12. DATAFED_GCS_ROOT_NAME - Needed by repo and authz by Globus
-13. GCS_COLLECTION_ROOT_PATH - Needed by repo and authz for Globus 
+13. DATAFED_GCS_COLLECTION_ROOT_PATH - Needed by repo and authz for Globus 
 14. DATAFED_REPO_ID_AND_DIR - Needed for repo and authz
 
 Descriptions of what these variables are can also be found in the ./config/datafed.sh file. Once the 
diff --git a/docs/admin/general.html b/docs/admin/general.html
index 370024abd..ef58285ea 100644
--- a/docs/admin/general.html
+++ b/docs/admin/general.html
@@ -203,7 +203,7 @@ <h2>Downloading DataFed &amp; Installing Dependencies</h2>
 <li><p>DATAFED_SERVER_PORT - Needed by repo server</p></li>
 <li><p>DATAFED_DOMAIN - Needed by repo, web and authz</p></li>
 <li><p>DATAFED_GCS_ROOT_NAME - Needed by repo and authz by Globus</p></li>
-<li><p>GCS_COLLECTION_ROOT_PATH - Needed by repo and authz for Globus</p></li>
+<li><p>DATAFED_GCS_COLLECTION_ROOT_PATH - Needed by repo and authz for Globus</p></li>
 <li><p>DATAFED_REPO_ID_AND_DIR - Needed for repo and authz</p></li>
 </ol>
 <p>Descriptions of what these variables are can also be found in the ./config/datafed.sh file. Once the
@@ -445,4 +445,4 @@ <h2>Networking</h2>
   </script> 
 
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/external/globus-connect-server-deploy b/external/globus-connect-server-deploy
index 20132721a..38a40b7a0 160000
--- a/external/globus-connect-server-deploy
+++ b/external/globus-connect-server-deploy
@@ -1,1 +1,1 @@
-Subproject commit 20132721ab9413078dc721a8d9e1d5a04a7e1c3f
+Subproject commit 38a40b7a00f86b2355663680c328c1fef3311f6a
diff --git a/python/datafed_pkg/datafed/CMakeLists.txt b/python/datafed_pkg/datafed/CMakeLists.txt
index c699904fe..9e066deea 100644
--- a/python/datafed_pkg/datafed/CMakeLists.txt
+++ b/python/datafed_pkg/datafed/CMakeLists.txt
@@ -17,23 +17,26 @@ protobuf_generate(
     TARGET protobuf-target-py
     IMPORT_DIRS "${DataFed_SOURCE_DIR}/common/proto/common"
     OUT_VAR protobuf-generated-files-py
-    PROTOC_OUT_DIR "${CMAKE_CURRENT_LIST_DIR}"
+    PROTOC_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}"
   )
 
 add_custom_target( pydatafed_src DEPENDS protobuf-target-py )
 
+# By default this will output the proto py files in the CMAKE BINARY DIR
 add_custom_command( TARGET pydatafed_src POST_BUILD
     COMMAND sed -i -r 's:^import.*_pb2:from . \\0:' ${protobuf-generated-files-py}
-    COMMAND ${CMAKE_SOURCE_DIR}/python/pyproto_add_msg_idx.py
-    ${CMAKE_SOURCE_DIR}/common/proto/common/SDMS_Anon.proto SDMS_Anon_pb2.py
-    COMMAND ${CMAKE_SOURCE_DIR}/python/pyproto_add_msg_idx.py
-    ${CMAKE_SOURCE_DIR}/common/proto/common/SDMS_Auth.proto SDMS_Auth_pb2.py )
-
-# Create copies of the files so they show up in the source folder as well 
+    COMMAND ${DataFed_SOURCE_DIR}/python/pyproto_add_msg_idx.py ${DataFed_SOURCE_DIR}/common/proto/common/SDMS_Anon.proto ${CMAKE_CURRENT_BINARY_DIR}/SDMS_Anon_pb2.py
+    COMMAND ${DataFed_SOURCE_DIR}/python/pyproto_add_msg_idx.py ${DataFed_SOURCE_DIR}/common/proto/common/SDMS_Auth.proto ${CMAKE_CURRENT_BINARY_DIR}/SDMS_Auth_pb2.py 
+)     
+      
+# Crea#te copies of the files so they show up in the source folder as well 
 # for the purpose of testing
 add_custom_target( pydatafed_proto_src DEPENDS pydatafed_src )
 add_custom_command( TARGET pydatafed_proto_src POST_BUILD pydatafed_src 
-    COMMAND cp SDMS_Auth_pb2.py ${CMAKE_CURRENT_SOURCE_DIR}/ 
-    COMMAND cp SDMS_pb2.py ${CMAKE_CURRENT_SOURCE_DIR}/ 
-    COMMAND cp Version_pb2.py ${CMAKE_CURRENT_SOURCE_DIR}/ 
-    COMMAND cp SDMS_Anon_pb2.py ${CMAKE_CURRENT_SOURCE_DIR}/ )
+  COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/SDMS_Auth_pb2.py ${CMAKE_CURRENT_SOURCE_DIR}/ 
+  COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/SDMS_pb2.py ${CMAKE_CURRENT_SOURCE_DIR}/ 
+  COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/Version_pb2.py ${CMAKE_CURRENT_SOURCE_DIR}/ 
+  COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/SDMS_Anon_pb2.py ${CMAKE_CURRENT_SOURCE_DIR}/
+)
+
+
diff --git a/python/docker/Dockerfile.python-client-base.ubuntu b/python/docker/Dockerfile.python-client-base.ubuntu
index 3395b072a..53102ac43 100644
--- a/python/docker/Dockerfile.python-client-base.ubuntu
+++ b/python/docker/Dockerfile.python-client-base.ubuntu
@@ -2,6 +2,9 @@ FROM ubuntu:focal as build
 
 ARG DATAFED_DIR="/datafed"
 ARG BUILD_DIR="/datafed/source"
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
+
+ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
 
 RUN mkdir -p ${BUILD_DIR}
 
@@ -9,7 +12,10 @@ WORKDIR ${BUILD_DIR}
 
 COPY ./scripts/dependency_install_functions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/dependency_versions.sh ${BUILD_DIR}/scripts/
+COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/
+COPY ./scripts/utils.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/install_python_client_dependencies.sh ${BUILD_DIR}/scripts/
 
 RUN echo "#!/bin/bash\n\$@" > /usr/bin/sudo && chmod +x /usr/bin/sudo
+RUN ${BUILD_DIR}/scripts/generate_datafed.sh
 RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ${BUILD_DIR}/scripts/install_python_client_dependencies.sh
diff --git a/python/docker/Dockerfile.python-client.ubuntu b/python/docker/Dockerfile.python-client.ubuntu
index f373603fc..d8b7151a6 100644
--- a/python/docker/Dockerfile.python-client.ubuntu
+++ b/python/docker/Dockerfile.python-client.ubuntu
@@ -5,11 +5,13 @@ FROM "$BASE_PYTHON_CLIENT_IMAGE" as build
 ARG DATAFED_DIR="/datafed"
 ARG BUILD_DIR="/datafed/source"
 ARG DATAFED_INSTALL_PATH="/datafed/install"
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
 
 # The above should also be available at runtime
 ENV DATAFED_DIR="$DATAFED_DIR"
 ENV BUILD_DIR="$BUILD_DIR"
 ENV DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH/python-client"
+ENV DATAFED_DEPENDENCIES_INSTALL_PATH="$DATAFED_DEPENDENCIES_INSTALL_PATH"
 
 RUN mkdir -p ${BUILD_DIR}/logs
 RUN mkdir -p ${DATAFED_INSTALL_PATH}
@@ -34,13 +36,13 @@ RUN chown -R datafed:datafed ${DATAFED_DIR}
 USER datafed
 
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh &&\
- cmake -S. -B build -DBUILD_REPO_SERVER=False -DBUILD_AUTHZ=False \
+ ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake -S. -B build -DBUILD_REPO_SERVER=False -DBUILD_AUTHZ=False \
                     -DBUILD_CORE_SERVER=False -DBUILD_WEB_SERVER=False \
                     -DBUILD_DOCS=False -DBUILD_PYTHON_CLIENT=True \
                     -DBUILD_FOXX=False -DENABLE_UNIT_TESTS=False \
                     -DBUILD_TESTS=False -DBUILD_COMMON=False &&\
- cmake --build build -j 8
-RUN cmake --build build --target pydatafed
+ ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build -j 8
+RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build --target pydatafed
 
 WORKDIR ${BUILD_DIR}/python/datafed_pkg
 
diff --git a/repository/docker/Dockerfile b/repository/docker/Dockerfile
index cd62529a8..74a3d5249 100644
--- a/repository/docker/Dockerfile
+++ b/repository/docker/Dockerfile
@@ -46,7 +46,7 @@ RUN ${BUILD_DIR}/scripts/generate_datafed.sh && \
 	-DBUILD_DOCS=False					\
 	-DBUILD_PYTHON_CLIENT=False	\
 	-DBUILD_FOXX=False
-RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build
+RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build -j 8
 RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build --target install
 
 FROM ${RUNTIME} AS repo
@@ -56,10 +56,12 @@ ARG DATAFED_DIR
 ARG DATAFED_INSTALL_PATH
 ARG BUILD_DIR
 ARG LIB_DIR
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH
 
 # The above should also be available at runtime
 ENV DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH"
 ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
+ENV DATAFED_GCS_COLLECTION_ROOT_PATH="/mnt/datafed"
 ENV          DATAFED_DIR="$DATAFED_DIR"
 ENV            BUILD_DIR="$BUILD_DIR"
 ENV              LIB_DIR="$LIB_DIR"
@@ -82,12 +84,16 @@ RUN ldconfig
 
 USER datafed
 
-COPY --chown=datafed:root ./repository/docker/entrypoint_repo.sh ${BUILD_DIR}/repository/docker/entrypoint.sh
-COPY --chown=datafed:root ./scripts/generate_datafed.sh          ${BUILD_DIR}/scripts/generate_datafed.sh
-COPY --chown=datafed:root ./scripts/generate_repo_config.sh      ${BUILD_DIR}/scripts/generate_repo_config.sh
-COPY --chown=datafed:root ./scripts/install_repo.sh              ${BUILD_DIR}/scripts/install_repo.sh
-COPY --chown=datafed:root ./cmake/Version.cmake                  ${BUILD_DIR}/cmake/Version.cmake
+COPY --chown=datafed:root ./scripts/generate_datafed.sh          ${DATAFED_DIR}/scripts/generate_datafed.sh
+COPY --chown=datafed:root ./scripts/generate_repo_config.sh      ${DATAFED_DIR}/scripts/generate_repo_config.sh
+COPY --chown=datafed:root ./scripts/install_repo.sh              ${DATAFED_DIR}/scripts/install_repo.sh
+COPY --chown=datafed:root ./cmake/Version.cmake                  ${DATAFED_DIR}/cmake/Version.cmake
+COPY --chown=datafed:root ./repository/docker/entrypoint_repo.sh ${BUILD_DIR}/repository/entrypoint.sh
 COPY --from=repo-build --chown=datafed:root ${DATAFED_INSTALL_PATH}/repo/datafed-repo ${DATAFED_INSTALL_PATH}/repo/datafed-repo
 
-ENTRYPOINT ["${BUILD_DIR}/repository/docker/entrypoint.sh"]
-CMD ["${DATAFED_INSTALL_PATH}/repo/datafed-repo","--cfg","${DATAFED_INSTALL_PATH}/repo/datafed-repo.cfg"]
+USER root
+
+RUN chown -R datafed:root /datafed
+RUN chown -R datafed:root /opt
+
+ENTRYPOINT [ "/datafed/source/repository/entrypoint.sh", "/opt/datafed/repo/datafed-repo","--cfg","/opt/datafed/repo/datafed-repo.cfg"]
diff --git a/repository/docker/Dockerfile.gcs b/repository/docker/Dockerfile.gcs
index fa95baf97..717d2a0f8 100644
--- a/repository/docker/Dockerfile.gcs
+++ b/repository/docker/Dockerfile.gcs
@@ -1,17 +1,18 @@
 # NOTE this image must be built with respect to the base of the project i.e.
 # cd ${PROJECT_ROOT} or cd DataFed
 # docker build -f repository/docker/Dockerfile.gcs .
-ARG           BUILD_BASE="ubuntu:focal"
-ARG         DEPENDENCIES="dependencies"
-ARG              RUNTIME="runtime"
-ARG          DATAFED_DIR="/datafed"
-ARG DATAFED_INSTALL_PATH="/opt/datafed"
-ARG            GCS_IMAGE="code.ornl.gov:4567/dlsw/datafed/gcs-ubuntu-focal"
-ARG            BUILD_DIR="$DATAFED_DIR/source"
-ARG              NVM_DIR="$DATAFED_DIR/.nvm"
-ARG              NVM_INC="$DATAFED_DIR/.nvm/versions/node/v13.14.0/include/node"
-ARG              NVM_BIN="$DATAFED_DIR/.nvm/versions/node/v13.14.0/bin"
-ARG              LIB_DIR="/usr/local/lib"
+ARG                        BUILD_BASE="ubuntu:focal"
+ARG                      DEPENDENCIES="dependencies"
+ARG                           RUNTIME="runtime"
+ARG                       DATAFED_DIR="/datafed"
+ARG              DATAFED_INSTALL_PATH="/opt/datafed"
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
+ARG                         GCS_IMAGE="code.ornl.gov:4567/dlsw/datafed/gcs-ubuntu-focal"
+ARG                         BUILD_DIR="$DATAFED_DIR/source"
+ARG                           NVM_DIR="$DATAFED_DIR/.nvm"
+ARG                           NVM_INC="$DATAFED_DIR/.nvm/versions/node/v13.14.0/include/node"
+ARG                           NVM_BIN="$DATAFED_DIR/.nvm/versions/node/v13.14.0/bin"
+ARG                           LIB_DIR="/usr/local/lib"
 
 FROM ${DEPENDENCIES} AS dependencies
 
@@ -20,19 +21,24 @@ FROM ${GCS_IMAGE}
 ARG DATAFED_DIR
 ARG BUILD_DIR
 ARG DATAFED_INSTALL_PATH
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH
 ARG LIB_DIR
 
-ENV GCS_COLLECTION_ROOT_PATH="$DATAFED_DIR/collections/mapped"
-ENV     DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH"
-ENV	             DATAFED_DIR="$DATAFED_DIR"
-ENV                BUILD_DIR="$BUILD_DIR"
-ENV                  LIB_DIR="$LIB_DIR"
+ENV DATAFED_DEPENDENCIES_INSTALL_PATH="$DATAFED_DEPENDENCIES_INSTALL_PATH"
+ENV  DATAFED_GCS_COLLECTION_ROOT_PATH="/mnt/datafed"
+ENV     DATAFED_GLOBUS_CRED_FILE_PATH=/opt/datafed/globus/client_cred.json
+ENV              DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH"
+ENV                       DATAFED_DIR="$DATAFED_DIR"
+ENV                         BUILD_DIR="$BUILD_DIR"
+ENV                           LIB_DIR="$LIB_DIR"
+ENV          DATAFED_GLOBUS_REPO_USER="datafed"
+ENV          DATAFED_DEFAULT_LOG_PATH="$DATAFED_INSTALL_PATH/logs"
 
 RUN mkdir -p ${BUILD_DIR}
-RUN mkdir -p ${BUILD_DIR}/logs
+RUN mkdir -p ${DATAFED_INSTALL_PATH}/logs
 RUN mkdir -p ${BUILD_DIR}/common/proto
 RUN mkdir -p ${DATAFED_INSTALL_PATH}/authz
-RUN mkdir -p ${DATAFED_DIR}/collections/mapped
+RUN mkdir -p /mnt/datafed
 
 # For communicating with public
 EXPOSE 443
@@ -46,69 +52,98 @@ RUN adduser --disabled-password --gecos "" datafed
 
 RUN echo "#!/bin/bash\n\$@" > /usr/bin/sudo && chmod +x /usr/bin/sudo
 
-COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/
+COPY ./scripts/generate_datafed.sh             ${BUILD_DIR}/scripts/
 COPY ./scripts/dependency_versions.sh          ${BUILD_DIR}/scripts/
+COPY ./scripts/utils.sh                        ${BUILD_DIR}/scripts/utils.sh
 COPY ./scripts/dependency_install_functions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/install_authz_dependencies.sh   ${BUILD_DIR}/scripts/
-COPY ./scripts/copy_dependency.sh   ${BUILD_DIR}/scripts/
+COPY ./scripts/copy_dependency.sh              ${BUILD_DIR}/scripts/
 
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh
 RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ${BUILD_DIR}/scripts/install_authz_dependencies.sh unify
 
-
 # All files should be owned by the datafed user
 RUN chown -R datafed:root ${DATAFED_DIR}
 
-COPY --chown=datafed:root ./scripts/dependency_versions.sh        ${BUILD_DIR}/scripts/
-COPY --chown=datafed:root ./scripts/generate_authz_config.sh      ${BUILD_DIR}/scripts/generate_authz_config.sh
-COPY --chown=datafed:root ./CMakeLists.txt                        ${BUILD_DIR}
-COPY --chown=datafed:root ./cmake                                 ${BUILD_DIR}/cmake
-COPY --chown=datafed:root ./repository/CMakeLists.txt             ${BUILD_DIR}/repository/CMakeLists.txt
-COPY --chown=datafed:root ./repository/gridftp/CMakeLists.txt     ${BUILD_DIR}/repository/gridftp/CMakeLists.txt
-COPY --chown=datafed:root ./scripts/globus/setup_globus.sh        ${BUILD_DIR}/scripts/globus/setup_globus.sh
-COPY --chown=datafed:root ./scripts/globus/generate_repo_form.sh  ${BUILD_DIR}/scripts/globus/generate_repo_form.sh
-COPY --chown=datafed:root ./repository/docker/entrypoint_authz.sh ${BUILD_DIR}/repository/docker/entrypoint_authz.sh
-COPY --chown=datafed:root ./common                                ${BUILD_DIR}/common
-COPY --chown=datafed:root ./repository/gridftp/globus5            ${BUILD_DIR}/repository/gridftp/globus5
-
+COPY --chown=datafed:root ./scripts/dependency_versions.sh            ${BUILD_DIR}/scripts/
+COPY --chown=datafed:root ./scripts/generate_authz_config.sh          ${BUILD_DIR}/scripts/generate_authz_config.sh
+COPY --chown=datafed:root ./scripts/utils.sh                          ${BUILD_DIR}/scripts/utils.sh
+COPY --chown=datafed:root ./CMakeLists.txt                            ${BUILD_DIR}
+COPY --chown=datafed:root ./cmake                                     ${BUILD_DIR}/cmake
+COPY --chown=datafed:root ./repository/CMakeLists.txt                 ${BUILD_DIR}/repository/CMakeLists.txt
+COPY --chown=datafed:root ./repository/gridftp/CMakeLists.txt         ${BUILD_DIR}/repository/gridftp/CMakeLists.txt
+COPY --chown=datafed:root ./scripts/globus/setup_globus.sh            ${BUILD_DIR}/scripts/globus/setup_globus.sh
+COPY --chown=datafed:root ./scripts/globus/clean_globus.sh            ${BUILD_DIR}/scripts/globus/clean_globus.sh
+COPY --chown=datafed:root ./scripts/globus/generate_repo_form.sh      ${BUILD_DIR}/scripts/globus/generate_repo_form.sh
+COPY --chown=datafed:root ./repository/docker/entrypoint_authz.sh     ${BUILD_DIR}/repository/docker/entrypoint_authz.sh
+COPY --chown=datafed:root ./common                                    ${BUILD_DIR}/common
+COPY --chown=datafed:root ./repository/gridftp/globus5                ${BUILD_DIR}/repository/gridftp/globus5
+COPY --chown=datafed:root ./scripts/generate_gsi-authz_config.sh      ${BUILD_DIR}/scripts/generate_gsi-authz_config.sh
+COPY --chown=datafed:root ./scripts/globus/create_guest_collection.py ${BUILD_DIR}/scripts/globus/create_guest_collection.py
+COPY --chown=datafed:root ./scripts/globus/utils.py                   ${BUILD_DIR}/scripts/globus/utils.py
+COPY --chown=datafed:root ./scripts/globus/__init__.py                ${BUILD_DIR}/scripts/globus/__init__.py
 # These extra copy statements are necessary as the authz binary is built in the GCS container
 # and not the dependencies container so we must copy the build dependencies as well as the
 # runtime depedencies
-COPY --from=dependencies /usr/local/include/google					/usr/local/include/google
-COPY --from=dependencies /usr/local/include/sodium					/usr/local/include/sodium
-COPY --from=dependencies /usr/local/include/sodium.h				/usr/local/include/sodium.h
-COPY --from=dependencies /usr/local/include/zmq.h						/usr/local/include/zmq.h
-COPY --from=dependencies /usr/local/include/zmq_utils.h			/usr/local/include/zmq_utils.h
-COPY --from=dependencies /usr/include/zmq.hpp								/usr/include/zmq.hpp
-COPY --from=dependencies /usr/local/lib/pkgconfig/libzmq.pc /usr/local/lib/pkgconfig/libzmq.pc
-COPY --from=dependencies /usr/local/lib/cmake								/usr/local/lib/cmake
-COPY --from=dependencies /usr/local/bin/protoc							/usr/local/bin/protoc
-
-COPY --from=dependencies /libraries/libprotobuf.a          /libraries/libprotobuf.a
-COPY --from=dependencies /libraries/libprotoc.a						/libraries/libprotoc.a
-COPY --from=dependencies /libraries/libzmq.a               /libraries/libzmq.a
-COPY --from=dependencies /libraries/libsodium.a            /libraries/libsodium.a
-RUN ${BUILD_DIR}/scripts/copy_dependency.sh protobuf to
-RUN ${BUILD_DIR}/scripts/copy_dependency.sh protoc to
-RUN ${BUILD_DIR}/scripts/copy_dependency.sh libzmq to
-RUN ${BUILD_DIR}/scripts/copy_dependency.sh libsodium to
-
-RUN ${BUILD_DIR}/scripts/generate_authz_config.sh &&	\
-	${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake -S. -B build						\
-		-DBUILD_REPO_SERVER=False		\
-		-DBUILD_AUTHZ=True					\
-		-DBUILD_CORE_SERVER=False		\
-		-DBUILD_WEB_SERVER=False		\
-		-DBUILD_DOCS=False					\
-		-DBUILD_PYTHON_CLIENT=False	\
-		-DBUILD_FOXX=False
-RUN	${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build
+COPY --from=dependencies ${DATAFED_DEPENDENCIES_INSTALL_PATH} ${DATAFED_DEPENDENCIES_INSTALL_PATH}
+
+COPY --from=dependencies /usr/bin/make /usr/bin/make
+COPY --from=dependencies /usr/bin/g++  /usr/bin/g++
+COPY --from=dependencies /usr/bin/gcc  /usr/bin/gcc
+COPY --from=dependencies /usr/bin/ar   /usr/bin/ar
+COPY --from=dependencies /usr/bin/as   /usr/bin/as
+COPY --from=dependencies /usr/bin/ld   /usr/bin/ld
+COPY --from=dependencies /usr/bin/jq   /usr/bin/jq
+COPY --from=dependencies /usr/bin/sudo /usr/bin/sudo
+
+COPY --from=dependencies /etc/sudoers /etc/sudoers
+
+COPY --from=dependencies /usr/lib/gcc  /usr/lib/gcc
+
+COPY --from=dependencies /lib         /lib
+COPY --from=dependencies /usr/include /usr/include
+COPY --from=dependencies /usr/lib     /usr/lib
+
+COPY --from=dependencies ${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib/libprotobuf.a ${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib/libprotobuf.a
+COPY --from=dependencies ${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib/libprotoc.a   ${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib/libprotoc.a
+COPY --from=dependencies ${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib/libzmq.a      ${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib/libzmq.a
+COPY --from=dependencies ${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib/libsodium.a   ${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib/libsodium.a
+# Copy python env over
+COPY --from=dependencies ${DATAFED_DEPENDENCIES_INSTALL_PATH}/python/datafed  ${DATAFED_DEPENDENCIES_INSTALL_PATH}/python/datafed
+
+RUN apt update
+RUN apt-get install vim -y
+RUN apt-get reinstall globus-connect-server54 -y
+
+RUN ${BUILD_DIR}/scripts/generate_gsi-authz_config.sh
+# Don't build with syslog the container does not seem to support syslog
+RUN ${BUILD_DIR}/scripts/generate_authz_config.sh &&  \
+  ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake -S. -B build  \
+    -DBUILD_REPO_SERVER=False         \
+    -DBUILD_AUTHZ=True                \
+    -DBUILD_AUTHZ_WITH_SYSLOG=False   \
+    -DBUILD_CORE_SERVER=False         \
+    -DBUILD_WEB_SERVER=False          \
+    -DBUILD_DOCS=False                \
+    -DBUILD_PYTHON_CLIENT=False       \
+    -DBUILD_FOXX=False
+RUN  ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build -j 8
 RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build --target install
 
+# Get the file that is not a link and give it the right permissions
+RUN chmod 755 ${DATAFED_INSTALL_PATH}/authz/$(ls -l /opt/datafed/authz/ | grep -v "^l" | grep libdatafed-authz | awk '{print  $9}')
+
 COPY --chown=datafed:root ./scripts/globus/setup_globus.sh        ${BUILD_DIR}/scripts/globus/setup_globus.sh
 COPY --chown=datafed:root ./scripts/globus/generate_repo_form.sh  ${BUILD_DIR}/scripts/globus/generate_repo_form.sh
 COPY --chown=datafed:root ./repository/docker/entrypoint_authz.sh ${BUILD_DIR}/repository/docker/entrypoint_authz.sh
 
 USER root
 
+# Remove --client-id from command because it was deprecated
+RUN sed -i '/--client-id/d' /entrypoint.sh
+
 WORKDIR ${DATAFED_INSTALL_PATH}/authz
+
+# NOTE If you do not overwrite the entrypoint file it will use the GCS instance
+# provided one
+ENTRYPOINT ["/datafed/source/repository/docker/entrypoint_authz.sh"]
diff --git a/repository/docker/Dockerfile.gcs-authz-base.ubuntu b/repository/docker/Dockerfile.gcs-authz-base.ubuntu
index 023a8b1df..4ca8ecb4b 100644
--- a/repository/docker/Dockerfile.gcs-authz-base.ubuntu
+++ b/repository/docker/Dockerfile.gcs-authz-base.ubuntu
@@ -1,11 +1,19 @@
-FROM camden.ornl.gov/datafed/gcs-ubuntu-focal
+ARG BASE_GCS_IMAGE="camden.ornl.gov/datafed/gcs-ubuntu-focal"
+
+FROM "$BASE_GCS_IMAGE" as build
 
 ARG DATAFED_DIR="/datafed"
 ARG BUILD_DIR="/datafed/source"
 ARG DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
 
 ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
-ENV PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:${PATH}"
+
+# NOTE - it does not look like using $PATH variable inside this definition
+# resolves correctly as such they system paths are included explicitly when
+# changes are made to ENV PATH
+ENV PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+
+RUN mkdir -p ${BUILD_DIR}
 RUN mkdir -p ${BUILD_DIR}
 
 WORKDIR ${BUILD_DIR}
diff --git a/repository/docker/Dockerfile.gcs-authz.ubuntu b/repository/docker/Dockerfile.gcs-authz.ubuntu
index e71bb01c7..3b904f544 100644
--- a/repository/docker/Dockerfile.gcs-authz.ubuntu
+++ b/repository/docker/Dockerfile.gcs-authz.ubuntu
@@ -3,17 +3,20 @@ FROM camden.ornl.gov/datafed/gcs-authz-base:latest as build
 ARG DATAFED_DIR="/datafed"
 ARG BUILD_DIR="/datafed/source"
 ARG DATAFED_INSTALL_PATH="/datafed/install"
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
 
 # The above should also be available at runtime
 ENV DATAFED_DIR="$DATAFED_DIR"
 ENV BUILD_DIR="$BUILD_DIR"
 ENV DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH"
-ENV GCS_COLLECTION_ROOT_PATH="$DATAFED_DIR/collections/mapped"
+ENV DATAFED_GCS_COLLECTION_ROOT_PATH="/mnt/datafed"
+ENV DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
 
-RUN mkdir -p ${BUILD_DIR}/logs
+RUN mkdir -p ${DATAFED_INSTALL_PATH}/logs
 RUN mkdir -p ${BUILD_DIR}/common/proto
 RUN mkdir -p ${DATAFED_INSTALL_PATH}/authz
 RUN mkdir -p ${DATAFED_DIR}/collections/mapped
+RUN mkdir -p ${DATAFED_DEPENDENCIES_INSTALL_PATH}
 
 # vim and nc are for debugging
 RUN apt-get install vim netcat -y
@@ -44,18 +47,21 @@ COPY ./common ${BUILD_DIR}/common
 # All files should be owned by the datafed user
 RUN chown -R datafed:datafed ${DATAFED_DIR}
 
+RUN apt-get reinstall globus-connect-server54
+
 # Build as if a non root user
 USER datafed
 
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh
 
 RUN ${BUILD_DIR}/scripts/generate_authz_config.sh &&\
- cmake -S. -B build -DBUILD_REPO_SERVER=False -DBUILD_AUTHZ=True \
+ ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake -S. -B build -DBUILD_REPO_SERVER=False -DBUILD_AUTHZ=True \
+                    -DBUILD_AUTHZ_WITH_SYSLOG=False   \
                     -DBUILD_CORE_SERVER=False -DBUILD_WEB_SERVER=False \
                     -DBUILD_DOCS=False -DBUILD_PYTHON_CLIENT=False \
                     -DBUILD_FOXX=False && \
- cmake --build build
-RUN cmake --build build --target install
+ ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build
+RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build --target install
 
 COPY ./scripts/globus/setup_globus.sh ${BUILD_DIR}/scripts/globus/setup_globus.sh
 COPY ./scripts/globus/generate_repo_form.sh ${BUILD_DIR}/scripts/globus/generate_repo_form.sh
diff --git a/repository/docker/Dockerfile.gcs-base.ubuntu b/repository/docker/Dockerfile.gcs-base.ubuntu
deleted file mode 100644
index 3456a0906..000000000
--- a/repository/docker/Dockerfile.gcs-base.ubuntu
+++ /dev/null
@@ -1,16 +0,0 @@
-FROM ubuntu:focal as build
-
-ARG DATAFED_DIR="/datafed"
-ARG BUILD_DIR="/datafed/source"
-
-RUN mkdir -p ${BUILD_DIR}
-
-WORKDIR ${BUILD_DIR}
-
-COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/
-COPY ./scripts/dependency_versions.sh ${BUILD_DIR}/scripts/
-COPY ./scripts/install_gcs.sh ${BUILD_DIR}/scripts/
-
-RUN echo "#!/bin/bash\n\$@" > /usr/bin/sudo && chmod +x /usr/bin/sudo
-RUN ${BUILD_DIR}/scripts/generate_datafed.sh
-RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ${BUILD_DIR}/scripts/install_gcs.sh
diff --git a/repository/docker/Dockerfile.repo-base.ubuntu b/repository/docker/Dockerfile.repo-base.ubuntu
index 1ac05a7d7..838a14beb 100644
--- a/repository/docker/Dockerfile.repo-base.ubuntu
+++ b/repository/docker/Dockerfile.repo-base.ubuntu
@@ -7,7 +7,12 @@ RUN mkdir -p ${BUILD_DIR}
 RUN mkdir -p ${BUILD_DIR}/repository/server
 
 ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
-ENV PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:${PATH}"
+
+# NOTE - it does not look like using $PATH variable inside this definition
+# resolves correctly as such they system paths are included explicitly when
+# changes are made to ENV PATH
+ENV PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+
 WORKDIR ${BUILD_DIR}
 
 COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/
diff --git a/repository/docker/Dockerfile.repo.ubuntu b/repository/docker/Dockerfile.repo.ubuntu
index a980a41bd..f6359a137 100644
--- a/repository/docker/Dockerfile.repo.ubuntu
+++ b/repository/docker/Dockerfile.repo.ubuntu
@@ -10,10 +10,12 @@ ARG DATAFED_INSTALL_PATH="/datafed/install"
 ENV DATAFED_DIR="$DATAFED_DIR"
 ENV BUILD_DIR="$BUILD_DIR"
 ENV DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH"
+ENV DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
 
 RUN mkdir -p "${BUILD_DIR}"
 RUN mkdir -p "${BUILD_DIR}/repository/server"
 RUN mkdir -p "${DATAFED_INSTALL_PATH}/keys"
+RUN mkdir -p ${DATAFED_DEPENDENCIES_INSTALL_PATH}
 
 # This port is needed to communicate with the DataFed core server
 EXPOSE 7512
@@ -45,12 +47,12 @@ RUN chown -R datafed:datafed ${DATAFED_DIR}
 USER datafed
 
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh &&\
- cmake -S. -B build -DBUILD_REPO_SERVER=True -DBUILD_AUTHZ=False \
+ ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake -S. -B build -DBUILD_REPO_SERVER=True -DBUILD_AUTHZ=False \
                     -DBUILD_CORE_SERVER=False -DBUILD_WEB_SERVER=False \
                     -DBUILD_DOCS=False -DBUILD_PYTHON_CLIENT=False \
                     -DBUILD_FOXX=False &&\
- cmake --build build
-RUN cmake --build build --target install
+ ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build
+RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build --target install
 
 ENV LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH"
 
diff --git a/repository/docker/entrypoint_authz.sh b/repository/docker/entrypoint_authz.sh
index e91974b5f..0d5aaf93a 100755
--- a/repository/docker/entrypoint_authz.sh
+++ b/repository/docker/entrypoint_authz.sh
@@ -4,24 +4,156 @@
 # To run it just pass in /entrypoint.sh as an argument
 set -euf -o pipefail
 
-if [ -n "$UID" ]; then
-    usermod -u $UID datafed
-fi
-
-
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 PROJECT_ROOT=$(realpath ${SOURCE}/../..)
 # Translate datafed env variables to globus env variables
-export GLOBUS_CLIENT_ID="$DATAFED_GLOBUS_APP_ID"
+
+export DATAFED_GCS_COLLECTION_ROOT_PATH=/mnt/datafed
+# This env variables are needed for running globus-connect-server without
+# logging in
+
+export GCS_CLI_CLIENT_ID=$(jq -r .client < /opt/datafed/globus/client_cred.json)
+export GCS_CLI_CLIENT_SECRET=$(jq -r .secret < /opt/datafed/globus/client_cred.json)
+export GCS_CLI_ENDPOINT_ID=$(jq -r .client_id < /opt/datafed/globus/deployment-key.json)
+
+export DEPLOYMENT_KEY_PATH="/opt/datafed/globus/deployment-key.json"
+# These env variables are for running the gcs entrypoint file
+
+export GLOBUS_CLIENT_ID=$(jq -r .client < /opt/datafed/globus/client_cred.json)
+export GLOBUS_CLIENT_SECRET=$(jq -r .secret < /opt/datafed/globus/client_cred.json)
+export DEPLOYMENT_KEY=$(cat "$DEPLOYMENT_KEY_PATH"  )
+
+if [ -n "$UID" ]; then
+    echo "Switching datafed user to UID: ${UID}"
+    usermod -u $UID datafed
+    chown -R datafed:root ${DATAFED_GCS_COLLECTION_ROOT_PATH}
+fi
+
 "${PROJECT_ROOT}/scripts/generate_datafed.sh"
-"${BUILD_DIR}/scripts/globus/setup_globus.sh"
+
+source "${PROJECT_ROOT}/config/datafed.sh"
+
+# After datafed.sh has been run created
+"${PROJECT_ROOT}/scripts/generate_authz_config.sh"
+
+# Make sure paths exist
+mkdir -p "${DATAFED_INSTALL_PATH}/keys"
+mkdir -p "${DATAFED_DEFAULT_LOG_PATH}"
+
+# Copy configuration files
+cp "$PROJECT_ROOT/config/gsi-authz.conf" /etc/grid-security
+cp "$PROJECT_ROOT/config/datafed-authz.cfg" "${DATAFED_INSTALL_PATH}/authz"
+
+# Run node setup command we have to use the entrypoint file for this because
+# the globus-connect-server node setup command attempts to use systemctl which
+# is not installed in the container
+
+# Make sure files did not persist from a previous docker compose run
+# This code block is used for cleaning up files that might have been cached by
+# docker compose. These files are not always appropraitely removed when the 
+# Globus container exits.
+#
+# List of files to delete
+files=("/run/gcs_manager" "/run/gcs_manager/pid" "/var/run/apache2/apache2.pid"
+  "/var/run/httpd/httpd.pid" "/run/globus-gridftp-server.pid")
+# Loop over the list of files
+for file in "${files[@]}"; do
+  if [ -e "$file" ]; then
+    echo "Removing $file"
+    rm -rf "$file"
+  fi
+done
+link="/run/gcs_manager.sock"
+if [ -L "$link" ]; then
+  echo "Removing symbolic link $link"
+  rm "$link"
+fi
+
+# Run the GCS entrypoint file in the background
+/entrypoint.sh &
+
+# NOTE - can only change the tcp control port after setting up the end point
+# But will probably need a firewall exception for that port
+# globus-connect-server endpoint update  --gridftp-control-channel-port "443"
+
+# Make sure globus-gridftp-server is running before running setup_globus
+data=$(ps aux | awk '{print $11}' | grep apache2 || true)
+while [ -z "${data}" ]
+do
+        data=$(ps aux | awk '{print $11}' | grep apache2 || true)
+        echo "Waiting for apache2 to start running"
+        sleep 1
+done
+echo "apache2 service found to be running!"
+
+# Needs a few seconds
+while [ ! -f /run/globus-gridftp-server.pid ]
+do
+  echo "Waiting for globus-gridftp-server pid file to be created"
+  sleep 1
+done
+echo "globus-gridftp-server pid file found!"
+
+# Need to wait until the domain name is properly registered
+DATAFED_GCS_URL=$(jq -r .domain_name < /var/lib/globus-connect-server/info.json)
+
+HTTP_CODE=$("${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/curl" -s -o /dev/null -w "%{http_code}\n" -I "https://${DATAFED_GCS_URL}/api/info")
+echo "Waiting for domain name (https://${DATAFED_GCS_URL}) to be registered! Code: $HTTP_CODE"
+printf "\n"
+minutes=0
+while [ "$HTTP_CODE" != "200" ]
+do
+
+	EraseToEOL=$(tput el)
+
+	msg="Minutes $minutes "
+	for i in {1..12}
+	do
+			printf "%s" "${msg}"
+			msg='.'
+			sleep 5
+	
+			HTTP_CODE=$("${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/curl" -s -o /dev/null  -w "%{http_code}\n" -I "https://${DATAFED_GCS_URL}/api/info")
+			if [ "$HTTP_CODE" == "200" ]
+			then
+				break
+			fi	
+	done
+	printf "\r${EraseToEOL}"
+
+	minutes=$((minutes + 1))
+  HTTP_CODE=$("${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/curl" -s -o /dev/null -w "%{http_code}\n" -I "https://${DATAFED_GCS_URL}/api/info")
+done
+printf "\n"
 
 log_path="$DATAFED_DEFAULT_LOG_PATH"
 
 if [ ! -d "${log_path}" ]
 then
-  su -c "mkdir -p ${log_path}" datafed
+  mkdir -p "${log_path}"
 fi
 
-su datafed -c '"$@"' -- argv0 "$@"
+if [ ! -d "${DATAFED_GCS_COLLECTION_ROOT_PATH}" ]
+then
+  mkdir -p "$DATAFED_GCS_COLLECTION_ROOT_PATH"
+  chown -R datafed:root ${DATAFED_GCS_COLLECTION_ROOT_PATH}
+fi
+
+"${BUILD_DIR}/scripts/globus/setup_globus.sh"
+
+source "${DATAFED_PYTHON_ENV}/bin/activate"
+# Must be passed in directly
+GCS_CLI_ENDPOINT_ID="$GCS_CLI_ENDPOINT_ID" \
+DATAFED_GCS_URL="$DATAFED_GCS_URL" \
+GCS_CLI_CLIENT_ID="$GCS_CLI_CLIENT_ID" \
+GCS_CLI_CLIENT_SECRET="$GCS_CLI_CLIENT_SECRET" \
+DATAFED_REPO_USER="$DATAFED_REPO_USER" \
+  python3 "${BUILD_DIR}/scripts/globus/create_guest_collection.py"
+
+"${BUILD_DIR}/scripts/globus/generate_repo_form.sh"
+
+echo "Container is running."
+
+# Return to last file
+tail -f "${DATAFED_DEFAULT_LOG_PATH}/datafed-gsi-authz.log"
diff --git a/repository/docker/entrypoint_repo.sh b/repository/docker/entrypoint_repo.sh
index 746a3d0d8..23ea4c4e7 100755
--- a/repository/docker/entrypoint_repo.sh
+++ b/repository/docker/entrypoint_repo.sh
@@ -1,11 +1,6 @@
 #!/bin/bash
 
 set -euf -o pipefail
-
-if [ -n "$UID" ]; then
-    usermod -u $UID datafed
-fi
-
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 PROJECT_ROOT=$(realpath "${SOURCE}/../..")
@@ -14,6 +9,16 @@ PROJECT_ROOT=$(realpath "${SOURCE}/../..")
 "${PROJECT_ROOT}/scripts/generate_repo_config.sh"
 "${PROJECT_ROOT}/scripts/install_repo.sh"
 
+# This is only part of the solution the other part is running chown
+if [ -n "$UID" ]; then
+    echo "Switching datafed user to UID: ${UID}"
+    usermod -u "$UID" datafed
+    chown -R datafed:root "${PROJECT_ROOT}"
+    chown -R datafed:root /opt/datafed/repo/
+    chown -R datafed:root /mnt/datafed
+fi
+
+
 log_path="$DATAFED_DEFAULT_LOG_PATH"
 
 if [ ! -d "${log_path}" ]
@@ -21,4 +26,18 @@ then
   su -c "mkdir -p ${log_path}" datafed
 fi
 
-su datafed -c '"$@"' -- argv0 "$@"
+datafed_repo_exec=$(basename "$1")
+if [ "${datafed_repo_exec}" = "datafed-repo" ]
+then
+  # Send output to log file
+  # For this to work all commands must be passed in as a single string
+  su datafed -c '"$@"' -- argv0 "$@" 2>&1 | tee "$log_path/datafed-repo.log"
+else
+  echo "Not sending output to datafed-core.log"
+  # If not do not by default send to log file
+  su datafed -c '"$@"' -- argv0 "$@"
+fi
+
+# Allow the container to exist for a bit in case we need to jump in and look 
+# around
+sleep 10000
diff --git a/repository/gridftp/globus5/authz/source/AuthzWorker.cpp b/repository/gridftp/globus5/authz/source/AuthzWorker.cpp
index 86ddb120a..fcae2dd09 100644
--- a/repository/gridftp/globus5/authz/source/AuthzWorker.cpp
+++ b/repository/gridftp/globus5/authz/source/AuthzWorker.cpp
@@ -29,7 +29,6 @@
 using namespace std;
 using namespace SDMS::Anon;
 using namespace SDMS::Auth;
-// using namespace SDMS::authz::version;
 
 namespace {
 std::string randomAlphaNumericCode() {
@@ -141,8 +140,30 @@ class AuthzWorker {
       EXCEPT(1, "Path to data item is not within the collection");
     }
 
-    auto sanitized_path = local_path.substr(prefix.length());
-
+    /**
+     * TODO Separation of Concerns Repo vs Core services
+     *
+     * The following line is currenctly commented out because it will require
+     * some actual work and testing to get it working correctly.
+     *
+     * Ideally, when a DataFed repository is registered with the DataFed core
+     * services it should not need to know anything about the DataFed repository
+     * i.e. posix path to the actual collection. So when a user makes a request
+     * to do something on the DataFed managed endpoint the repo service should
+     * not need to send the full POSIX path back to the core service.
+     *
+     * This is not currently working but that is what the sanitized path should
+     * be.
+     * auto sanitized_path = local_path.substr(prefix.length());
+     *
+     * Changes will need to be made anywhere the path is used. And when a
+     * transfer document is created it should be provided with a path relative
+     * to the endpoint not to the absolute of the filesystem.
+     *
+     * Changes will also need to be made when entering information in the repo
+     * form if this is done.
+     **/
+    auto sanitized_path = local_path;
     std::unique_ptr<SDMS::ICredentials> m_sec_ctx;
 
     std::unordered_map<CredentialType, std::string> cred_options;
@@ -226,8 +247,11 @@ class AuthzWorker {
                               << client->address());
 
     auto response = client->receive(MessageType::GOOGLE_PROTOCOL_BUFFER);
-    log_context.correlation_id = std::get<std::string>(
-        response.message->get(MessageAttribute::CORRELATION_ID));
+    if (response.message) { // Make sure the message exists before we try to
+                            // access it
+      log_context.correlation_id = std::get<std::string>(
+          response.message->get(MessageAttribute::CORRELATION_ID));
+    }
     if (response.time_out) {
       std::string error_msg =
           "AuthWorker.cpp Core service did not respond within timeout.";
@@ -250,6 +274,12 @@ class AuthzWorker {
                             "communicating with the core service: "
                                 << response.error_msg);
     } else {
+
+      if (not response.message) {
+        DL_ERROR(log_context, "No error was reported and no time out occured "
+                              "but message is not defined.");
+      }
+
       auto payload =
           std::get<google::protobuf::Message *>(response.message->getPayload());
       Anon::NackReply *nack = dynamic_cast<Anon::NackReply *>(payload);
@@ -303,9 +333,21 @@ const char *getReleaseVersion() {
 // The same
 int checkAuthorization(char *client_id, char *object, char *action,
                        struct Config *config) {
+#if defined(DONT_USE_SYSLOG)
+  SDMS::global_logger.setSysLog(false);
+#else
   SDMS::global_logger.setSysLog(true);
-  SDMS::global_logger.setLevel(SDMS::LogLevel::TRACE);
+#endif
+  SDMS::global_logger.setLevel(SDMS::LogLevel::INFO);
   SDMS::global_logger.addStream(std::cerr);
+  auto log_path_authz = std::string(config->log_path);
+  if (log_path_authz.length() > 0) {
+    // Append to the existing path because we don't want the C++ and C code
+    // trying to write to the same file
+    log_path_authz.append("_authz");
+    std::ofstream log_file_worker(log_path_authz);
+    SDMS::global_logger.addStream(log_file_worker);
+  }
 
   SDMS::LogContext log_context;
   log_context.thread_name = "authz_check";
diff --git a/repository/gridftp/globus5/authz/source/CMakeLists.txt b/repository/gridftp/globus5/authz/source/CMakeLists.txt
index 076559f5d..309d80b04 100644
--- a/repository/gridftp/globus5/authz/source/CMakeLists.txt
+++ b/repository/gridftp/globus5/authz/source/CMakeLists.txt
@@ -6,10 +6,20 @@ configure_file(
   @ONLY)
 file( GLOB Sources "*.cpp" "*.c" )
 add_library( datafed-authz SHARED ${Sources} )
+
+target_compile_options(datafed-authz PRIVATE -fPIC)
 SET_TARGET_PROPERTIES( datafed-authz PROPERTIES LINKER_LANGUAGE CXX )
 set_target_properties(datafed-authz PROPERTIES POSITION_INDEPENDENT_CODE ON SOVERSION ${DATAFED_AUTHZ_MAJOR} VERSION ${DATAFED_AUTHZ_MAJOR}.${DATAFED_AUTHZ_MINOR}.${DATAFED_AUTHZ_PATCH})
 add_dependencies( datafed-authz common)
+if ( NOT BUILD_AUTHZ_WITH_SYSLOG )
+  target_compile_definitions(datafed-authz PRIVATE DONT_USE_SYSLOG)
+endif()
 
-target_link_libraries( datafed-authz common Threads::Threads libzmq-static datafed-protobuf ${Boost_LIBRARIES} )
-
+if(BUILD_SHARED_LIBS)
+  target_link_libraries( datafed-authz common Threads::Threads libzmq
+    datafed-protobuf ${DATAFED_BOOST_LIBRARIES} )
+else()
+  target_link_libraries( datafed-authz common Threads::Threads libzmq-static
+    datafed-protobuf ${DATAFED_BOOST_LIBRARIES} )
+endif()
 target_include_directories( datafed-authz PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} )
diff --git a/repository/gridftp/globus5/authz/source/libauthz.c b/repository/gridftp/globus5/authz/source/libauthz.c
index 8bea6e112..629d64178 100644
--- a/repository/gridftp/globus5/authz/source/libauthz.c
+++ b/repository/gridftp/globus5/authz/source/libauthz.c
@@ -1,6 +1,7 @@
 
 // Local private includes
 #include "AuthzWorker.h"
+#include "Config.h"
 
 // Globus third party includes
 #include <globus_error_hierarchy.h>
@@ -10,9 +11,75 @@
 // Standard includes
 #include <stdarg.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+
+// Define LOG_LEVEL and USE_SYSLOG
+#define LOG_LEVEL 1
+
+#ifndef DONT_USE_SYSLOG
+#define DONT_USE_SYSLOG
+#endif
+
+// Define logging macros
+#if defined(DONT_USE_SYSLOG)
+FILE *log_file = NULL;
+bool write_to_file = false;
+#define AUTHZ_LOG_DEBUG(fmt, ...)                                              \
+  do {                                                                         \
+    if (LOG_LEVEL <= 1)                                                        \
+      fprintf(stderr, "[DEBUG] " fmt "", ##__VA_ARGS__);                       \
+  } while (0);                                                                 \
+  do {                                                                         \
+    if (LOG_LEVEL <= 1 && write_to_file)                                       \
+      fprintf(log_file, "[DEBUG] " fmt "", ##__VA_ARGS__);                     \
+  } while (0)
+#define AUTHZ_LOG_INFO(fmt, ...)                                               \
+  do {                                                                         \
+    if (LOG_LEVEL <= 2)                                                        \
+      fprintf(stderr, "[INFO] " fmt "", ##__VA_ARGS__);                        \
+  } while (0);                                                                 \
+  do {                                                                         \
+    if (LOG_LEVEL <= 2 && write_to_file)                                       \
+      fprintf(log_file, "[INFO] " fmt "", ##__VA_ARGS__);                      \
+  } while (0)
+#define AUTHZ_LOG_ERROR(fmt, ...)                                              \
+  do {                                                                         \
+    if (LOG_LEVEL <= 3)                                                        \
+      fprintf(stderr, "[ERROR] " fmt "", ##__VA_ARGS__);                       \
+    if (LOG_LEVEL <= 3 && write_to_file)                                       \
+      fprintf(log_file, "[ERROR] " fmt "", ##__VA_ARGS__);                     \
+  } while (0)
+#define AUTHZ_LOG_INIT(file_path)                                              \
+  log_file = fopen(file_path, "a");                                            \
+  if (log_file != NULL) {                                                      \
+    write_to_file = true;                                                      \
+  }
+#define AUTHZ_LOG_CLOSE()                                                      \
+  if (log_file != NULL) {                                                      \
+    fclose(log_file);                                                          \
+  }
+#else
 #include <syslog.h>
+#define AUTHZ_LOG_DEBUG(fmt, ...)                                              \
+  do {                                                                         \
+    if (LOG_LEVEL <= 1)                                                        \
+      syslog(LOG_DEBUG, "[DEBUG] " fmt, ##__VA_ARGS__);                        \
+  } while (0)
+#define AUTHZ_LOG_INFO(fmt, ...)                                               \
+  do {                                                                         \
+    if (LOG_LEVEL <= 2)                                                        \
+      syslog(LOG_INFO, "[INFO] " fmt, ##__VA_ARGS__);                          \
+  } while (0)
+#define AUTHZ_LOG_ERROR(fmt, ...)                                              \
+  do {                                                                         \
+    if (LOG_LEVEL <= 3)                                                        \
+      syslog(LOG_ERR, "[ERROR] " fmt, ##__VA_ARGS__);                          \
+  } while (0)
+#define AUTHZ_LOG_INIT(file_path) openlog("gsi_authz", 0, LOG_AUTH);
+#define AUTHZ_LOG_CLOSE() closelog();
+#endif
 
 typedef void *globus_gsi_authz_handle_t;
 typedef void (*globus_gsi_authz_cb_t)(void *callback_arg,
@@ -149,14 +216,14 @@ bool setConfigVal(const char *a_label, char *a_dest, char *a_src,
   size_t len = strlen(a_src);
 
   if (len == 0) {
-    syslog(LOG_ERR, "DataFed - '%s' value not set.", a_label);
+    AUTHZ_LOG_ERROR("DataFed - '%s' value not set.\n", a_label);
     return true;
   }
 
   if (len > a_max_len) {
-    syslog(LOG_ERR,
-           "DataFed - '%s' value too long in authz config file (max %lu).",
-           a_label, a_max_len);
+    AUTHZ_LOG_ERROR(
+        "DataFed - '%s' value too long in authz config file (max %zu).\n",
+        a_label, a_max_len);
     return true;
   }
 
@@ -169,12 +236,12 @@ bool loadKeyFile(char *a_dest, char *a_filename) {
   FILE *inf = fopen(a_filename, "r");
 
   if (!inf) {
-    syslog(LOG_ERR, "DataFed - Could not open key file: %s", a_filename);
+    AUTHZ_LOG_ERROR("DataFed - Could not open key file: %s\n", a_filename);
     return true;
   }
 
   if (!fgets(a_dest, MAX_KEY_LEN, inf)) {
-    syslog(LOG_ERR, "DataFed - Error reading key from file: %s", a_filename);
+    AUTHZ_LOG_ERROR("DataFed - Error reading key from file: %s\n", a_filename);
     fclose(inf);
     return true;
   }
@@ -197,13 +264,13 @@ bool loadConfig() {
     // If env variable is not set check default location
     inf = fopen("/opt/datafed/authz/datafed-authz.cfg", "r");
     if (!inf) {
-      syslog(LOG_ERR, "DataFed - DATAFED_AUTHZ_CFG_FILE env variable not set, "
+      AUTHZ_LOG_ERROR("DataFed - DATAFED_AUTHZ_CFG_FILE env variable not set, "
                       "and datafed-authz.cfg is not located in default "
-                      "location /opt/datafed/authz");
+                      "location /opt/datafed/authz\n");
       return true;
     }
   } else {
-    syslog(LOG_INFO, "DataFed - Loading authz config file: %s", cfg_file);
+    AUTHZ_LOG_INFO("DataFed - Loading authz config file: %s\n", cfg_file);
     inf = fopen(cfg_file, "r");
   }
   if (inf) {
@@ -229,8 +296,8 @@ bool loadConfig() {
       // Content is formatted as "key=value" (no spaces)
       val = strchr(buf, '=');
       if (!val) {
-        syslog(LOG_ERR,
-               "DataFed - Syntax error in authz config file at line %i.", lc);
+        AUTHZ_LOG_ERROR(
+            "DataFed - Syntax error in authz config file at line %i.\n", lc);
         return true;
       } else {
         *val = 0;
@@ -239,6 +306,7 @@ bool loadConfig() {
 
       // Default values
       g_config.timeout = 10000;
+      g_config.log_path[0] = '\0';
 
       if (strcmp(buf, "repo_id") == 0)
         err = setConfigVal("repo_id", g_config.repo_id, val, MAX_ID_LEN);
@@ -247,9 +315,10 @@ bool loadConfig() {
                            MAX_ADDR_LEN);
       else if (strcmp(buf, "user") == 0)
         err = setConfigVal("user", g_config.user, val, MAX_ID_LEN);
-      else if (strcmp(buf, "log_path") == 0)
+      else if (strcmp(buf, "log_path") == 0) {
         err = setConfigVal("log_path", g_config.log_path, val, MAX_PATH_LEN);
-      else if (strcmp(buf, "test_path") == 0)
+        AUTHZ_LOG_INIT(g_config.log_path);
+      } else if (strcmp(buf, "test_path") == 0)
         err = setConfigVal("test_path", g_config.test_path, val, MAX_PATH_LEN);
       else if (strcmp(buf, "globus-collection-path") == 0)
         err = setConfigVal("globus-collection-path",
@@ -264,9 +333,9 @@ bool loadConfig() {
         g_config.timeout = atoi(val);
       else {
         err = true;
-        syslog(LOG_ERR,
-               "DataFed - Invalid key, '%s', in authz config file at line %i.",
-               buf, lc);
+        AUTHZ_LOG_ERROR(
+            "DataFed - Invalid key, '%s', in authz config file at line %i.\n",
+            buf, lc);
       }
 
       if (err) {
@@ -295,25 +364,40 @@ bool loadConfig() {
     if (g_config.server_key[0] == 0)
       strcat(miss, " server_key");
 
+    AUTHZ_LOG_INFO("DataFed Authz module started, version %s\n", getVersion());
+    AUTHZ_LOG_INFO("                         API, version %s\n",
+                   getAPIVersion());
+    AUTHZ_LOG_INFO("                     Release, version %s\n",
+                   getReleaseVersion());
+
     if (miss[0] != 0) {
-      syslog(LOG_ERR, "DataFed - Missing required authz config items:%s", miss);
+      AUTHZ_LOG_ERROR("DataFed - Missing required authz config items:%s\n",
+                      miss);
       return true;
     }
   } else {
-    syslog(LOG_ERR, "DataFed - Could not open authz config file.");
+
+    AUTHZ_LOG_INFO("DataFed Authz module started, version %s\n", getVersion());
+    AUTHZ_LOG_INFO("                         API, version %s\n",
+                   getAPIVersion());
+    AUTHZ_LOG_INFO("                     Release, version %s\n",
+                   getReleaseVersion());
+    AUTHZ_LOG_ERROR("DataFed - Could not open authz config file.\n");
+
     return true;
   }
 
+  AUTHZ_LOG_INFO("DataFed Authz module started, version %s\n", getVersion());
+  AUTHZ_LOG_INFO("                         API, version %s\n", getAPIVersion());
+  AUTHZ_LOG_INFO("                     Release, version %s\n",
+                 getReleaseVersion());
+
   return false;
 }
 
 // The same
 globus_result_t gsi_authz_init() {
-  openlog("gsi_authz", 0, LOG_AUTH);
-  syslog(LOG_INFO, "DataFed Authz module started, version %s", getVersion());
-  syslog(LOG_INFO, "                         API, version %s", getAPIVersion());
-  syslog(LOG_INFO, "                     Release, version %s",
-         getReleaseVersion());
+  // openlog("gsi_authz", 0, LOG_AUTH);
   memset(g_active_contexts, 0, sizeof(g_active_contexts));
 
   // This line is different
@@ -325,14 +409,14 @@ globus_result_t gsi_authz_init() {
 
 // The same
 globus_result_t gsi_authz_destroy() {
-  syslog(LOG_DEBUG, "gsi_authz_destroy");
-
+  AUTHZ_LOG_DEBUG("gsi_authz_destroy\n");
+  AUTHZ_LOG_CLOSE();
   return 0;
 }
 
 // The same
 globus_result_t gsi_authz_handle_init(va_list ap) {
-  syslog(LOG_DEBUG, "gsi_authz_handle_init");
+  AUTHZ_LOG_DEBUG("gsi_authz_handle_init\n");
 
   globus_result_t result = GLOBUS_FAILURE;
   globus_gsi_authz_handle_t *handle = va_arg(ap, globus_gsi_authz_handle_t *);
@@ -348,9 +432,10 @@ globus_result_t gsi_authz_handle_init(va_list ap) {
     if (setContext(*handle, context) == false)
       result = GLOBUS_SUCCESS;
     else
-      syslog(LOG_ERR, "gsi_authz_handle_init out of handle context space");
+      AUTHZ_LOG_ERROR("gsi_authz_handle_init out of handle context space\n");
   } else {
-    syslog(LOG_ERR, "gsi_authz_handle_init context handle already initialized");
+    AUTHZ_LOG_DEBUG(
+        "gsi_authz_handle_init context handle already initialized\n");
   }
 
   callback(callback_arg, callback_arg, result);
@@ -360,7 +445,7 @@ globus_result_t gsi_authz_handle_init(va_list ap) {
 
 // The same
 globus_result_t gsi_authz_handle_destroy(va_list ap) {
-  syslog(LOG_INFO, "gsi_authz_handle_destroy");
+  AUTHZ_LOG_INFO("gsi_authz_handle_destroy\n");
 
   globus_result_t result = GLOBUS_FAILURE;
   globus_gsi_authz_handle_t handle = va_arg(ap, globus_gsi_authz_handle_t);
@@ -373,7 +458,7 @@ globus_result_t gsi_authz_handle_destroy(va_list ap) {
   if (clearContext(handle) == false) {
     result = GLOBUS_SUCCESS;
   } else {
-    syslog(LOG_ERR, "gsi_authz_handle_destroy context handle lookup FAILED");
+    AUTHZ_LOG_ERROR("gsi_authz_handle_destroy context handle lookup FAILED\n");
   }
 
   callback(callback_arg, handle, result);
@@ -382,7 +467,7 @@ globus_result_t gsi_authz_handle_destroy(va_list ap) {
 }
 
 globus_result_t gsi_authz_authorize_async(va_list ap) {
-  syslog(LOG_INFO, "gsi_authz_authorize_async");
+  AUTHZ_LOG_INFO("gsi_authz_authorize_async\n");
 
   globus_result_t result = GLOBUS_FAILURE;
   globus_gsi_authz_handle_t handle = va_arg(ap, globus_gsi_authz_handle_t);
@@ -393,17 +478,27 @@ globus_result_t gsi_authz_authorize_async(va_list ap) {
   // void *                      authz_system_state  = va_arg(ap, void *);
 
   char *callout_ids1 = getenv("GLOBUS_GRIDFTP_GUEST_IDENTITY_IDS");
-
-  syslog(LOG_DEBUG, "libauthz.c GLOBUS_GRIDFTP_GUEST_IDENTITY_IDS: %s\n",
-         callout_ids1);
+  char *callout_username_mapped1 = getenv("GLOBUS_GRIDFTP_MAPPED_USERNAME");
+  char *callout_id_mapped1 = getenv("GLOBUS_GRIDFTP_MAPPED_IDENTITY_ID");
+
+  AUTHZ_LOG_DEBUG("libauthz.c GLOBUS_GRIDFTP_GUEST_IDENTITY_IDS: %s\n",
+                  callout_ids1);
+  AUTHZ_LOG_DEBUG("libauthz.c GLOBUS_GRIDFTP_MAPPED_USERNAME: %s\n",
+                  callout_username_mapped1);
+  AUTHZ_LOG_DEBUG("libauthz.c GLOBUS_GRIDFTP_MAPPED_IDENTITY_ID: %s\n",
+                  callout_id_mapped1);
+  AUTHZ_LOG_INFO("Allowed collection path: %s, action: %s, object is %s\n",
+                 g_config.globus_collection_path, action, object);
   if (strcmp(action, "lookup") == 0 || strcmp(action, "chdir") == 0) {
+    AUTHZ_LOG_INFO("Allowed collection path: %s, action: %s, object is %s\n",
+                   g_config.globus_collection_path, action, object);
     result = GLOBUS_SUCCESS;
     callback(callback_arg, handle, result);
     return result;
   }
 
-  syslog(LOG_ERR, "gsi_authz_authorize_async, handle: %p, act: %s, obj: %s",
-         handle, action, object);
+  AUTHZ_LOG_ERROR("gsi_authz_authorize_async, handle: %p, act: %s, obj: %s\n",
+                  handle, action, object);
 
   OM_uint32 min_stat;
   gss_name_t client = GSS_C_NO_NAME;
@@ -425,8 +520,8 @@ globus_result_t gsi_authz_authorize_async(va_list ap) {
         maj_stat =
             gss_display_name(&min_stat, target, &target_buf, &target_type);
         if (maj_stat == GSS_S_COMPLETE) {
-          syslog(LOG_INFO, "Auth client: %s, file: %s, action: %s",
-                 (char *)client_buf.value, object, action);
+          AUTHZ_LOG_INFO("Auth client: %s, file: %s, action: %s\n",
+                         (char *)client_buf.value, object, action);
 
 // Testing hack
 #if 0
@@ -442,8 +537,8 @@ globus_result_t gsi_authz_authorize_async(va_list ap) {
           // Consortium/OU=Globus Connect User/CN=", 52 ) != 0 )
           if (strncmp((char *)client_buf.value,
                       "/C=US/O=Globus Consortium/OU=Globus", 35) != 0) {
-            syslog(LOG_ERR, "Invalid certificate subject prefix: %s",
-                   (char *)client_buf.value);
+            AUTHZ_LOG_ERROR("Invalid certificate subject prefix: %s\n",
+                            (char *)client_buf.value);
           } else {
             /* Note: For some reason, globus will provide the CN as either a
              * UUID that is linked to the client's account and encoded in
@@ -457,37 +552,45 @@ globus_result_t gsi_authz_authorize_async(va_list ap) {
             // TODO Should check client uuid str len to make sure it won't
             // overflow
             if (strncmp((char *)client_buf.value + 52, "u_", 2) == 0) {
-              syslog(LOG_INFO, "Globus user prefix detected, decode UUID");
+              AUTHZ_LOG_INFO("Globus user prefix detected, decode UUID\n");
 
               client_id = malloc(40);
 
               if (!decodeUUID((char *)client_buf.value + 54, client_id)) {
-                syslog(LOG_ERR, "Failed to decode subject UUID: %s",
-                       (char *)client_buf.value + 54);
+                AUTHZ_LOG_ERROR("Failed to decode subject UUID: %s\n",
+                                (char *)client_buf.value + 54);
                 free(client_id);
                 client_id = 0;
               }
             } else {
-              syslog(LOG_INFO, "Using client CN for authz");
+              AUTHZ_LOG_INFO("Using client CN for authz\n");
 
               // Find "/CN=" in cert DN
               const char *cn = strstr(client_buf.value, "/CN=");
-              if (!cn)
-                syslog(LOG_ERR, "Common Name not found in client DN");
-              else
+              if (!cn) {
+                AUTHZ_LOG_ERROR("Common Name not found in client DN\n");
+              } else {
                 client_id = strdup((char *)cn + 4);
+              }
 
               char *callout_ids = getenv("GLOBUS_GRIDFTP_GUEST_IDENTITY_IDS");
+              char *callout_id_mapped =
+                  getenv("GLOBUS_GRIDFTP_MAPPED_IDENTITY_ID");
 
               if (callout_ids != NULL) {
-                syslog(LOG_DEBUG,
-                       "libauthz.c GLOBUS_GRIDFTP_GUEST_IDENTITY_IDS: %s\n",
-                       callout_ids);
+                AUTHZ_LOG_DEBUG(
+                    "libauthz.c GLOBUS_GRIDFTP_GUEST_IDENTITY_IDS: %s\n",
+                    callout_ids);
                 client_id = strdup(callout_ids);
-                syslog(LOG_INFO, "libauthz.c client_id(s): %s\n", client_id);
+                AUTHZ_LOG_INFO("libauthz.c client_id(s): %s\n", client_id);
+              } else if (callout_id_mapped != NULL) {
+                AUTHZ_LOG_DEBUG(
+                    "libauthz.c GLOBUS_GRIDFTP_MAPPED_IDENTITY_ID: %s\n",
+                    callout_id_mapped);
+                client_id = strdup(callout_id_mapped);
               } else {
-                syslog(LOG_ERR,
-                       "libauthz.c GLOBUS_GRIDFTP_GUEST_IDENTITY_IDS.\n");
+                AUTHZ_LOG_ERROR(
+                    "libauthz.c GLOBUS_GRIDFTP_GUEST_IDENTITY_IDS.\n");
               }
             }
 
@@ -497,10 +600,10 @@ globus_result_t gsi_authz_authorize_async(va_list ap) {
                 result = GLOBUS_SUCCESS;
               } else {
 
-                syslog(LOG_INFO,
-                       "libauthz.c Auth client_id: %s, file: %s, action: %s",
-                       client_id, object, action);
-                syslog(LOG_INFO, "libauthz.c checkAuthorization FAIL.\n");
+                AUTHZ_LOG_INFO(
+                    "libauthz.c Auth client_id: %s, file: %s, action: %s\n",
+                    client_id, object, action);
+                AUTHZ_LOG_INFO("libauthz.c checkAuthorization FAIL.\n");
               }
 
               free(client_id);
@@ -509,51 +612,51 @@ globus_result_t gsi_authz_authorize_async(va_list ap) {
 
           gss_release_buffer(&min_stat, &target_buf);
         } else {
-          syslog(LOG_ERR, "gss_display_name target FAILED, maj: %d, min: %d",
-                 maj_stat, min_stat);
+          AUTHZ_LOG_ERROR("gss_display_name target FAILED, maj: %d, min: %d\n",
+                          maj_stat, min_stat);
         }
 
         gss_release_buffer(&min_stat, &client_buf);
       } else {
-        syslog(LOG_ERR, "gss_display_name source FAILED, maj: %d, min: %d",
-               maj_stat, min_stat);
+        AUTHZ_LOG_ERROR("gss_display_name source FAILED, maj: %d, min: %d\n",
+                        maj_stat, min_stat);
       }
     } else {
-      syslog(LOG_ERR, "gss_inquire_context FAILED, maj: %d, min: %d", maj_stat,
-             min_stat);
+      AUTHZ_LOG_ERROR("gss_inquire_context FAILED, maj: %d, min: %d\n",
+                      maj_stat, min_stat);
     }
   } else {
-    syslog(LOG_ERR, "context handle lookup FAILED");
+    AUTHZ_LOG_ERROR("context handle lookup FAILED\n");
   }
 
   if (result != GLOBUS_SUCCESS) {
     globus_object_t *error = globus_error_construct_no_authentication(0, 0);
-    syslog(LOG_INFO, "Authz: FAILED");
+    AUTHZ_LOG_INFO("Authz: FAILED\n");
     result = globus_error_put(error);
   } else {
-    syslog(LOG_ERR, "Authz: PASSED");
+    AUTHZ_LOG_DEBUG("Authz: PASSED\n");
     callback(callback_arg, handle, result);
   }
 
-  syslog(LOG_ERR, "Authz returning");
+  AUTHZ_LOG_ERROR("Authz returning\n");
 
   return result;
 }
 
 // The same
 globus_result_t gsi_authz_cancel() {
-  syslog(LOG_DEBUG, "gsi_authz_cancel\n");
+  AUTHZ_LOG_DEBUG("gsi_authz_cancel\n");
   return 0;
 }
 
 // The same
 globus_result_t gsi_authz_identify() {
-  syslog(LOG_DEBUG, "gsi_authz_identify\n");
+  AUTHZ_LOG_DEBUG("gsi_authz_identify\n");
   return 0;
 }
 
 globus_result_t gsi_map_user(va_list Ap) {
-  syslog(LOG_DEBUG, "gsi_map_user");
+  AUTHZ_LOG_DEBUG("gsi_map_user\n");
 
   char *service = NULL;
   char *desired_identity = NULL;
diff --git a/repository/server/CMakeLists.txt b/repository/server/CMakeLists.txt
index cf0691ff6..3ed87318d 100644
--- a/repository/server/CMakeLists.txt
+++ b/repository/server/CMakeLists.txt
@@ -9,6 +9,11 @@ configure_file(
 
 add_executable( datafed-repo ${Sources} )
 add_dependencies( datafed-repo common )
-target_link_libraries( datafed-repo common Threads::Threads libzmq-static datafed-protobuf ${Boost_LIBRARIES} )
+if(BUILD_SHARED_LIBS)
+  target_link_libraries( datafed-repo common Threads::Threads libzmq datafed-protobuf ${DATAFED_BOOST_LIBRARIES} )
+else()
+  target_link_libraries( datafed-repo common Threads::Threads libzmq-static datafed-protobuf ${DATAFED_BOOST_LIBRARIES} )
+endif()
+ 
 
 target_include_directories( datafed-repo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} )
diff --git a/repository/server/RepoServer.cpp b/repository/server/RepoServer.cpp
index 69cbf49dc..c58acb23a 100644
--- a/repository/server/RepoServer.cpp
+++ b/repository/server/RepoServer.cpp
@@ -101,9 +101,9 @@ void Server::run() {
 }
 
 void Server::checkServerVersion() {
-  DL_INFO(m_log_context, "Checking core server connection and version");
+  DL_INFO(m_log_context, "Checking core server connection and version at "
+                             << m_config.core_server);
 
-  auto msg = std::make_unique<VersionRequest>(); //      msg;
   // Generate random security keys for anon version request to core server
   KeyGenerator generator;
   auto local_keys =
@@ -147,7 +147,21 @@ void Server::checkServerVersion() {
 
   MessageFactory msg_factory;
 
-  for (int i = 0; i < 10; i++) {
+  size_t attempt = 0;
+
+  /**
+   * Here the repo server will continually try to make a connection with the
+   * core services. This was changed from making a fixed number of attempts.
+   * The reasoning was that doing so would make the repo service dependent
+   * on the core actually running. By continually looping the coupling between
+   * the core services and the repo service is reduced.
+   **/
+  while (true) {
+    ++attempt;
+    DL_INFO(m_log_context,
+            "Attempt " << attempt << " to initialize communication "
+                       << " with core server at " << m_config.core_server);
+    auto msg = std::make_unique<VersionRequest>();
     auto message = msg_factory.create(MessageType::GOOGLE_PROTOCOL_BUFFER);
     message->setPayload(std::move(msg));
     message->set(MessageAttribute::KEY,
@@ -159,14 +173,19 @@ void Server::checkServerVersion() {
     client->send(*message);
 
     auto response = client->receive(MessageType::GOOGLE_PROTOCOL_BUFFER);
-    msg_log_context.correlation_id = std::get<std::string>(
-        response.message->get(MessageAttribute::CORRELATION_ID));
 
     if (response.time_out) {
       DL_ERROR(msg_log_context,
                "Timeout waiting for response from core server: "
                    << m_config.core_server);
+    } else if (response.error) {
+      DL_ERROR(msg_log_context, "Error encountered waiting for core server: "
+                                    << m_config.core_server << " msg "
+                                    << response.error_msg);
     } else {
+
+      msg_log_context.correlation_id = std::get<std::string>(
+          response.message->get(MessageAttribute::CORRELATION_ID));
       auto payload =
           std::get<google::protobuf::Message *>(response.message->getPayload());
       VersionReply *ver_reply = dynamic_cast<VersionReply *>(payload);
diff --git a/repository/server/RequestWorker.cpp b/repository/server/RequestWorker.cpp
index 5ee08f0cd..49bb9186e 100644
--- a/repository/server/RequestWorker.cpp
+++ b/repository/server/RequestWorker.cpp
@@ -36,6 +36,125 @@ namespace Repo {
 
 map<uint16_t, RequestWorker::msg_fun_t> RequestWorker::m_msg_handlers;
 
+bool RequestWorker::prefixesEqual(const std::string &str1,
+                                  const std::string &str2,
+                                  size_t length) const {
+  // Check if either string is shorter than the specified length
+  if (str1.length() < length || str2.length() < length) {
+    return false;
+  }
+
+  // Use std::equal to compare the prefixes
+  return std::equal(str1.begin(), str1.begin() + length, str2.begin());
+}
+
+std::string RequestWorker::createSanitizedPath(const std::string &path) const {
+
+  string sanitized_request_path = path;
+  while (!sanitized_request_path.empty()) {
+    if (sanitized_request_path.back() == '/') {
+      sanitized_request_path.pop_back();
+    } else {
+      break;
+    }
+  }
+  /**
+   * When item is provided it is allowed to be provided as
+   *
+   * path = '/relative_path/to/file'
+   *
+   * or
+   *
+   * path = 'globus_collection_path/relative_path/to/file
+   *
+   * If a relative path is applied then the config settings are used and
+   * the globus collection path is prepended. Otherwise if the the absolute
+   * path is provided then no change is made.
+   *
+   * E.g.
+   *
+   * Assuming
+   * path = "/datafed-home"
+   * globus_collection_path = "/mnt/datafed"
+   *
+   * Then
+   * local_path = "/mnt/datafed/datafed-home"
+   *
+   * Else if
+   *
+   * path = "/mnt/datafed/datafed-home"
+   * globus_collection_path = "/mnt/datafed"
+   *
+   * Then it won't prepend
+   * local_path = "/mnt/datafed/datafed-home"
+   **/
+  string local_path;
+  if (prefixesEqual(sanitized_request_path, m_config.globus_collection_path,
+                    m_config.globus_collection_path.length())) {
+
+    /**
+     * If both paths exist throw an error indicating there is ambiguity. I.e.
+     *
+     * If both
+     * /datafed/datafed/file1.txt
+     * /datafed/file1.txt
+     *
+     * exist and the variables are
+     *
+     * globus_collectin_path = "/datafed"
+     * path = "/datafed/file1.txt"
+     *
+     * First off something with the configuration is likely off and secondly
+     * It's impossible to determine which file is correct.
+     *
+     **/
+    std::string local_path_1 = m_config.globus_collection_path;
+    std::string local_path_2 = "";
+    if (sanitized_request_path.front() != '/') {
+      if (local_path_1.back() != '/') {
+        local_path_1 += "/" + sanitized_request_path;
+      } else {
+        local_path_1 += sanitized_request_path;
+      }
+      local_path_2 += "/" + sanitized_request_path;
+    } else {
+      local_path_1 += sanitized_request_path;
+      local_path_2 += sanitized_request_path;
+    }
+
+    boost::filesystem::path data_path_1(local_path_1); // long
+    boost::filesystem::path data_path_2(local_path_2); // shorter
+
+    if (boost::filesystem::exists(data_path_1) and
+        boost::filesystem::exists(data_path_2)) {
+      // If they are the exact same then ignore else throw an error
+      //
+      // i.e. if globus_collection_path is /
+      if (local_path_1 != local_path_2) {
+        DL_ERROR(m_log_context,
+                 "RequestWorker::createSanitizedPath, exception: something is "
+                 "really wrong both path "
+                     << local_path_1 << " and path " << local_path_2
+                     << " exists, which makes unambiguously determining the "
+                        "correct paths impossible.");
+        throw;
+      }
+    }
+
+    // Use the shorter path
+    local_path = local_path_2;
+  } else {
+    local_path = m_config.globus_collection_path;
+    if (sanitized_request_path.front() != '/') {
+      local_path += "/" + sanitized_request_path;
+    } else {
+      local_path += sanitized_request_path;
+    }
+  }
+
+  return local_path;
+}
+
 RequestWorker::RequestWorker(size_t a_tid, LogContext log_context)
     : m_config(Config::getInstance()), m_tid(a_tid), m_run(true),
       m_log_context(log_context) {
@@ -146,33 +265,38 @@ void RequestWorker::workerThread(LogContext log_context) {
 
       DL_TRACE(message_log_context, "Checking timeouts: " << response.time_out);
       if (response.time_out == false and response.error == false) {
-        // May not have a correlation id if the message timed out
-        DL_TRACE(log_context, "Getting correlation_id.");
-        if (response.message->exists(MessageAttribute::CORRELATION_ID)) {
-          message_log_context.correlation_id = std::get<std::string>(
-              response.message->get(MessageAttribute::CORRELATION_ID));
-        }
-
-        IMessage &message = *response.message;
-        uint16_t msg_type = std::get<uint16_t>(
-            message.get(constants::message::google::MSG_TYPE));
-
-        DL_TRACE(message_log_context, "Received msg of type: " << msg_type);
-
-        if (m_msg_handlers.count(msg_type)) {
-          map<uint16_t, msg_fun_t>::iterator handler =
-              m_msg_handlers.find(msg_type);
-          DL_TRACE(message_log_context, "Calling handler");
-
-          auto send_message =
-              (this->*handler->second)(std::move(response.message));
-
-          client->send(*(send_message));
-
-          DL_TRACE(message_log_context, "Reply sent.");
+        if (not response.message) {
+          DL_ERROR(log_context, "Error: No error or timeout occurred but the"
+                                    << " message does not exist.");
         } else {
-          DL_ERROR(message_log_context,
-                   "Received unregistered msg type: " << msg_type);
+          // May not have a correlation id if the message timed out
+          DL_TRACE(log_context, "Getting correlation_id.");
+          if (response.message->exists(MessageAttribute::CORRELATION_ID)) {
+            message_log_context.correlation_id = std::get<std::string>(
+                response.message->get(MessageAttribute::CORRELATION_ID));
+          }
+
+          IMessage &message = *response.message;
+          uint16_t msg_type = std::get<uint16_t>(
+              message.get(constants::message::google::MSG_TYPE));
+
+          DL_TRACE(message_log_context, "Received msg of type: " << msg_type);
+
+          if (m_msg_handlers.count(msg_type)) {
+            map<uint16_t, msg_fun_t>::iterator handler =
+                m_msg_handlers.find(msg_type);
+            DL_TRACE(message_log_context, "Calling handler");
+
+            auto send_message =
+                (this->*handler->second)(std::move(response.message));
+
+            client->send(*(send_message));
+
+            DL_TRACE(message_log_context, "Reply sent.");
+          } else {
+            DL_ERROR(message_log_context,
+                     "Received unregistered msg type: " << msg_type);
+          }
         }
       } else if (response.error) {
         DL_DEBUG(message_log_context, "Error detected: " << response.error_msg);
@@ -288,7 +412,8 @@ RequestWorker::procDataDeleteRequest(std::unique_ptr<IMessage> &&msg_request) {
   if (request->loc_size()) {
 
     for (int i = 0; i < request->loc_size(); i++) {
-      string local_path = request->loc(i).path();
+      std::string local_path = createSanitizedPath(request->loc(i).path());
+
       DL_DEBUG(message_log_context, "Delete "
                                         << request->loc_size()
                                         << " file(s), path: " << local_path);
@@ -311,21 +436,8 @@ RequestWorker::procDataGetSizeRequest(std::unique_ptr<IMessage> &&msg_request) {
   for (int i = 0; i < request->loc_size(); i++) {
     const RecordDataLocation &item = request->loc(i);
 
-    string sanitized_request_path = item.path();
-    while (!sanitized_request_path.empty()) {
-      if (sanitized_request_path.back() == '/') {
-        sanitized_request_path.pop_back();
-      } else {
-        break;
-      }
-    }
+    std::string local_path = createSanitizedPath(item.path());
 
-    string local_path = m_config.globus_collection_path;
-    if (sanitized_request_path.front() != '/') {
-      local_path += "/" + sanitized_request_path;
-    } else {
-      local_path += sanitized_request_path;
-    }
     boost::filesystem::path data_path(local_path);
 
     data_sz = reply.add_size();
@@ -352,20 +464,7 @@ std::unique_ptr<IMessage>
 RequestWorker::procPathCreateRequest(std::unique_ptr<IMessage> &&msg_request) {
   PROC_MSG_BEGIN(Auth::RepoPathCreateRequest, Anon::AckReply)
 
-  string sanitized_request_path = request->path();
-  while (!sanitized_request_path.empty()) {
-    if (sanitized_request_path.back() == '/') {
-      sanitized_request_path.pop_back();
-    } else {
-      break;
-    }
-  }
-  string local_path = m_config.globus_collection_path;
-  if (sanitized_request_path.front() != '/') {
-    local_path += "/" + sanitized_request_path;
-  } else {
-    local_path += sanitized_request_path;
-  }
+  std::string local_path = createSanitizedPath(request->path());
 
   boost::filesystem::path data_path(local_path);
   DL_DEBUG(message_log_context,
@@ -386,21 +485,7 @@ RequestWorker::procPathDeleteRequest(std::unique_ptr<IMessage> &&msg_request) {
   DL_DEBUG(message_log_context,
            "Relative path delete request: " << request->path());
 
-  string sanitized_request_path = request->path();
-  while (!sanitized_request_path.empty()) {
-    if (sanitized_request_path.back() == '/') {
-      sanitized_request_path.pop_back();
-    } else {
-      break;
-    }
-  }
-
-  string local_path = m_config.globus_collection_path;
-  if (sanitized_request_path.front() != '/') {
-    local_path += "/" + sanitized_request_path;
-  } else {
-    local_path += sanitized_request_path;
-  }
+  std::string local_path = createSanitizedPath(request->path());
 
   boost::filesystem::path data_path(local_path);
   DL_TRACE(message_log_context,
diff --git a/repository/server/RequestWorker.hpp b/repository/server/RequestWorker.hpp
index 74bef01b8..12a04a0f2 100644
--- a/repository/server/RequestWorker.hpp
+++ b/repository/server/RequestWorker.hpp
@@ -33,6 +33,9 @@ class RequestWorker {
 private:
   void setupMsgHandlers();
   void workerThread(LogContext log_context);
+  bool prefixesEqual(const std::string &str1, const std::string &str2,
+                     size_t length) const;
+  std::string createSanitizedPath(const std::string &path) const;
 
   std::unique_ptr<IMessage> procVersionRequest(std::unique_ptr<IMessage> &&);
   std::unique_ptr<IMessage> procDataDeleteRequest(std::unique_ptr<IMessage> &&);
diff --git a/scripts/dependency_install_functions.sh b/scripts/dependency_install_functions.sh
index 9107a091d..ce6924ee9 100644
--- a/scripts/dependency_install_functions.sh
+++ b/scripts/dependency_install_functions.sh
@@ -7,9 +7,10 @@ source "${SOURCE}/utils.sh"
 
 sudo_command
 # these are the dependencies to be installed by apt
-apt_file_path="${PROJECT_ROOT}/tmp/apt_deps"
+export apt_file_path="${PROJECT_ROOT}/tmp/apt_deps"
+export pip_file_path="${PROJECT_ROOT}/tmp/pip_deps"
 # these are the dependencies to be installed and built via cmake
-ext_file_path="${PROJECT_ROOT}/tmp/ext_deps"
+export ext_file_path="${PROJECT_ROOT}/tmp/ext_deps"
 
 if [ ! -d "${PROJECT_ROOT}/tmp" ]; then
     mkdir -p "${PROJECT_ROOT}/tmp" 
@@ -35,12 +36,40 @@ if [ ! -e "$DATAFED_DEPENDENCIES_INSTALL_PATH" ] || [ ! -d "$DATAFED_DEPENDENCIE
     fi
 fi
 
-LD_LIBRARY_PATH="$DATAFED_DEPENDENCIES_INSTALL_PATH/lib:$LD_LIBRARY_PATH"
+# NOTE - LD_LIBRARY_PATH must not be a variable for this to work. You cannot
+# replace ! -v LD_LIBRARY_PATH with ! -v ${LD_LIBRARY_PATH} because this is
+# checking if the variable even exists.
+if [[ ! -v LD_LIBRARY_PATH ]]; then
+  LD_LIBRARY_PATH="$DATAFED_DEPENDENCIES_INSTALL_PATH/lib"
+else
+  if [[ -n "$LD_LIBRARY_PATH" ]]; then
+    LD_LIBRARY_PATH="$DATAFED_DEPENDENCIES_INSTALL_PATH/lib:$LD_LIBRARY_PATH"
+  else
+    LD_LIBRARY_PATH="$DATAFED_DEPENDENCIES_INSTALL_PATH/lib"
+  fi
+fi
+
+if [[ ! -v PATH ]]; then
+  PATH="$DATAFED_DEPENDENCIES_INSTALL_PATH/bin"
+else
+  if [[ -n "$PATH" ]]; then
+    PATH="$DATAFED_DEPENDENCIES_INSTALL_PATH/bin:$PATH"
+  else
+    PATH="$DATAFED_DEPENDENCIES_INSTALL_PATH/bin"
+  fi
+fi
+
+init_python() {
+  if [ ! -e "$DATAFED_DEPENDENCIES_INSTALL_PATH" ] || [ ! -d "$DATAFED_PYTHON_DEPENDENCIES_DIR" ]; then
+      mkdir -p "$DATAFED_PYTHON_DEPENDENCIES_DIR"
+  fi
+  python3 -m venv "${DATAFED_PYTHON_ENV}"
+}
 
 install_cmake() {
   if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.cmake_installed-${DATAFED_CMAKE_VERSION}" ]; then
     wget https://github.com/Kitware/CMake/releases/download/v${DATAFED_CMAKE_VERSION}/cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64.tar.gz
-    tar -xzvf cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64.tar.gz
+    tar -xzvf "cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64.tar.gz" >/dev/null 2>&1
     cp -r "cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64/bin" "${DATAFED_DEPENDENCIES_INSTALL_PATH}"
     cp -r "cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64/share" "${DATAFED_DEPENDENCIES_INSTALL_PATH}"
 
@@ -55,19 +84,30 @@ install_cmake() {
 }
 
 install_protobuf() {
-  local original_dir=$(pwd)
-  cd "${PROJECT_ROOT}"
-  echo "PROJECT_ROOT $PROJECT_ROOT"
   if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.protobuf_installed-${DATAFED_PROTOBUF_VERSION}" ]; then
+    local original_dir=$(pwd)
+    cd "${PROJECT_ROOT}"
     if [ -d "${PROJECT_ROOT}/external/protobuf" ]
     then
       # sudo required because of egg file
       "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/protobuf"
     fi
-    git submodule update --init --recursive "${PROJECT_ROOT}/external/protobuf"
+    # Here we are using clone instead of submodule update, because submodule
+    # requires the .git folder exist and the current folder be considered a repo
+    # this creates problems in docker because each time a commit is made the 
+    # .git folder contents are changed causing a fresh rebuild of all containers
+    git clone "https://github.com/protocolbuffers/protobuf.git" \
+      "${PROJECT_ROOT}/external/protobuf"
+
     cd "${PROJECT_ROOT}/external/protobuf"
     git checkout "v${DATAFED_PROTOBUF_VERSION}"
     git submodule update --init --recursive
+    # Build static library, cannot build shared library at same time apparently
+    # there cannot be a shared libsodium file in the
+    # DATAFED_DEPENDENCIES_INSTALL_PREFIX if you want to have everything static
+    # libzmq picks up any shared file regardless of whether you have told it to 
+    # only use static libraries or not.
+    # NOTE - static libraries must be built first
     cmake -S . -B build \
       -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DBUILD_SHARED_LIBS=OFF \
@@ -80,13 +120,20 @@ install_protobuf() {
     else
       "$SUDO_CMD" cmake --build build --target install
     fi
+    # Build Shared library 
+    # Don't build shared, it messes up the static library linking because the
+    # cmake file installed are not compatible
+    # WARNING - static library will break if build with shared options on
+
     cd python
-    LD_LIBRARY_PATH="$LD_LIBRARY_PATH" python3 -m pip install numpy
-    LD_LIBRARY_PATH="$LD_LIBRARY_PATH" python3 setup.py build
-    LD_LIBRARY_PATH="$LD_LIBRARY_PATH" python3 setup.py test
-    LD_LIBRARY_PATH="$LD_LIBRARY_PATH" python3 setup.py install --user
-    python3 setup.py build
-    python3 setup.py install --user
+    init_python
+    source "${DATAFED_PYTHON_ENV}/bin/activate"
+    LD_LIBRARY_PATH="$LD_LIBRARY_PATH" PATH="$PATH" python3 -m pip install numpy
+    LD_LIBRARY_PATH="$LD_LIBRARY_PATH" PATH="$PATH" python3 setup.py build
+    LD_LIBRARY_PATH="$LD_LIBRARY_PATH" PATH="$PATH" python3 setup.py test
+    # Because we have activaited a venv we don't want to use the --user flag
+    # with the install command
+    LD_LIBRARY_PATH="$LD_LIBRARY_PATH" PATH="$PATH" python3 setup.py install
     cd ../
     # Cleanup build file with root ownership
     if [ -f build/install_manifest.txt ]
@@ -97,67 +144,70 @@ install_protobuf() {
 
     # Mark protobuf as installed
     touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.protobuf_installed-${DATAFED_PROTOBUF_VERSION}"
+    cd "$original_dir"
   fi
-  cd "$original_dir"
 }
 
 install_libsodium() {
   if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libsodium_installed-${DATAFED_LIBSODIUM_VERSION}" ]; then
-    if [ -d libsodium ]
+    local original_dir=$(pwd)
+    if [ -d "${PROJECT_ROOT}/external/libsodium" ]
     then
-      rm -rf libsodium 
+      # sudo required because of egg file
+      "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/libsodium"
     fi
-    #git clone --recursive https://github.com/robinlinden/libsodium-cmake.git
-    #cd libsodium-cmake/libsodium
-    #git checkout "$DATAFED_LIBSODIUM_VERSION"
-    #cd ../
-    #cmake -DBUILD_SHARED_LIBS=OFF -S. -B build \
-    #  -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-    #  -DCMAKE_INSTALL_PREFIX="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
-    #cmake --build build -j 8
-    #if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
-    #  cmake --build build --target install
-    #else
-    #  "$SUDO_CMD" cmake --build build --target install
-    #fi
-
-    git clone https://github.com/jedisct1/libsodium.git
-    cd libsodium
+    # Here we are using clone instead of submodule update, because submodule
+    # requires the .git folder exist and the current folder be considered a repo
+    # this creates problems in docker because each time a commit is made the 
+    # .git folder contents are changed causing a fresh rebuild of all containers
+    git clone https://github.com/jedisct1/libsodium.git "${PROJECT_ROOT}/external/libsodium"
+    cd "${PROJECT_ROOT}/external/libsodium"
     git checkout "$DATAFED_LIBSODIUM_VERSION"
     ./autogen.sh
-    SODIUM_STATIC=1 ./configure --enable-static=yes --enable-shared=no --prefix="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
+    # Build static ONLY!!!!
+    # Note if zmq detects a shared sodium library it will grab it no matter what
+    # --enable-shared=no must be set here
+    SODIUM_STATIC=1 ./configure --enable-static=yes --enable-shared=no --with-pic=yes --prefix="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
+    make -j 8
     make check
     if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
       make install
     else
       "$SUDO_CMD" make install
     fi
-    cd ../
     
     # Mark libsodium as installed
     touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libsodium_installed-${DATAFED_LIBSODIUM_VERSION}"
+    cd "$original_dir"
   fi
 }
 
 install_libzmq() {
   if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libzmq_installed-${DATAFED_LIBZMQ_VERSION}" ]; then
-    if [ -d libzmq ]
+    local original_dir=$(pwd)
+    if [ -d "${PROJECT_ROOT}/external/libzmq" ]
     then
-      rm -rf libzmq 
+      "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/libzmq"
     fi
     if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libsodium_installed-${DATAFED_LIBSODIUM_VERSION}" ]; then
       echo "You must first install libsodium before installing libzmq"
       exit 1
     fi
-    git clone https://github.com/zeromq/libzmq.git
-    cd libzmq
+    # Here we are using clone instead of submodule update, because submodule
+    # requires the .git folder exist and the current folder be considered a repo
+    # this creates problems in docker because each time a commit is made the 
+    # .git folder contents are changed causing a fresh rebuild of all containers
+    git clone https://github.com/zeromq/libzmq.git "${PROJECT_ROOT}/external/libzmq"
+    cd "${PROJECT_ROOT}/external/libzmq"
     git checkout "v${DATAFED_LIBZMQ_VERSION}"
+    # Build static only
     cmake -S. -B build \
       -DBUILD_STATIC=ON \
       -DBUILD_SHARED_LIBS=OFF \
       -DBUILD_SHARED=OFF \
       -DWITH_LIBSODIUM_STATIC=ON \
       -DBUILD_TESTS=OFF \
+      -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DCMAKE_PREFIX_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib" \
       -DCMAKE_INSTALL_PREFIX="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
     cmake --build build -j 8
@@ -166,19 +216,21 @@ install_libzmq() {
     else
       "$SUDO_CMD" cmake --build build --target install
     fi
-    cd ../ 
 
-    if [ -d cppzmq ]
+    if [ -d "${PROJECT_ROOT}/external/cppzmq" ]
     then
-      rm -rf cppzmq
+      # sudo required because of egg file
+      "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/cppzmq"
     fi
-    git clone https://github.com/zeromq/cppzmq.git
-    cd cppzmq
+    git clone https://github.com/zeromq/cppzmq.git "${PROJECT_ROOT}/external/cppzmq"
+    cd "${PROJECT_ROOT}/external/cppzmq"
     git checkout v"${DATAFED_LIB_ZMQCPP_VERSION}"
     # Will will not build the unit tests because there are not enough controls
     # to link to the correct static library.
+    # NOTE - static libraries must be built first
     cmake -S. -B build \
       -DBUILD_SHARED_LIBS=OFF \
+      -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DCPPZMQ_BUILD_TESTS=OFF \
       -DCMAKE_INSTALL_PREFIX="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
     cmake --build build -j 8
@@ -188,6 +240,7 @@ install_libzmq() {
       "$SUDO_CMD" cmake --build build --target install
     fi
     
+    cd "$original_dir"
     # Mark libzmq as installed
     touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libzmq_installed-${DATAFED_LIBZMQ_VERSION}"
   fi
@@ -195,15 +248,19 @@ install_libzmq() {
 
 install_nlohmann_json() {
   if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.nlohmann_json_installed-${DATAFED_NLOHMANN_JSON_VERSION}" ]; then
-    if [ -d json ]
+    local original_dir=$(pwd)
+    if [ -d "${PROJECT_ROOT}/external/json" ]
     then
-      rm -rf json
+      "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/json"
     fi
-    git clone https://github.com/nlohmann/json.git
-    cd json
+    git clone https://github.com/nlohmann/json.git "${PROJECT_ROOT}/external/json"
+    cd "${PROJECT_ROOT}/external/json"
     git checkout v${DATAFED_NLOHMANN_JSON_VERSION}
     echo "FILE STRUCTURE $(ls)"
+    # Build static
     cmake -S . -B build \
+      -DBUILD_SHARED_LIBS=OFF \
+      -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DCMAKE_INSTALL_PREFIX="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
     cmake --build build -j 8
     if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
@@ -211,23 +268,38 @@ install_nlohmann_json() {
     else
       "$SUDO_CMD" cmake --build build --target install
     fi
-    cd ../
-    
+    # Build shared
+    cmake -S . -B build \
+      -DBUILD_SHARED_LIBS=ON \
+      -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
+      -DCMAKE_INSTALL_PREFIX="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
+    cmake --build build -j 8
+    if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
+      cmake --build build --target install
+    else
+      "$SUDO_CMD" cmake --build build --target install
+    fi
+
     # Mark nlohmann_json as installed
     touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.nlohmann_json_installed-${DATAFED_NLOHMANN_JSON_VERSION}"
+    cd "$original_dir"
   fi
 }
 
 install_json_schema_validator() {
   if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.json_schema_validator_installed-${DATAFED_JSON_SCHEMA_VALIDATOR_VERSION}" ]; then
-    if [ -d json-schema-validator ]
+    local original_dir=$(pwd)
+    if [ -d "${PROJECT_ROOT}/external/json-schema-validator" ]
     then
-      rm -rf json-schema-validator
+      "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/json-schema-validator"
     fi
-    git clone https://github.com/pboettch/json-schema-validator
-    cd json-schema-validator
+    git clone https://github.com/pboettch/json-schema-validator "${PROJECT_ROOT}/external/json-schema-validator"
+    cd "${PROJECT_ROOT}/external/json-schema-validator"
     git checkout ${DATAFED_JSON_SCHEMA_VALIDATOR_VERSION}
+    # Build static 
     cmake -S . -B build \
+      -DBUILD_SHARED_LIBS=OFF \
+      -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DCMAKE_INSTALL_PREFIX="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
     cmake --build build -j 8
     if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
@@ -235,10 +307,12 @@ install_json_schema_validator() {
     else
       "$SUDO_CMD" cmake --build build --target install
     fi
-    cd ../
-    
+    # WARNING building shared library will overwrite cmake file for static
+    # library, does not appear to support both targets at the same time, similar
+    # to protobuf
     # Mark json-schema-validator as installed
     touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.json_schema_validator_installed-${DATAFED_JSON_SCHEMA_VALIDATOR_VERSION}"
+    cd "$original_dir"
   fi
 }
 
@@ -274,13 +348,37 @@ install_nvm() {
   fi
 }
 
-install_node() {
-  # By default this will place NVM in $HOME/.nvm
+install_ws_node_packages() {
+
   if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.nvm_installed-${DATAFED_NVM_VERSION}" ]; then
-    echo "You must first install nvm before installing node."
+    echo "You must first install nvm before installing ws node packages."
+    exit 1
+  fi
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.node_installed-${DATAFED_NODE_VERSION}" ]; then
+    echo "You must first install node before installing ws node packages"
     exit 1
   fi
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.cmake_installed-${DATAFED_CMAKE_VERSION}" ]; then
+    echo "You must first install cmake before installing ws node packages"
+    exit 1
+  fi
+
+  # Configure the package.json.in file -> package.json
+  cmake -P "${PROJECT_ROOT}/cmake/Web.cmake" 
+  export NVM_DIR="${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm"
+  export NODE_VERSION="$DATAFED_NODE_VERSION"
+  "$NVM_DIR/nvm-exec" npm --prefix "${PROJECT_ROOT}/web" install "${PROJECT_ROOT}/web"
+}
+
+
+install_node() {
+  # By default this will place NVM in $HOME/.nvm
   if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.node_installed-${DATAFED_NODE_VERSION}" ]; then
+    local original_dir=$(pwd)
+    if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.nvm_installed-${DATAFED_NVM_VERSION}" ]; then
+      echo "You must first install nvm before installing node."
+      exit 1
+    fi
 
     export NVM_DIR="${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm"
 
@@ -288,6 +386,7 @@ install_node() {
     nvm install "$DATAFED_NODE_VERSION"
     # Mark node as installed
     touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.node_installed-${DATAFED_NODE_VERSION}"
+    cd "$original_dir"
   else
     export NVM_DIR="${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm"
     [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
@@ -305,12 +404,14 @@ install_foxx_cli() {
   fi
   # By default this will place NVM in $HOME/.nvm
   if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.foxx_cli_installed" ]; then
+    local original_dir=$(pwd)
     export NVM_DIR="${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm"
     [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
     export NODE_VERSION="$DATAFED_NODE_VERSION"
     "$NVM_DIR/nvm-exec" npm install --global foxx-cli --prefix "${DATAFED_DEPENDENCIES_INSTALL_PATH}/npm"
     # Mark foxx_cli as installed
     touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.foxx_cli_installed"
+    cd "$original_dir"
   else
     export NVM_DIR="${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm"
     [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
@@ -327,6 +428,107 @@ install_arangodb() {
   "$SUDO_CMD" apt-get install arangodb3
 }
 
+install_openssl() {
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.openssl_installed-${DATAFED_OPENSSL}" ]; then
+    local original_dir=$(pwd)
+    if [ -d "${PROJECT_ROOT}/external/openssl" ]
+    then
+      "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/openssl"
+    fi
+    git clone https://github.com/openssl/openssl "${PROJECT_ROOT}/external/openssl"
+    cd "${PROJECT_ROOT}/external/openssl"
+    git checkout "$DATAFED_OPENSSL_COMMIT"
+    ./config --prefix="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
+    make -j 8
+
+    if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
+      make install
+    else
+      "$SUDO_CMD" make install 
+    fi
+    # Mark openssl as installed
+    touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.openssl_installed-${DATAFED_OPENSSL}"
+    cd "$original_dir"
+  fi
+}
+
+install_libcurl() {
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libcurl_installed-${DATAFED_LIBCURL}" ]; then
+    local original_dir=$(pwd)
+    if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.zlib_installed-${DATAFED_ZLIB_VERSION}" ]; then
+      echo "You must first install zlib before installing libcurl packages"
+      exit 1
+    fi
+    if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.openssl_installed-${DATAFED_OPENSSL}" ]; then
+      echo "You must first install OpenSSL before installing libcurl packages"
+      exit 1
+    fi
+    if [ -d "${PROJECT_ROOT}/external/libcurl" ]
+    then
+      "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/libcurl"
+    fi
+    wget "${DATAFED_LIBCURL_URL}"
+    mkdir -p "${PROJECT_ROOT}/external/libcurl"
+    tar -xf "curl-${DATAFED_LIBCURL}.tar.gz" -C "${PROJECT_ROOT}/external/libcurl"
+    cd "${PROJECT_ROOT}/external/libcurl/curl-${DATAFED_LIBCURL}"
+
+    # Making third party features and dependencies explicit
+    # OpenSSL is needed for HTTPS encryption
+    # File - allows caching requires libc
+    # GNUTLS - HTTPS support session management certificate verification etc
+    # NOTE: NSS - Network Security Services for HTTP support is deprecated
+    # NOTE: metalink - is no longer supported and not a valid argument
+    PKG_CONFIG_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib/pkgconfig" \
+    ./configure --with-ssl="${DATAFED_DEPENDENCIES_INSTALL_PATH}" --with-gnutls --with-zlib \
+      --enable-file --disable-shared \
+      --disable-ldap --disable-ldaps --disable-rtsp --disable-dict \
+      --disable-telnet --disable-tftp --disable-pop3 --disable-imap \
+      --disable-smtp  --disable-gopher --disable-smb --disable-ftp \
+      --disable-file --disable-sspi --without-zstd --without-libidn2 --without-librtmp \
+      --without-winidn --without-libpsl \
+      --without-libssh2 --without-nghttp2 --without-brotli \
+      --without-libidn --without-libbrotli \
+      --prefix="${DATAFED_DEPENDENCIES_INSTALL_PATH}" 
+    make -j 8
+
+    if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
+      make install
+    else
+      "$SUDO_CMD" make install 
+    fi
+
+    # Mark libcurl as installed
+    touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libcurl_installed-${DATAFED_LIBCURL}"
+    cd "$original_dir"
+  fi
+}
+
+install_zlib() {
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.zlib_installed-${DATAFED_ZLIB_VERSION}" ]; then
+    local original_dir=$(pwd)
+    if [ -d "${PROJECT_ROOT}/external/zlib" ]
+    then
+      "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/zlib"
+    fi
+    wget "${DATAFED_ZLIB_URL}"
+    mkdir -p "${PROJECT_ROOT}/external/zlib"
+    tar -xf "zlib-${DATAFED_ZLIB_VERSION}.tar.gz" -C "${PROJECT_ROOT}/external/zlib"
+    cd "${PROJECT_ROOT}/external/zlib/zlib-${DATAFED_ZLIB_VERSION}"
+    PKG_CONFIG_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/lib/pkgconfig" ./configure --prefix="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
+    make -j 8
+
+    if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
+      make install
+    else
+      "$SUDO_CMD" make install 
+    fi
+
+    # Mark libcurl as installed
+    touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.zlib_installed-${DATAFED_ZLIB_VERSION}"
+    cd "$original_dir"
+  fi
+}
+
 install_dep_by_name() {
   case "$1" in
     "cmake")
@@ -350,6 +552,24 @@ install_dep_by_name() {
     "libzmq")
       install_libzmq
       ;;
+    "libopenssl")
+      install_openssl
+      ;;
+    "libcurl")
+      install_libcurl
+      ;;
+    "zlib")
+      install_zlib
+      ;;
+    "nvm")
+      install_nvm
+      ;;
+    "node")
+      install_node
+      ;;
+    "ws_node_packages")
+      install_ws_node_packages
+      ;;
   esac
   cd ~
 }
diff --git a/scripts/dependency_versions.sh b/scripts/dependency_versions.sh
index ee94626d0..2ada763da 100644
--- a/scripts/dependency_versions.sh
+++ b/scripts/dependency_versions.sh
@@ -1,7 +1,7 @@
 
 # Versions
 DATAFED_CMAKE_VERSION="3.17.5"
-DATAFED_GLOBUS_VERSION="6.0.22"
+DATAFED_GLOBUS_VERSION="6.0.31-1"
 DATAFED_JSON_SCHEMA_VALIDATOR_VERSION="2.1.0"
 DATAFED_NLOHMANN_JSON_VERSION="3.10.2"
 DATAFED_LIBSODIUM_VERSION="1.0.18"
@@ -20,6 +20,11 @@ DATAFED_PROTOBUF_VERSION="25.2"
 DATAFED_DYNAMIC_LIBRARY_PROTOBUF_VERSION="25.2.0"
 # Full version
 DATAFED_FULL_PROTOBUF_VERSION="4.25.2"
-DATAFED_LIBCURL="7.68.0"
+DATAFED_LIBCURL="7.88.1"
+DATAFED_LIBCURL_URL="https://github.com/curl/curl/releases/download/curl-7_88_1/curl-7.88.1.tar.gz"
 DATAFED_OPENSSL="1.1.1"
+DATAFED_OPENSSL_COMMIT="e04bd34"
 DATAFED_BOOST="1.71.0"
+DATAFED_ZLIB_VERSION="1.3.1"
+DATAFED_ZLIB_URL="https://zlib.net/zlib-1.3.1.tar.gz"
+DATAFED_GCS_SUBMODULE_VERSION="v2.8.0"
diff --git a/scripts/generate_authz_config.sh b/scripts/generate_authz_config.sh
index c709e57eb..e7e5f25ab 100755
--- a/scripts/generate_authz_config.sh
+++ b/scripts/generate_authz_config.sh
@@ -30,6 +30,13 @@ Help()
 
 REPO_ID="datafed-home"
 
+if [ -z "${DATAFED_DEFAULT_LOG_PATH}" ]
+then
+  local_DATAFED_LOG_PATH="/var/log/datafed"
+else
+  local_DATAFED_LOG_PATH=$(printenv DATAFED_DEFAULT_LOG_PATH)
+fi
+
 if [ -z "DATAFED_DOMAIN" ]
 then
   local_DATAFED_DOMAIN="datafed.ornl.gov"
@@ -46,18 +53,18 @@ else
   local_DATAFED_SERVER_PORT=$(printenv DATAFED_SERVER_PORT)
 fi
 
-if [ -z "${GCS_COLLECTION_ROOT_PATH}" ]
+if [ -z "${DATAFED_GCS_COLLECTION_ROOT_PATH}" ]
 then
-  local_GCS_COLLECTION_ROOT_PATH="/mnt/datafed-repo/mapped"
+  local_DATAFED_GCS_COLLECTION_ROOT_PATH="/mnt/datafed-repo/mapped"
 else
-  local_GCS_COLLECTION_ROOT_PATH=$(printenv GCS_COLLECTION_ROOT_PATH)
+  local_DATAFED_GCS_COLLECTION_ROOT_PATH=$(printenv DATAFED_GCS_COLLECTION_ROOT_PATH)
 fi
 
-if [ -z $DATAFED_AUTHZ_USER ]
+if [ -z "${DATAFED_GLOBUS_REPO_USER}" ]
 then
-  local_DATAFED_AUTHZ_USER="$USER"
+  local_DATAFED_AUTHZ_USER="$DATAFED_GLOBUS_REPO_USER"
 else
-  local_DATAFED_AUTHZ_USER=$(printenv DATAFED_AUTHZ_USER)
+  local_DATAFED_AUTHZ_USER=$(printenv DATAFED_GLOBUS_REPO_USER)
 fi
 
 
@@ -94,7 +101,7 @@ while [ : ]; do
         ;;
     -g | --globus-collection-path)
         echo "Processing 'Globus Collection Path' option. Input argument is '$2'"
-        local_GCS_COLLECTION_ROOT_PATH=$2
+        local_DATAFED_GCS_COLLECTION_ROOT_PATH=$2
         shift 2
         ;;
     --) shift; 
@@ -115,8 +122,9 @@ server_key=${DATAFED_INSTALL_PATH}/keys/datafed-core-key.pub
 repo_id=repo/$DATAFED_REPO_ID_AND_DIR
 pub_key=${DATAFED_INSTALL_PATH}/keys/datafed-repo-key.pub
 priv_key=${DATAFED_INSTALL_PATH}/keys/datafed-repo-key.priv
+log_path=${local_DATAFED_LOG_PATH}/datafed-gsi-authz.log
 user=$local_DATAFED_AUTHZ_USER
-globus-collection-path=$local_GCS_COLLECTION_ROOT_PATH
+globus-collection-path=$local_DATAFED_GCS_COLLECTION_ROOT_PATH
 EOF
 
 echo
diff --git a/scripts/generate_datafed.sh b/scripts/generate_datafed.sh
index 7c70f72ad..e829ee9fe 100755
--- a/scripts/generate_datafed.sh
+++ b/scripts/generate_datafed.sh
@@ -159,6 +159,36 @@ else
   local_DATAFED_GOOGLE_ANALYTICS_TAG=$(printenv DATAFED_GOOGLE_ANALYTICS_TAG)
 fi
 
+if [ -z "${DATAFED_GLOBUS_REPO_USER}" ]
+then
+  local_DATAFED_GLOBUS_REPO_USER=""
+else
+  local_DATAFED_GLOBUS_REPO_USER=$(printenv DATAFED_GLOBUS_REPO_USER)
+fi
+
+if [ -z "${DATAFED_CORE_USER}" ]
+then
+  local_DATAFED_CORE_USER=""
+else
+  local_DATAFED_CORE_USER=$(printenv DATAFED_CORE_USER)
+fi
+
+
+if [ -z "${DATAFED_GLOBUS_CONTROL_PORT}" ]
+then
+  local_DATAFED_GLOBUS_CONTROL_PORT="443"
+else
+  local_DATAFED_GLOBUS_CONTROL_PORT=$(printenv DATAFED_GLOBUS_CONTROL_PORT)
+fi
+
+if [ -z "${DATAFED_GLOBUS_ALLOWED_DOMAINS}" ]
+then
+  local_DATAFED_GLOBUS_ALLOWED_DOMAINS="globusid.org"
+else
+  local_DATAFED_GLOBUS_ALLOWED_DOMAINS=$(printenv DATAFED_GLOBUS_ALLOWED_DOMAINS)
+fi
+
+
 if [ ! -d "$PATH_TO_CONFIG_DIR" ]
 then
   mkdir -p "$PATH_TO_CONFIG_DIR"
@@ -185,6 +215,8 @@ export DATAFED_INSTALL_PATH="$local_DATAFED_INSTALL_PATH"
 # by default it will install to:
 # /opt/datafed/dependencies
 export DATAFED_DEPENDENCIES_INSTALL_PATH="$local_DATAFED_DEPENDENCIES_INSTALL_PATH"
+export DATAFED_PYTHON_DEPENDENCIES_DIR="${local_DATAFED_DEPENDENCIES_INSTALL_PATH}/python"
+export DATAFED_PYTHON_ENV="${local_DATAFED_DEPENDENCIES_INSTALL_PATH}/python/datafed"
 # ************************************************
 # Env Variables for Core & Web Server
 # ************************************************
@@ -202,7 +234,7 @@ export DATAFED_SERVER_PORT="$local_DATAFED_SERVER_PORT"
 # ************************************************
 # DataFed Repository POSIX user account that DataFed users will be mapped too
 # from Globus, so the posix account all globus users will map too
-export DATAFED_GLOBUS_REPO_USER=""
+export DATAFED_GLOBUS_REPO_USER="$local_DATAFED_GLOBUS_REPO_USER"
 
 # ******************************************************************
 # Env Variables for Authz, Web, Repo Server & administrative scripts
@@ -215,7 +247,7 @@ export DATAFED_DOMAIN="$local_DATAFED_DOMAIN"
 # ************************************************
 export DATAFED_DATABASE_PASSWORD="$local_DATAFED_DATABASE_PASSWORD"
 # The user account the datafed core application will run under
-export DATAFED_CORE_USER=""
+export DATAFED_CORE_USER="$local_DATAFED_CORE_USER"
 
 # ************************************************
 # Env Variables for Web Server
@@ -273,18 +305,18 @@ export DATAFED_GCS_ROOT_NAME="$local_DATAFED_GCS_ROOT_NAME"
 #
 # The path will be created if it does not exist
 # 
-# \$GCS_COLLECTION_ROOT_PATH/\$DATAFED_REPO_ID_AND_DIR"
+# \$DATAFED_GCS_COLLECTION_ROOT_PATH/\$DATAFED_REPO_ID_AND_DIR"
 #
 # So if these variables are defined as:
 # DATAFED_GCS_ROOT_NAME="datafed-home"
-# GCS_COLLECTION_ROOT_PATH="/home/cades/collections/mapped/"
+# DATAFED_GCS_COLLECTION_ROOT_PATH="/home/cades/collections/mapped/"
 # 
 # A folder named 
 #
 # "/home/cades/collections/mapped/datafed-home"
 #
 # Will be created
-export GCS_COLLECTION_ROOT_PATH="$local_DATAFED_GCS_COLLECTION_ROOT_PATH"
+export DATAFED_GCS_COLLECTION_ROOT_PATH="$local_DATAFED_GCS_COLLECTION_ROOT_PATH"
 # The DataFed repo id, this also must be the name
 # of the directory that will be placed in Globus 
 # collection, avoid using spaces in the name.
@@ -292,6 +324,10 @@ export GCS_COLLECTION_ROOT_PATH="$local_DATAFED_GCS_COLLECTION_ROOT_PATH"
 export DATAFED_REPO_ID_AND_DIR="$local_DATAFED_REPO_ID_AND_DIR"
 # Institutionally allowed domains, users that have accounts in these domains
 # will have the ability to store data on the repository.
-# i.e. ornl.gov, or cu.edu
-export DATAFED_GLOBUS_ALLOWED_DOMAINS=""
+# i.e. ornl.gov or cu.edu or gmail.com by default clients.auth.globus.org
+# must be allowed to allow automatic setup.
+export DATAFED_GLOBUS_ALLOWED_DOMAINS="$local_DATAFED_GLOBUS_ALLOWED_DOMAINS"
+# Globus control port default is 443, might want to change if hosting
+# a web server on the same machine.
+export DATAFED_GLOBUS_CONTROL_PORT="$local_DATAFED_GLOBUS_CONTROL_PORT"
 EOF
diff --git a/scripts/generate_gsi-authz_config.sh b/scripts/generate_gsi-authz_config.sh
new file mode 100755
index 000000000..3482ce297
--- /dev/null
+++ b/scripts/generate_gsi-authz_config.sh
@@ -0,0 +1,32 @@
+#!/bin/env bash
+# Cannot run with -u because we check for unbound variables
+# and the script will exit prematurely if '-u' is set
+set -ef -o pipefail
+
+SCRIPT=$(realpath "$0")
+SOURCE=$(dirname "$SCRIPT")
+PROJECT_ROOT=$(realpath "${SOURCE}/..")
+source "${PROJECT_ROOT}/config/datafed.sh"
+
+PATH_TO_CONFIG_DIR=$(realpath "$SOURCE/../config")
+CONFIG_FILE_NAME="gsi-authz.conf"
+
+cat << EOF > "$PATH_TO_CONFIG_DIR/$CONFIG_FILE_NAME"
+GLOBUS_GSI_AUTHZ_SYSTEM_INIT           $DATAFED_INSTALL_PATH/authz/libdatafed-authz gsi_authz_init
+GLOBUS_GSI_AUTHZ_SYSTEM_DESTROY        $DATAFED_INSTALL_PATH/authz/libdatafed-authz gsi_authz_destroy
+GLOBUS_GSI_AUTHZ_HANDLE_INIT           $DATAFED_INSTALL_PATH/authz/libdatafed-authz gsi_authz_handle_init
+GLOBUS_GSI_AUTHORIZE_ASYNC             $DATAFED_INSTALL_PATH/authz/libdatafed-authz gsi_authz_authorize_async
+GLOBUS_GSI_AUTHZ_CANCEL                $DATAFED_INSTALL_PATH/authz/libdatafed-authz gsi_authz_cancel
+GLOBUS_GSI_AUTHZ_HANDLE_DESTROY        $DATAFED_INSTALL_PATH/authz/libdatafed-authz gsi_authz_handle_destroy
+GLOBUS_GSI_GET_AUTHORIZATION_IDENTITY  $DATAFED_INSTALL_PATH/authz/libdatafed-authz gsi_authz_identify
+globus_mapping                         $DATAFED_INSTALL_PATH/authz/libdatafed-authz gsi_map_user
+EOF
+
+echo
+echo "gsi-conf file is being placed here: $PATH_TO_CONFIG_DIR/${CONFIG_FILE_NAME}"
+echo
+echo "Contents are:"
+echo 
+cat "$PATH_TO_CONFIG_DIR/${CONFIG_FILE_NAME}"
+
+
diff --git a/scripts/generate_repo_config.sh b/scripts/generate_repo_config.sh
index 2145016fb..bfc2e30a1 100755
--- a/scripts/generate_repo_config.sh
+++ b/scripts/generate_repo_config.sh
@@ -17,7 +17,7 @@ Help()
   echo "-c, --cred-dir                    The location of the credential directory where the"
   echo "                                  private keys of the repo server are and the public"
   echo "                                  key of the core server."
-  echo "-e, --egress-port                 The egress port that needs to be open on the repo"
+  echo "-p, --port                        The port that needs to be open on the repo"
   echo "                                  server so the repo server can communicate with "
   echo "                                  the datafed server."
   echo "-d, --domain                      The DataFed fully qualified domain name and port"
@@ -38,11 +38,11 @@ else
   local_DATAFED_DOMAIN=$(printenv DATAFED_DOMAIN)
 fi
 
-if [ -z "${GCS_COLLECTION_ROOT_PATH}" ]
+if [ -z "${DATAFED_GCS_COLLECTION_ROOT_PATH}" ]
 then
-  local_GCS_COLLECTION_ROOT_PATH="/mnt/datafed-repo/mapped"
+  local_DATAFED_GCS_COLLECTION_ROOT_PATH="/mnt/datafed-repo/mapped"
 else
-  local_GCS_COLLECTION_ROOT_PATH=$(printenv GCS_COLLECTION_ROOT_PATH)
+  local_DATAFED_GCS_COLLECTION_ROOT_PATH=$(printenv DATAFED_GCS_COLLECTION_ROOT_PATH)
 fi
 
 local_DATAFED_LOG_PATH=""
@@ -55,10 +55,10 @@ else
 fi
 
 local_DATAFED_CRED_DIR="${DATAFED_INSTALL_PATH}/keys/"
-local_DATAFED_REPO_EGRESS_PORT="9000"
+local_DATAFED_REPO_PORT="9000"
 local_DATAFED_REPO_THREADS=2
 
-VALID_ARGS=$(getopt -o ht:c:e:d:g: --long 'help',threads:,cred-dir:,egress-port:,globus-collection-path:,datafed-domain-port: -- "$@")
+VALID_ARGS=$(getopt -o ht:c:e:d:g: --long 'help',threads:,cred-dir:,port:,globus-collection-path:,datafed-domain-port: -- "$@")
 if [[ $? -ne 0 ]]; then
       exit 1;
 fi
@@ -79,9 +79,9 @@ while [ : ]; do
         local_DATAFED_CRED_DIR=$2
         shift 2
         ;;
-    -e | --egress-port)
-        echo "Processing 'egress port' option. Input argument is '$2'"
-        local_DATAFED_REPO_EGRESS_PORT=$2
+    -p | --port)
+        echo "Processing 'port' option. Input argument is '$2'"
+        local_DATAFED_REPO_PORT=$2
         shift 2
         ;;
     -d | --domain)
@@ -91,7 +91,7 @@ while [ : ]; do
         ;;
     -g | --globus-collection-path)
         echo "Processing 'Globus Collection Path' option. Input argument is '$2'"
-        local_GCS_COLLECTION_ROOT_PATH=$2
+        local_DATAFED_GCS_COLLECTION_ROOT_PATH=$2
         shift 2
         ;;
     --) shift; 
@@ -114,9 +114,9 @@ CONFIG_FILE_NAME="datafed-repo.cfg"
 cat << EOF > "$PATH_TO_CONFIG_DIR/$CONFIG_FILE_NAME"
 cred-dir=$local_DATAFED_CRED_DIR
 server=tcp://$local_DATAFED_DOMAIN:${local_DATAFED_PORT}
-port=$local_DATAFED_REPO_EGRESS_PORT
+port=$local_DATAFED_REPO_PORT
 threads=$local_DATAFED_REPO_THREADS
-globus-collection-path=$local_GCS_COLLECTION_ROOT_PATH
+globus-collection-path=$local_DATAFED_GCS_COLLECTION_ROOT_PATH
 EOF
 
 echo
diff --git a/scripts/globus/__init__.py b/scripts/globus/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/scripts/globus/clean_globus.sh b/scripts/globus/clean_globus.sh
index fc0c5c369..5d6f95b26 100755
--- a/scripts/globus/clean_globus.sh
+++ b/scripts/globus/clean_globus.sh
@@ -20,6 +20,12 @@ collection_line=$( globus-connect-server collection list | grep "$COLLECTION_NAM
 if [ ! -z "$collection_line" ]
 then
   uuid_of_collection=$( globus-connect-server collection list | grep "$COLLECTION_NAME" | awk '{ print $1 }')
+
+  
+  globus-connect-server collection update \
+    "$uuid_of_collection" \
+    --no-delete-protected
+
   globus-connect-server collection delete "$uuid_of_collection"
 fi
 
diff --git a/scripts/globus/create_guest_collection.py b/scripts/globus/create_guest_collection.py
new file mode 100644
index 000000000..25299f10f
--- /dev/null
+++ b/scripts/globus/create_guest_collection.py
@@ -0,0 +1,220 @@
+import globus_sdk
+from globus_sdk import scopes
+
+import utils
+import os
+
+
+# The Globus project the GCS endpoint will be created in
+DATAFED_GCS_ROOT_NAME = os.getenv("DATAFED_GCS_ROOT_NAME", "DataFed Repo")
+PROJECT_NAME = os.getenv(
+    "DATAFED_GLOBUS_PROJECT_NAME", DATAFED_GCS_ROOT_NAME + " Project"
+)
+# This is for confidential client
+CLIENT_NAME = os.getenv(
+    "DATAFED_GLOBUS_CLIENT_NAME", DATAFED_GCS_ROOT_NAME + " Setup Client"
+)
+# Name of the client secret used by the confidential client
+CRED_NAME = os.getenv("DATAFED_GLOBUS_CRED_NAME", DATAFED_GCS_ROOT_NAME + " Cred")
+# Name of the file where we will store confidential client credentials
+CRED_FILE_PATH = os.getenv("DATAFED_GLOBUS_CRED_FILE_PATH", "./client_cred.json")
+ENDPOINT_ID = os.getenv("GCS_CLI_ENDPOINT_ID")
+ENDPOINT_NAME = os.getenv(
+    "DATAFED_GLOBUS_ENDPOINT_NAME", DATAFED_GCS_ROOT_NAME + " Endpoint"
+)
+# Path to deployment key
+DEPLOYMENT_KEY_PATH = os.getenv(
+    "DATAFED_GLOBUS_DEPLOYMENT_KEY_PATH", "./deployment-key.json"
+)
+
+# Path to deployment key
+DATAFED_GLOBUS_CONTROL_PORT = os.getenv("DATAFED_GLOBUS_CONTROL_PORT", "443")
+DATAFED_GCS_URL = os.getenv("DATAFED_GCS_URL")
+client_id = os.getenv("GCS_CLI_CLIENT_ID")
+client_secret = os.getenv("GCS_CLI_CLIENT_SECRET")
+mapped_collection_id = os.getenv("MAPPED_COLLECTION_ID")
+mapped_collection_name = os.getenv(
+    "DATAFED_GCS_COLLECTION_MAPPED", f"{DATAFED_GCS_ROOT_NAME} Collection Mapped"
+)
+guest_collection_name = os.getenv(
+    "DATAFED_GCS_COLLECTION_GUEST", f"{DATAFED_GCS_ROOT_NAME} Collection Guest"
+)
+storage_gateway_id = os.getenv("STORAGE_GATEWAY_ID")
+storage_gateway_name = os.getenv(
+    "DATAFED_GCS_STORAGE_GATEWAY", f"{DATAFED_GCS_ROOT_NAME} Storage Gateway"
+)
+local_username = os.getenv("DATAFED_REPO_USER")
+
+if ENDPOINT_ID is None:
+    raise Exception("GCS_CLI_ENDPOINT_ID must be defined as an env varaible")
+if DATAFED_GCS_URL is None:
+    raise Exception(
+        "Unable to create guest collection, DATAFED_GCS_URL is not" " defined."
+    )
+if local_username is None:
+    raise Exception("DATAFED_REPO_USER is not defined.")
+
+if client_id is None:
+    client_id = utils.getClientIdFromCredFile(CRED_FILE_PATH)
+
+if client_secret is None:
+    client_secret = utils.getCredentialFromFile(CRED_FILE_PATH, client_id)
+
+client = globus_sdk.ConfidentialAppAuthClient(client_id, client_secret)
+
+auth_scopes = (
+    "openid profile email "
+    "urn:globus:auth:scope:auth.globus.org:manage_projects "
+    "urn:globus:auth:scope:auth.globus.org:view_identities"
+)
+
+authorizer = globus_sdk.ClientCredentialsAuthorizer(client, auth_scopes)
+
+gcs_client = globus_sdk.GCSClient(DATAFED_GCS_URL, authorizer=authorizer)
+
+# The scope the client will need, note that primary scope is for the endpoint,
+# but it has a dependency on the mapped collection's data_access scope
+
+# Build a GCSClient to act as the client by using a ClientCredentialsAuthorizor
+confidential_client = globus_sdk.ConfidentialAppAuthClient(
+    client_id=client_id, client_secret=client_secret
+)
+
+token_response = confidential_client.oauth2_client_credentials_tokens()
+globus_transfer_data = token_response.by_resource_server["transfer.api.globus.org"]
+globus_transfer_token = globus_transfer_data["access_token"]
+authorizer_tc = globus_sdk.AccessTokenAuthorizer(globus_transfer_token)
+tc = globus_sdk.TransferClient(authorizer=authorizer_tc)
+
+scope = scopes.GCSEndpointScopeBuilder(ENDPOINT_ID).make_mutable("manage_collections")
+authorizer = globus_sdk.ClientCredentialsAuthorizer(confidential_client, scopes=scope)
+client = globus_sdk.GCSClient(DATAFED_GCS_URL, authorizer=authorizer)
+
+collection_list = client.get_collection_list()
+print("collection_list")
+print(collection_list)
+
+mapped_collection_found = False
+
+for item in collection_list["data"]:
+    print(item["display_name"])
+    if mapped_collection_id is not None:
+        if item["id"] == mapped_collection_id:
+            mapped_collection_found = True
+            if item["display_name"] != mapped_collection_name:
+                raise Exception(
+                    "Expected display name is different from what "
+                    "is expected for mapped collection "
+                    f"{mapped_collection_id}, if using non standard"
+                    " display name for mapped collection "
+                    f"{mapped_collection_name} then the "
+                    "MAPPED_COLLECTION_NAME env variable must be "
+                    "set."
+                )
+            break
+    elif item["display_name"] == mapped_collection_name:
+        mapped_collection_found = True
+        mapped_collection_id = item["id"]
+        break
+
+if mapped_collection_found is False:
+    raise Exception("Missing required mapped collection")
+
+storage_gateway_found = False
+storage_gateway_list = client.get_storage_gateway_list()
+for item in storage_gateway_list["data"]:
+    print(item["display_name"])
+    if storage_gateway_id is not None:
+        if item["id"] == storage_gateway_id:
+            storage_gateway_found = True
+            if item["display_name"] != storage_gateway_name:
+                raise Exception(
+                    "Expected display name is different from what "
+                    "is expected for storage gateway "
+                    f"{storage_gateway_id}, if using non standard"
+                    " display name for storage gateway "
+                    f"{storage_gateway_name} then the "
+                    "DATAFED_GCS_STORAGE_GATEWAY env variable must be "
+                    "set."
+                )
+            break
+    elif item["display_name"] == storage_gateway_name:
+        storage_gateway_found = True
+        storage_gateway_id = item["id"]
+        break
+
+if storage_gateway_found is False:
+    raise Exception("Missing required storage gateway")
+
+
+# Get the mapped collection id
+scope.add_dependency(scopes.GCSCollectionScopeBuilder(mapped_collection_id).data_access)
+authorizer = globus_sdk.ClientCredentialsAuthorizer(confidential_client, scopes=scope)
+client = globus_sdk.GCSClient(DATAFED_GCS_URL, authorizer=authorizer)
+
+guest_collection_found = False
+
+for item in collection_list["data"]:
+    print(item["display_name"])
+    if item["display_name"] == guest_collection_name:
+        guest_collection_found = True
+        guest_collection_id = item["id"]
+        break
+
+# https://github.com/globus/globus-sdk-python/blob/main/docs/examples/guest_collection_creation.rst
+if guest_collection_found is False:
+    credential_document = globus_sdk.UserCredentialDocument(
+        storage_gateway_id=storage_gateway_id,
+        identity_id=client_id,
+        username=local_username,
+    )
+    client.create_user_credential(credential_document)
+
+    # Create the collection
+    collection_document = globus_sdk.GuestCollectionDocument(
+        public="True",
+        collection_base_path="/",
+        display_name=guest_collection_name,
+        mapped_collection_id=mapped_collection_id,
+    )
+    response = client.create_collection(collection_document)
+    guest_collection_id = response["id"]
+    print(f"guest collection {guest_collection_id} created")
+
+
+# Create ACL rule for Guest anonymous access
+acl_list = tc.endpoint_acl_list(endpoint_id=guest_collection_id)
+create_acl = True
+update_acl = False
+acl_id = None
+for item in acl_list["DATA"]:
+    if item["principal_type"] == "all_authenticated_users":
+        create_acl = False
+        acl_id = item["id"]
+        if item["permissions"] != "rw":
+            print("Need to update acl for all users permissions are incorrect")
+            update_acl = True
+        else:
+            print("ACL rule already exists for all users")
+        break
+
+rule_data = {
+    "DATA_TYPE": "access",
+    "path": "/",
+    "permissions": "rw",
+    "principal": "",
+    "principal_type": "all_authenticated_users",
+    "role_id": None,
+    "role_type": None,
+}
+if create_acl:
+    print(f"Creating acl rule for guest_collection {guest_collection_id}")
+    tc.add_endpoint_acl_rule(endpoint_id=guest_collection_id, rule_data=rule_data)
+elif update_acl:
+    print(f"Updating acl rule ({acl_id}) for guest_collection {guest_collection_id}")
+    tc.update_endpoint_acl_rule(
+        endpoint_id=guest_collection_id, rule_id=acl_id, rule_data=rule_data
+    )
+
+acl_list = tc.endpoint_acl_list(endpoint_id=guest_collection_id)
+print(acl_list)
diff --git a/scripts/globus/generate_repo_form.sh b/scripts/globus/generate_repo_form.sh
index ba4516dff..e279a1cce 100755
--- a/scripts/globus/generate_repo_form.sh
+++ b/scripts/globus/generate_repo_form.sh
@@ -27,9 +27,9 @@ then
   exit 1
 fi
 
-if [ -z "$GCS_COLLECTION_ROOT_PATH" ]
+if [ -z "$DATAFED_GCS_COLLECTION_ROOT_PATH" ]
 then
-  echo "GCS_COLLECTION_ROOT_PATH is not defined cannot run $SCRIPT"
+  echo "DATAFED_GCS_COLLECTION_ROOT_PATH is not defined cannot run $SCRIPT"
   exit 1
 fi
 
@@ -93,7 +93,7 @@ public_key=$(cat ${DATAFED_INSTALL_PATH}/keys/datafed-repo-key.pub)
 GATEWAY_NAME="${DATAFED_GCS_ROOT_NAME} Storage Gateway"
 GUEST_COLLECTION_NAME="${DATAFED_GCS_ROOT_NAME} Collection Guest"
 MAPPED_COLLECTION_NAME="${DATAFED_GCS_ROOT_NAME} Collection Mapped"
-PATH_TO_GUEST_ROOT="${GCS_COLLECTION_ROOT_PATH}"
+PATH_TO_GUEST_ROOT="${DATAFED_GCS_COLLECTION_ROOT_PATH}"
 
 uuid_of_collection=$( globus-connect-server collection list | grep "$GUEST_COLLECTION_NAME" | awk '{ print $1 }')
 
@@ -108,7 +108,7 @@ fi
 
 # Probably should grab this from the config file
 local_DATAFED_REPO_EGRESS_PORT="9000"
-repo_domain_name=$(domainname -A | xargs)
+repo_domain_name=$(domainname -A | awk '{print $1}')
 
 if [ -z "$repo_domain_name" ]
 then
@@ -125,7 +125,7 @@ then
   echo "export DATAFED_REPO_SERVER_ADDRESS=\"tcp://$repo_domain_name:$local_DATAFED_REPO_EGRESS_PORT\"" >> ${OUTPUT_SCRIPT_NAME}
   echo "export DATAFED_REPO_PUBLIC_KEY=\"$public_key\"" >> ${OUTPUT_SCRIPT_NAME}
   echo "export DATAFED_REPO_ENDPOINT_UUID=\"$uuid_of_collection\"" >> ${OUTPUT_SCRIPT_NAME}
-  echo "export DATAFED_REPO_RELATIVE_PATH=\"\\$DATAFED_REPO_ID_AND_DIR\"" >> ${OUTPUT_SCRIPT_NAME}
+  echo "export DATAFED_REPO_RELATIVE_PATH=\"${PATH_TO_GUEST_ROOT}/$DATAFED_REPO_ID_AND_DIR\"" >> ${OUTPUT_SCRIPT_NAME}
   echo "export DATAFED_REPO_DOMAIN=\"\"" >> ${OUTPUT_SCRIPT_NAME}
   echo "export DATAFED_REPO_EXPORT_PATH=\"\"" >> ${OUTPUT_SCRIPT_NAME}
   echo "export DATAFED_REPO_CAPACITY=\"\"" >> ${OUTPUT_SCRIPT_NAME}
@@ -141,7 +141,7 @@ then
   echo "address=\"tcp://$repo_domain_name:$local_DATAFED_REPO_EGRESS_PORT\"" >> ${OUTPUT_SCRIPT_NAME}
   echo "pub_key=\"$public_key\"" >> ${OUTPUT_SCRIPT_NAME}
   echo "endpoint=\"$uuid_of_collection\"" >> ${OUTPUT_SCRIPT_NAME}
-  echo "path=\"\\$DATAFED_REPO_ID_AND_DIR\"" >> ${OUTPUT_SCRIPT_NAME}
+  echo "path=\"${PATH_TO_GUEST_ROOT}/$DATAFED_REPO_ID_AND_DIR\"" >> ${OUTPUT_SCRIPT_NAME}
   echo "domain=\"\"" >> ${OUTPUT_SCRIPT_NAME}
   echo "exp_path=\"\"" >> ${OUTPUT_SCRIPT_NAME}
   echo "capacity=\"\"" >> ${OUTPUT_SCRIPT_NAME}
@@ -159,7 +159,7 @@ then
   echo "  \"address\": \"tcp://$repo_domain_name:$local_DATAFED_REPO_EGRESS_PORT\"," >> ${OUTPUT_SCRIPT_NAME}
   echo "  \"pub_key\": \"$public_key\"," >> ${OUTPUT_SCRIPT_NAME}
   echo "  \"endpoint\": \"$uuid_of_collection\"," >> ${OUTPUT_SCRIPT_NAME}
-  echo "  \"path\": \"/$DATAFED_REPO_ID_AND_DIR\"," >> ${OUTPUT_SCRIPT_NAME}
+  echo "  \"path\": \"${PATH_TO_GUEST_ROOT}/$DATAFED_REPO_ID_AND_DIR\"," >> ${OUTPUT_SCRIPT_NAME}
   echo "  \"domain\": \"\"," >> ${OUTPUT_SCRIPT_NAME}
   echo "  \"exp_path\": \"\"," >> ${OUTPUT_SCRIPT_NAME}
   echo "  \"capacity\": 0," >> ${OUTPUT_SCRIPT_NAME}
@@ -176,7 +176,7 @@ echo "Description: A description of the repository."
 echo "Srvr. Address: tcp://$repo_domain_name:$local_DATAFED_REPO_EGRESS_PORT"
 echo "Public Key: $public_key"
 echo "End-point ID: $uuid_of_collection"
-echo "Path: /$DATAFED_REPO_ID_AND_DIR"
+echo "Path: ${PATH_TO_GUEST_ROOT}/$DATAFED_REPO_ID_AND_DIR"
 echo "Domain: "
 # I don't know what this is
 echo "Export Path: "
diff --git a/scripts/globus/globus_cleanup.py b/scripts/globus/globus_cleanup.py
new file mode 100644
index 000000000..3ca6a6965
--- /dev/null
+++ b/scripts/globus/globus_cleanup.py
@@ -0,0 +1,208 @@
+import globus_sdk
+from globus_sdk import AuthClient, GroupsClient
+from globus_sdk.scopes import GroupsScopes
+import subprocess
+import sys
+import os
+import utils
+
+# Native Client ID for the DataFed Globus Setup Scripts
+CLIENT_ID = "f8d0afca-7ac4-4a3c-ac05-f94f5d9afce8"  # NATIVE
+
+# The Globus project the GCS endpoint will be created in
+if os.getenv("DATAFED_GCS_ROOT_NAME") is not None:
+    DATAFED_GCS_ROOT_NAME = os.getenv("DATAFED_GCS_ROOT_NAME")
+else:
+    DATAFED_GCS_ROOT_NAME = "DataFed Repo"
+
+if os.getenv("DATAFED_GLOBUS_PROJECT_NAME") is not None:
+    PROJECT_NAME = os.getenv("DATAFED_GLOBUS_PROJECT_NAME")
+else:
+    PROJECT_NAME = DATAFED_GCS_ROOT_NAME + " Project"
+
+# This is for confidential client
+if os.getenv("DATAFED_GLOBUS_CLIENT_NAME") is not None:
+    CLIENT_NAME = os.getenv("DATAFED_GLOBUS_CLIENT_NAME")
+else:
+    CLIENT_NAME = DATAFED_GCS_ROOT_NAME + " Setup Client"
+
+# Name of the client secret used by the confidential client
+if os.getenv("DATAFED_GLOBUS_CRED_NAME") is not None:
+    CRED_NAME = os.getenv("DATAFED_GLOBUS_CRED_NAME")
+else:
+    CRED_NAME = DATAFED_GCS_ROOT_NAME + " Cred"
+
+# Name of the file where we will store confidential client credentials
+if os.getenv("DATAFED_GLOBUS_CRED_FILE_PATH") is not None:
+    CRED_FILE_PATH = os.getenv("DATAFED_GLOBUS_CRED_FILE_PATH")
+else:
+    CRED_FILE_PATH = "./client_cred.json"
+
+# Name to give to endpoint
+if os.getenv("DATAFED_GLOBUS_ENDPOINT_NAME") is not None:
+    ENDPOINT_NAME = os.getenv("DATAFED_GLOBUS_ENDPOINT_NAME")
+else:
+    ENDPOINT_NAME = DATAFED_GCS_ROOT_NAME + " Endpoint"
+
+# Path to deployment key
+if os.getenv("DATAFED_GLOBUS_DEPLOYMENT_KEY_PATH") is not None:
+    DEPLOYMENT_KEY_PATH = os.getenv("DATAFED_GLOBUS_DEPLOYMENT_KEY_PATH")
+else:
+    DEPLOYMENT_KEY_PATH = "./deployment-key.json"
+
+
+client = globus_sdk.NativeAppAuthClient(CLIENT_ID)
+# manage_projects scope to create a project
+group_scope = GroupsScopes.make_mutable("all")
+client.oauth2_start_flow(
+    requested_scopes="openid profile email "
+    "urn:globus:auth:scope:auth.globus.org:manage_projects "
+    "urn:globus:auth:scope:auth.globus.org:view_identities " + str(group_scope),
+    refresh_tokens=True,
+)
+
+authorize_url = client.oauth2_get_authorize_url(query_params={"prompt": "login"})
+print("Please go to this URL and login: \n", authorize_url)
+auth_code = input("Please enter the authorization code: ")
+
+token_response = client.oauth2_exchange_code_for_tokens(auth_code)
+
+print("Token Response is")
+
+print(token_response)
+
+refresh_token_auth = token_response.by_resource_server["auth.globus.org"][
+    "refresh_token"
+]
+refresh_token_groups = token_response.by_resource_server["groups.api.globus.org"][
+    "refresh_token"
+]
+
+rt_authorizer = globus_sdk.RefreshTokenAuthorizer(refresh_token_auth, client)
+
+rt_authorizer_groups = globus_sdk.RefreshTokenAuthorizer(refresh_token_groups, client)
+ac_rt = AuthClient(authorizer=rt_authorizer)
+gr_rt = GroupsClient(authorizer=rt_authorizer_groups)
+
+userinfo = ac_rt.oauth2_userinfo()
+
+# Will get the primary email and id
+identity_id = userinfo["sub"]
+email = userinfo["email"]
+username = userinfo["preferred_username"]
+organization = userinfo["identity_provider_display_name"]
+
+# check if project exists already
+project_exists = utils.projectExists(ac_rt, PROJECT_NAME)
+
+if project_exists:
+
+    projects = ac_rt.get_projects()
+    project_id = utils.getProjectId(projects, PROJECT_NAME)
+    print(f"project id is {project_id}")
+    clients_in_project = utils.getClientsInProject(ac_rt, project_id)
+
+    if len(clients_in_project) == 0:
+        print(
+            "No clients were detected in the project we can just delete the"
+            "project and be done."
+        )
+    else:
+
+        # Check if the deployment key exists if it does read it and verify that the
+        # client exists for the globus connect server if it does not then we will
+        # call the setup command
+
+        gcs_id_from_deployment_key = utils.getGCSClientIDFromDeploymentFile(
+            DEPLOYMENT_KEY_PATH
+        )
+
+        valid_key = utils.isGCSDeploymentKeyValid(
+            ac_rt, project_id, ENDPOINT_NAME, gcs_id_from_deployment_key
+        )
+
+        all_gcs_client_ids = utils.getAllGCSClientIds(ac_rt, project_id, ENDPOINT_NAME)
+
+        if valid_key is False and len(all_gcs_client_ids) > 0:
+            print(
+                "Looks like gcs client does not exist in the cloud"
+                f" for the project: {project_id}."
+                "Maybe you have the wrong deployment key cloud_ids {all_gcs_client_ids}"
+                f"deployment key id {gcs_id_from_deployment_key}"
+            )
+            sys.exit(1)
+
+        if gcs_id_from_deployment_key is None and len(all_gcs_client_ids) > 0:
+            print(
+                "Looks like deployment key does not exist, please either "
+                "add the correct deployment."
+                f" cloud_ids {all_gcs_client_ids}"
+                f"deployment key id {gcs_id_from_deployment_key}"
+            )
+            sys.exit(1)
+
+        if len(all_gcs_client_ids) > 0:
+
+            if utils.command_exists("globus-connect-server") is False:
+                print(
+                    "Cannot create deployment key, we require globus-connect-server to be installed"
+                )
+                sys.exit(1)
+
+            else:
+
+                print(
+                    "Now that we know a GCS instance exists we have to make sure"
+                    "we have valid credentials to run the globus-connect-server command"
+                    "non interatively, this means we have to create credentials and a"
+                    "client if they don't exist and when we are done with everything"
+                    "delete them."
+                )
+
+                client_id, client_secret = utils.createClient(
+                    ac_rt, CLIENT_NAME, project_id, CRED_NAME, CRED_FILE_PATH
+                )
+
+                ac_rt.update_project(project_id, admin_ids=[identity_id, client_id])
+
+                bash_command = f'GCS_CLI_CLIENT_ID="{client_id}" '
+                bash_command = f'GCS_CLI_CLIENT_SECRET="{client_secret}" '
+                bash_command += "globus-connect-server endpoint cleanup "
+                bash_command += f' --deployment-key "{DEPLOYMENT_KEY_PATH}" '
+                bash_command += " --agree-to-delete-endpoint"
+                print("Bash command to run")
+                print(bash_command)
+
+                proc = subprocess.Popen(
+                    bash_command,
+                    stdin=subprocess.PIPE,
+                    stdout=subprocess.PIPE,
+                    stderr=subprocess.PIPE,
+                    universal_newlines=True,
+                    shell=True,
+                    text=True,
+                )
+
+                output, error = proc.communicate(input="yes\n")
+
+                # Print the output and error, if any
+                print(output)
+                print(error)
+
+        # Now we can try to delete the remaining clients that are in the project
+        # Get all of the clients that are not gcs clients and delete them
+
+        utils.deleteAllNonGCSClients(ac_rt, project_id)
+
+    # CLOSE - if len(clients_in_project) == 0:
+
+    # Try to remove project this will only work if there are no other clients in
+    # the project
+    print(f"Attempting to remove project {project_id}")
+    project_remove = ac_rt.delete_project(project_id)
+    print(project_remove)
+
+# Now trying to clean up groups
+group_display_name = f"{DATAFED_GCS_ROOT_NAME} Group"
+
+utils.deleteGroup(gr_rt, group_display_name)
diff --git a/scripts/globus/initialize_globus_endpoint.py b/scripts/globus/initialize_globus_endpoint.py
new file mode 100644
index 000000000..2aa989cae
--- /dev/null
+++ b/scripts/globus/initialize_globus_endpoint.py
@@ -0,0 +1,184 @@
+import globus_sdk
+import utils
+from globus_sdk import AuthClient, GroupsClient
+from globus_sdk.scopes import GroupsScopes
+
+import os
+import sys
+
+# Hard coded Native Client ID
+CLIENT_ID = "f8d0afca-7ac4-4a3c-ac05-f94f5d9afce8"
+
+# The Globus project the GCS endpoint will be created in
+if os.getenv("DATAFED_GCS_ROOT_NAME") is not None:
+    DATAFED_GCS_ROOT_NAME = os.getenv("DATAFED_GCS_ROOT_NAME")
+else:
+    DATAFED_GCS_ROOT_NAME = "DataFed Repo"
+
+if os.getenv("DATAFED_GLOBUS_PROJECT_NAME") is not None:
+    PROJECT_NAME = os.getenv("DATAFED_GLOBUS_PROJECT_NAME")
+else:
+    PROJECT_NAME = DATAFED_GCS_ROOT_NAME + " Project"
+
+# This is for confidential client
+if os.getenv("DATAFED_GLOBUS_CLIENT_NAME") is not None:
+    CLIENT_NAME = os.getenv("DATAFED_GLOBUS_CLIENT_NAME")
+else:
+    CLIENT_NAME = DATAFED_GCS_ROOT_NAME + " Setup Client"
+
+# Name of the client secret used by the confidential client
+if os.getenv("DATAFED_GLOBUS_CRED_NAME") is not None:
+    CRED_NAME = os.getenv("DATAFED_GLOBUS_CRED_NAME")
+else:
+    CRED_NAME = DATAFED_GCS_ROOT_NAME + " Cred"
+
+# Name of the file where we will store confidential client credentials
+if os.getenv("DATAFED_GLOBUS_CRED_FILE_PATH") is not None:
+    CRED_FILE_PATH = os.getenv("DATAFED_GLOBUS_CRED_FILE_PATH")
+else:
+    CRED_FILE_PATH = "./client_cred.json"
+
+# Name to give to endpoint
+if os.getenv("DATAFED_GLOBUS_ENDPOINT_NAME") is not None:
+    ENDPOINT_NAME = os.getenv("DATAFED_GLOBUS_ENDPOINT_NAME")
+else:
+    ENDPOINT_NAME = DATAFED_GCS_ROOT_NAME + " Endpoint"
+
+# Path to deployment key
+if os.getenv("DATAFED_GLOBUS_DEPLOYMENT_KEY_PATH") is not None:
+    DEPLOYMENT_KEY_PATH = os.getenv("DATAFED_GLOBUS_DEPLOYMENT_KEY_PATH")
+else:
+    DEPLOYMENT_KEY_PATH = "./deployment-key.json"
+
+# Path to deployment key
+if os.getenv("DATAFED_GLOBUS_CONTROL_PORT") is not None:
+    DATAFED_GLOBUS_CONTROL_PORT = os.getenv("DATAFED_GLOBUS_CONTROL_PORT")
+else:
+    DATAFED_GLOBUS_CONTROL_PORT = "443"
+
+if os.getenv("DATAFED_GLOBUS_SUBSCRIPTION") is not None:
+    DATAFED_GLOBUS_SUBSCRIPTION = os.getenv("DATAFED_GLOBUS_SUBSCRIPTION")
+else:
+    DATAFED_GLOBUS_SUBSCRIPTION = None
+
+client = globus_sdk.NativeAppAuthClient(CLIENT_ID)
+
+# manage_projects scope to create a project
+# view_identities to user information for creating GCS server
+
+group_scope = GroupsScopes.make_mutable("all")
+client.oauth2_start_flow(
+    requested_scopes="openid profile email "
+    "urn:globus:auth:scope:auth.globus.org:manage_projects "
+    "urn:globus:auth:scope:auth.globus.org:view_identities " + str(group_scope),
+    refresh_tokens=True,
+)
+
+authorize_url = client.oauth2_get_authorize_url(query_params={"prompt": "login"})
+print("Please go to this URL and login: \n", authorize_url)
+auth_code = input("Please enter the authorization code: ")
+
+token_response = client.oauth2_exchange_code_for_tokens(auth_code)
+# Extract the token
+refresh_token_auth = token_response.by_resource_server["auth.globus.org"][
+    "refresh_token"
+]
+refresh_token_groups = token_response.by_resource_server["groups.api.globus.org"][
+    "refresh_token"
+]
+rt_authorizer = globus_sdk.RefreshTokenAuthorizer(refresh_token_auth, client)
+
+rt_authorizer_groups = globus_sdk.RefreshTokenAuthorizer(refresh_token_groups, client)
+# auth_client_refresh_token
+ac_rt = AuthClient(authorizer=rt_authorizer)
+gr_rt = GroupsClient(authorizer=rt_authorizer_groups)
+
+userinfo = ac_rt.oauth2_userinfo()
+# Will get the primary email and id
+identity_id = userinfo["sub"]
+email = userinfo["email"]
+username = userinfo["preferred_username"]
+print("username")
+print(username)
+print("userinfo")
+print(userinfo)
+organization = userinfo["identity_provider_display_name"]
+
+# Need to determine the project uuid
+if utils.projectExists(ac_rt, PROJECT_NAME) is False:
+    project_id = utils.createProject(ac_rt, PROJECT_NAME, userinfo)
+else:
+    project_id = utils.getProjectId(ac_rt, PROJECT_NAME, userinfo)
+
+count = utils.countProjects(ac_rt, PROJECT_NAME)
+
+if count != 1:
+    print(
+        "Something is wrong there should be at least one project with name"
+        f" {PROJECT_NAME} instead there are {count} with that name"
+    )
+    sys.exit(1)
+
+print(f"Project id is {project_id}")
+client_id, client_secret = utils.createClient(
+    ac_rt, CLIENT_NAME, project_id, CRED_NAME, CRED_FILE_PATH
+)
+
+# Add the globus client as an admin to the project
+ac_rt.update_project(project_id, admin_ids=[identity_id, client_id])
+
+# Get clients in project
+clients_in_project = utils.getClientsInProject(ac_rt, project_id)
+
+# Check if the deployment key exists if it does read it and verify that the
+# client exists for the globus connect server if it does not then we will
+# call the setup command
+utils.createGCSEndpoint(
+    ac_rt,
+    client_id,
+    client_secret,
+    project_id,
+    DEPLOYMENT_KEY_PATH,
+    ENDPOINT_NAME,
+    DATAFED_GLOBUS_CONTROL_PORT,
+    userinfo,
+)
+
+if DATAFED_GLOBUS_SUBSCRIPTION is not None:
+    # Create subscription subgroup
+    results = gr_rt.get_group_by_subscription_id(DATAFED_GLOBUS_SUBSCRIPTION)
+
+    parent_group_id = results["group_id"]
+    print("Groups by sub")
+    print(results)
+    group_name = f"{DATAFED_GCS_ROOT_NAME} Group"
+
+    if utils.groupExists(gr_rt, group_name):
+        print("Group exists already")
+        group_id = utils.getGroupId(gr_rt, group_name)
+    else:
+        print(f"Group does not exist {group_name}")
+        package = {
+            "name": group_name,
+            "description": "DataFed Repository Subscription Group, used for"
+            "granting access to the application client to setup the repository in "
+            "Globus",
+            "parent_id": str(parent_group_id),
+        }
+
+        result = gr_rt.create_group(package)
+        group_id = result["id"]
+
+    print("group id")
+    print(group_id)
+
+    batch = globus_sdk.BatchMembershipActions()
+    batch.add_members(client_id, role="admin")
+    result = gr_rt.batch_membership_action(group_id, batch)
+
+    print("membership_action")
+    print(result)
+    package = {"subscription_id": DATAFED_GLOBUS_SUBSCRIPTION}
+    result = gr_rt.update_group(group_id, package)
+    print("update group")
+    print(result)
diff --git a/scripts/globus/setup_globus.sh b/scripts/globus/setup_globus.sh
index 2006744c7..421fbbd65 100755
--- a/scripts/globus/setup_globus.sh
+++ b/scripts/globus/setup_globus.sh
@@ -13,9 +13,9 @@ then
   exit 1
 fi
 
-if [ -z "$GCS_COLLECTION_ROOT_PATH" ]
+if [ -z "$DATAFED_GCS_COLLECTION_ROOT_PATH" ]
 then
-  echo "GCS_COLLECTION_ROOT_PATH is not defined cannot run $SCRIPT"
+  echo "DATAFED_GCS_COLLECTION_ROOT_PATH is not defined cannot run $SCRIPT"
   exit 1
 fi
 
@@ -25,18 +25,42 @@ then
   exit 1
 fi
 
+if [ -z "$DATAFED_GLOBUS_CRED_FILE_PATH" ]
+then
+  echo "DATAFED_GLOBUS_CRED_FILE_PATH is not defined cannot run $DATAFED_GLOBUS_CRED_FILE_PATH"
+  exit 1
+else
+  CRED_FILE_PATH="$DATAFED_GLOBUS_CRED_FILE_PATH"
+fi
+
+if [ -z "${DATAFED_GLOBUS_SUBSCRIPTION}" ]
+then
+  echo "DATAFED_GLOBUS_SUBSCRIPTION not defined"
+  DATAFED_GLOBUS_SUBSCRIPTION=""
+else
+  DATAFED_GLOBUS_SUBSCRIPTION=$(printenv DATAFED_GLOBUS_SUBSCRIPTION)
+fi
+
+if [ -z "$DATAFED_GLOBUS_ALLOWED_DOMAINS" ]
+then
+  echo "DATAFED_GLOBUS_ALLOWED_DOMAINS is not defined shoudl be i.e. globusid.org or gmail.com or ornl.gov or cu.edu or something of the sort."
+  exit 1
+fi
+
+if [ -f "$CRED_FILE_PATH" ]; then
+    echo "File exists! $CRED_FILE_PATH"
+else
+    echo "File does not exist. $CRED_FILE_PATH"
+		echo "run the Globus python script first"
+		exit 1
+fi
+
 GATEWAY_NAME="${DATAFED_GCS_ROOT_NAME} Storage Gateway"
 COLLECTION_NAME="${DATAFED_GCS_ROOT_NAME} Collection Mapped"
 GUEST_COLLECTION_NAME="${DATAFED_GCS_ROOT_NAME} Collection Guest"
 
 gateway_line=$( globus-connect-server storage-gateway list | grep "$GATEWAY_NAME" )
-#    {
-#      "source": "{id}",
-#      "match": "${DATAFED_GLOBUS_APP_ID}@clients.auth.globus.org",
-#      "output": "${DATAFED_GLOBUS_REPO_USER}",
-#      "ignore_case": false,
-#      "literal": true
-#    }
+
 echo "$DATAFED_GLOBUS_REPO_USER"
 cat << EOF > mapping.json 
 {
@@ -53,8 +77,14 @@ cat << EOF > mapping.json
 }
 EOF
 
-#DOMAINS="--domain ornl.gov --domain clients.auth.globus.org --domain gmail.com"
-DOMAINS="--domain ornl.gov"
+# For the Globus client to create the guest collection need to allow client
+# from the domain
+DOMAINS="--domain $DATAFED_GLOBUS_ALLOWED_DOMAINS --domain clients.auth.globus.org"
+
+echo "{" > path_restriction.json
+echo "  \"DATA_TYPE\": \"path_restrictions#1.0.0\"," >> path_restriction.json
+echo "  \"read_write\": [\"${DATAFED_GCS_COLLECTION_ROOT_PATH}\"]" >> path_restriction.json
+echo "}" >> path_restriction.json
 
 if [ -z "$gateway_line" ]
 then
@@ -62,6 +92,7 @@ then
   globus-connect-server storage-gateway create posix \
     "$GATEWAY_NAME" \
     ${DOMAINS}  \
+    --restrict-paths file:path_restriction.json \
     --identity-mapping file:mapping.json
 
 else
@@ -72,16 +103,15 @@ else
 
   globus-connect-server storage-gateway update posix \
     "$uuid_of_storage_gateway" \
+    --restrict-paths file:path_restriction.json \
     ${DOMAINS} \
     --identity-mapping file:mapping.json
 
 fi
 
-PATH_TO_GUEST_ROOT="${GCS_COLLECTION_ROOT_PATH}"
-
 # Create project/ and /user folders
-#mkdir -p "${PATH_TO_GUEST_ROOT}/${DATAFED_REPO_ID_AND_DIR}/user"
-#mkdir -p "${PATH_TO_GUEST_ROOT}/${DATAFED_REPO_ID_AND_DIR}/project"
+mkdir -p "${DATAFED_GCS_COLLECTION_ROOT_PATH}/${DATAFED_REPO_ID_AND_DIR}/user"
+mkdir -p "${DATAFED_GCS_COLLECTION_ROOT_PATH}/${DATAFED_REPO_ID_AND_DIR}/project"
 
 collection_line=$( globus-connect-server collection list | grep "$COLLECTION_NAME" )
 
@@ -89,33 +119,41 @@ spaces_in_name=$(echo $GATEWAY_NAME | awk '{print gsub("[ \t]",""); exit}')
 columns=$(( $spaces_in_name + 3 ))
 uuid_of_storage_gateway=$( globus-connect-server storage-gateway list | grep "$GATEWAY_NAME" | awk -v col=$columns '{ print $col }')
 
+# If not empty
+extra_collection_arg=""
+if [ -n "${DATAFED_GLOBUS_SUBSCRIPTION}" ]
+then
+  globus-connect-server endpoint set-subscription-id "${DATAFED_GLOBUS_SUBSCRIPTION}"
+  extra_collection_arg="--allow-guest-collections"
+fi
+
 if [ -z "$collection_line" ]
 then
 
+  # NOTE enable-anonymous-writes is allowed without a subscription
+  # NOTE allow-guest-collections requires a subscription
   globus-connect-server collection create \
     "$uuid_of_storage_gateway" \
     "/" \
     "$COLLECTION_NAME" \
-    --allow-guest-collections \
     --enable-anonymous-writes \
-    --default-directory "${GCS_COLLECTION_ROOT_PATH}" \
-    --disable-https
+    --default-directory "/" \
+    --disable-https "$extra_collection_arg"
 else
 
   uuid_of_collection=$( globus-connect-server collection list | grep "$COLLECTION_NAME" | awk '{ print $1 }')
-  
+  # NOTE enable-anonymous-writes is allowed without a subscription
+  # NOTE allow-guest-collections requires a subscription
   globus-connect-server collection update \
     "$uuid_of_collection" \
-    --allow-guest-collections \
     --enable-anonymous-writes \
-    --default-directory "${GCS_COLLECTION_ROOT_PATH}" \
-    --disable-https
-
+    --default-directory "/" \
+    --disable-https "$extra_collection_arg"
 fi
 
-echo "When creating a guest collection it must be created in: $PATH_TO_GUEST_ROOT"
+echo "When creating a guest collection it must be created in: $DATAFED_GCS_COLLECTION_ROOT_PATH"
 echo "And the display name should be exactly: $GUEST_COLLECTION_NAME"
 echo "You will also need to add permissions for all Globus users so that they have write access."
 echo ""
 echo "When registering the repository with DataFed the ID must be: $DATAFED_REPO_ID_AND_DIR"
-echo "When registering the repository with DataFed path is abs to the mapped collection and must be listed as: ${PATH_TO_GUEST_ROOT}/${DATAFED_REPO_ID_AND_DIR}"
+echo "When registering the repository with DataFed path is abs to the mapped collection and must be listed as: ${DATAFED_GCS_COLLECTION_ROOT_PATH}/${DATAFED_REPO_ID_AND_DIR}"
diff --git a/scripts/globus/utils.py b/scripts/globus/utils.py
new file mode 100644
index 000000000..424afd7b4
--- /dev/null
+++ b/scripts/globus/utils.py
@@ -0,0 +1,371 @@
+import subprocess
+import json
+import os
+import sys
+
+
+def getProjectId(projects, project_name):
+    for project in projects:
+        if project["display_name"] == project_name:
+            return project["id"]
+    return None
+
+
+def projectExists(auth_client, project_name):
+    projects = auth_client.get_projects()
+    project_id = getProjectId(projects, project_name)
+
+    project_exists = True
+    if project_id is None:
+        project_exists = False
+    return project_exists
+
+
+def createProject(auth_client, project_name, userinfo):
+
+    identity_id = userinfo["sub"]
+    email = userinfo["email"]
+
+    project_exists = projectExists(auth_client, project_name)
+
+    if project_exists is False:
+        project_create_result = auth_client.create_project(
+            project_name, contact_email=email, admin_ids=[identity_id]
+        )
+        return project_create_result["project"]["id"]
+
+    projects = auth_client.get_projects()
+    return getProjectId(projects, project_name)
+
+
+def countProjects(auth_client, project_name):
+    projects = auth_client.get_projects()
+    count = 0
+    for project in projects:
+        if project["display_name"] == project_name:
+            count += 1
+    return count
+
+
+def getClientId(auth_client, client_name, project_id):
+    get_client_result = auth_client.get_clients()
+    for client in get_client_result["clients"]:
+        if client["name"] == client_name and client["project"] == project_id:
+            return client["id"]
+    return None
+
+
+def getAllGCSClientIds(auth_client, project_id, endpoint_name):
+    clients_in_project = getClientsInProject(auth_client, project_id)
+    all_gcs_client_ids = []
+    for client in clients_in_project:
+        if (
+            client["client_type"] == "globus_connect_server"
+            and client["name"] == endpoint_name
+        ):
+            all_gcs_client_ids.append(client["id"])
+    return all_gcs_client_ids
+
+
+def getClientsInProject(auth_client, project_id):
+    # Get clients in project
+    get_client_result = auth_client.get_clients()
+    clients_in_project = []
+    for client in get_client_result["clients"]:
+        if client["project"] == project_id:
+            clients_in_project.append(client)
+    return clients_in_project
+
+
+def createNewClient(auth_client, client_name, project_id):
+    client_id = getClientId(auth_client, client_name, project_id)
+
+    client_exists = False
+    if client_id:
+        client_exists = True
+
+    if client_exists is False:
+        result = auth_client.create_client(
+            client_name, project=project_id, public_client=False
+        )
+        client_id = result["client"]["id"]
+
+    return client_id
+
+
+def getCredentialID(auth_client, client_id, cred_name):
+    get_client_cred_result = auth_client.get_client_credentials(client_id)
+    for cred in get_client_cred_result["credentials"]:
+        if cred["name"] == cred_name:
+            return cred["id"]
+    return None
+
+
+def groupExists(client, group_name):
+    my_groups = client.get_my_groups()
+    print("My groups")
+    print(my_groups)
+    for group in my_groups:
+        if group["name"] == group_name:
+            return True
+    return False
+
+
+def getGroupId(client, group_name):
+    my_groups = client.get_my_groups()
+    for group in my_groups:
+        if group["name"] == group_name:
+            return group["id"]
+    return None
+
+
+def deleteGroup(client, group_name):
+    my_groups = client.get_my_groups()
+    for group in my_groups:
+        if group["name"] == group_name:
+            result = client.delete_group(group["id"])
+            print(f"Removing group: {group_name} with id: {group['id']}")
+            print(result)
+
+
+def validFile(file_name):
+    file_exists = False
+    file_empty = True
+    if os.path.exists(file_name):
+        file_exists = True
+        file_size = os.path.getsize(file_name)
+        # Check if the file size is 0 (empty)
+        if file_size != 0:
+            file_empty = False
+    return file_exists, file_empty
+
+
+def getCredentialFromFile(cred_file_name, cred_id):
+    # Check to see if the local secret is the same id and not just the same
+    # name
+    _, cred_empty = validFile(cred_file_name)
+    if cred_empty is False:
+        with open(cred_file_name, "r") as f:
+            loaded_data = json.load(f)
+            if loaded_data["client"] == cred_id:
+                return loaded_data["secret"]
+    return None
+
+
+def getClientIdFromCredFile(cred_file_name):
+    # Check to see if the local secret is the same id and not just the same
+    # name
+    _, cred_empty = validFile(cred_file_name)
+    if cred_empty is False:
+        with open(cred_file_name, "r") as f:
+            loaded_data = json.load(f)
+            return loaded_data["client"]
+    return None
+
+
+def getEndpointIdFromFile(deployment_key_file_path):
+    # Check to see if the local secret is the same id and not just the same
+    # name
+    _, empty = validFile(deployment_key_file_path)
+    if empty is False:
+        with open(deployment_key_file_path, "r") as f:
+            loaded_data = json.load(f)
+            return loaded_data["client_id"]
+    return None
+
+
+def createNewCredential(auth_client, client_id, cred_name, cred_file):
+
+    get_client_cred_result = auth_client.get_client_credentials(client_id)
+    for cred in get_client_cred_result["credentials"]:
+        # Should have stored secret locally
+        auth_client.delete_client_credential(client_id, cred["id"])
+
+    cred_result = auth_client.create_client_credential(client_id, cred_name)
+    # Have to change this to a dict
+    obj = {
+        "client": cred_result["credential"]["client"],
+        "id": cred_result["credential"]["id"],
+        "name": cred_result["credential"]["name"],
+        "secret": cred_result["credential"]["secret"],
+    }
+    with open(cred_file, "w") as f:
+        json.dump(obj, f)
+
+    return cred_result["credential"]["secret"]
+
+
+def getClientSecret(auth_client, client_id, cred_name, cred_id, cred_file):
+
+    client_secret = getCredentialFromFile(cred_file, cred_id)
+
+    create_new_credential = True
+    remove_cached_credential = True
+    remove_old_credential = False
+    if client_secret:
+        create_new_credential = False
+        remove_cached_credential = False
+        remove_old_credential = True
+
+    if remove_old_credential:
+        auth_client.delete_client_credential(client_id, cred_id)
+
+    if remove_cached_credential:
+        if os.path.exists(cred_file):
+            os.remove(cred_file)
+
+    if create_new_credential:
+        # Remove credentials from cloud
+        client_secret = createNewCredential(
+            auth_client, client_id, cred_name, cred_file
+        )
+
+    return client_secret
+
+
+def createClient(auth_client, client_name, project_id, cred_name, cred_file):
+    client_id = createNewClient(auth_client, client_name, project_id)
+
+    cred_id = getCredentialID(auth_client, client_id, cred_name)
+
+    client_secret = getClientSecret(
+        auth_client, client_id, cred_name, cred_id, cred_file
+    )
+    return client_id, client_secret
+
+
+def getGCSClientIDFromDeploymentFile(deployment_key_file):
+    deployment_key_exists, deployment_key_empty = validFile(deployment_key_file)
+
+    # Remove the file if it is empty
+    if deployment_key_empty:
+        if deployment_key_exists:
+            print("Removing deployment key it is empty")
+            os.remove(deployment_key_file)
+    else:
+        # If it is not empty get the client id
+        with open(deployment_key_file, "r") as f:
+            loaded_data = json.load(f)
+            return loaded_data["client_id"]
+    return None
+
+
+def command_exists(command):
+    try:
+        # Use 'which' command to check if the specified command exists
+        subprocess.check_call(["which", command])
+        return True
+    except subprocess.CalledProcessError:
+        # 'which' command returns non-zero exit status if the command is not found
+        return False
+
+
+def isGCSDeploymentKeyValid(auth_client, project_id, endpoint_name, gcs_id):
+
+    clients_in_project = getClientsInProject(auth_client, project_id)
+    # Check if the deployment key is valid for the project
+    for client in clients_in_project:
+        if (
+            client["client_type"] == "globus_connect_server"
+            and client["name"] == endpoint_name
+        ):
+            if gcs_id:
+                # If gcs_id exists see if it is found remotely
+                if client["id"] == gcs_id:
+                    print("Deployment key endpoint is still valid found in cloud")
+                    return True
+            else:
+                # Found a globus_connect_server but did not find local deployment
+                # key
+                print(
+                    "Found globus_connect_server already registered but did"
+                    " not find deployment key locally."
+                )
+    return False
+
+
+def deleteAllNonGCSClients(auth_client, project_id):
+    clients = getClientsInProject(auth_client, project_id)
+    for client in clients:
+        if (
+            client["project"] == project_id
+            and client["client_type"] != "globus_connect_server"
+        ):
+            auth_client.delete_client(client["id"])
+
+
+def createGCSEndpoint(
+    auth_client,
+    client_id,
+    client_secret,
+    project_id,
+    deployment_key_file,
+    endpoint_name,
+    control_port,
+    userinfo,
+):
+
+    email = userinfo["email"]
+    username = userinfo["preferred_username"]
+    organization = userinfo["identity_provider_display_name"]
+
+    gcs_id_from_deployment_key = getGCSClientIDFromDeploymentFile(deployment_key_file)
+
+    valid_key = isGCSDeploymentKeyValid(
+        auth_client, project_id, endpoint_name, gcs_id_from_deployment_key
+    )
+
+    if valid_key is False and gcs_id_from_deployment_key:
+        print(
+            "Looks like deployment key exists but does not contain credentials "
+            f"in the cloud for the project: {project_id}, please either "
+            "add the correct deployment key or remove the gcs instance"
+            "registered in the project"
+        )
+        sys.exit(1)
+
+    # Create gcs_instance
+    if valid_key is False:
+
+        if command_exists("globus-connect-server") is False:
+            print(
+                "Cannot create deployment key, we require globus-connect-server to be installed"
+            )
+            sys.exit(1)
+
+        else:
+            bash_command = f'GCS_CLI_CLIENT_ID="{client_id}" '
+            bash_command += f' GCS_CLI_CLIENT_SECRET="{client_secret}" '
+            bash_command += f' globus-connect-server endpoint setup "{endpoint_name}" '
+            bash_command += f' --organization "{organization}"  '
+            bash_command += f' --project-id "{project_id}"  '
+            bash_command += " --agree-to-letsencrypt-tos "
+            bash_command += f' --project-admin "{username}" '
+            bash_command += f' --owner "{client_id}@clients.auth.globus.org" '
+            bash_command += f' --contact-email "{email}" '
+            bash_command += f' --deployment-key "{deployment_key_file}" '
+            print("Bash command to run")
+            print(bash_command)
+
+            process = subprocess.Popen(
+                bash_command,
+                shell=True,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+                universal_newlines=True,
+            )
+            # Print the output
+            for line in process.stdout:
+                print(line, end="")
+
+            deployment_key_exists, deployment_key_empty = validFile(deployment_key_file)
+            if deployment_key_exists is False:
+                print(
+                    f"Something is wrong deployment key does not exist {deployment_key_file} "
+                )
+                sys.exit(1)
+            if deployment_key_empty:
+                print(
+                    f"Something is wrong deployment key is empty {deployment_key_file} "
+                )
+                sys.exit(1)
diff --git a/scripts/install_authz_dependencies.sh b/scripts/install_authz_dependencies.sh
index be6b44961..68142b5a4 100755
--- a/scripts/install_authz_dependencies.sh
+++ b/scripts/install_authz_dependencies.sh
@@ -10,8 +10,11 @@ PROJECT_ROOT=$(realpath ${SOURCE}/..)
 source "${PROJECT_ROOT}/scripts/utils.sh"
 source "${PROJECT_ROOT}/scripts/dependency_install_functions.sh"
 
-packages=("libtool" "build-essential" "g++" "gcc" "libboost-all-dev" "autoconf" "automake" "make" "git" "python3-pkg-resources" "python3-pip" "pkg-config" "libglobus-common-dev" "wget" "libssl-dev")
-externals=("cmake" "protobuf" "libsodium" "libzmq")
+packages=("libtool" "build-essential" "g++" "gcc" "autoconf"
+  "automake" "make" "git" "python3-pkg-resources" "python3-pip" "pkg-config"
+  "libglobus-common-dev" "wget" "jq" "sudo" "libboost-all-dev" "python3-venv")
+pip_packages=("setuptools" "distro" "jwt" "globus_sdk")
+externals=("cmake" "protobuf" "libsodium" "libzmq" )
 
 local_UNIFY=false
 
@@ -25,6 +28,7 @@ if [ $# -eq 1 ]; then
       # If 'unify' is provided, print the packages
       # The extra space is necessary to not conflict with the other install scripts
       echo -n "${packages[@]} " >> "$apt_file_path"
+      echo -n "${pip_packages[@]} " >> "$pip_file_path"
       echo -n "${externals[@]} " >> "$ext_file_path"
       local_UNIFY=true
       ;;
@@ -34,19 +38,18 @@ if [ $# -eq 1 ]; then
   esac
 fi
 
-sudo_command
 
-"$SUDO_CMD" apt-get update
-"$SUDO_CMD" dpkg --configure -a
-"$SUDO_CMD" apt-get install -y "${packages[@]}"
-
-cd ~
-install_cmake
+if [[ $local_UNIFY = false ]]; then
+  sudo_command
 
-python3 -m pip install --upgrade pip
-python3 -m pip install setuptools
+  "$SUDO_CMD" apt-get update
+  "$SUDO_CMD" dpkg --configure -a
+  "$SUDO_CMD" apt-get install -y "${packages[@]}"
+  init_python
+  source "${DATAFED_PYTHON_ENV}/bin/activate"
+  python3 -m pip install --upgrade pip
+  python3 -m pip install "${pip_packages[@]}"
 
-if [[ $local_UNIFY = false ]]; then
   for ext in "${externals[@]}"; do
     install_dep_by_name "$ext"
   done
diff --git a/scripts/install_client_dependencies.sh b/scripts/install_client_dependencies.sh
index cea8afb38..2c475fa4b 100755
--- a/scripts/install_client_dependencies.sh
+++ b/scripts/install_client_dependencies.sh
@@ -10,11 +10,16 @@ PROJECT_ROOT=$(realpath "${SOURCE}/..")
 source "${PROJECT_ROOT}/scripts/utils.sh"
 source "${PROJECT_ROOT}/scripts/dependency_install_functions.sh"
 
+packages=("python3-pkg-resources" "python3-pip" "pkg-config" "python3-venv")
+
 sudo_command
 # This script will install all of the dependencies needed by DataFed 1.0
 "$SUDO_CMD" apt-get update
 "$SUDO_CMD" dpkg --configure -a
+"$SUDO_CMD" apt-get install -y "${packages[@]}"
 
+init_python
+source "${DATAFED_PYTHON_ENV}/bin/activate"
 python3 -m pip install -r "${PROJECT_ROOT}/python/datafed_pkg/requirements.txt"
 
 install_protobuf
diff --git a/scripts/install_core_dependencies.sh b/scripts/install_core_dependencies.sh
index 9953a66a4..c79ee55d2 100755
--- a/scripts/install_core_dependencies.sh
+++ b/scripts/install_core_dependencies.sh
@@ -10,8 +10,11 @@ PROJECT_ROOT=$(realpath ${SOURCE}/..)
 source "${PROJECT_ROOT}/scripts/utils.sh"
 source "${PROJECT_ROOT}/scripts/dependency_install_functions.sh"
 
-packages=("libtool" "build-essential" "g++" "gcc" "make" "libboost-all-dev" "pkg-config" "autoconf" "automake" "unzip" "libcurl4-openssl-dev" "wget" "rapidjson-dev" "libkrb5-dev" "git" "python3-pkg-resources" "python3-pip" "libssl-dev")
-externals=("cmake" "nlohmann_json" "json_schema_validator" "protobuf" "libsodium" "libzmq")
+packages=("libtool" "build-essential" "g++" "gcc" "make" "libboost-all-dev" "pkg-config" "autoconf" "automake" "unzip" "wget" "rapidjson-dev" "libkrb5-dev" "git" "python3-pkg-resources" "python3-pip" "python3-venv")
+pip_packages=("setuptools")
+# NOTE the order matters here
+externals=("cmake" "nlohmann_json" "json_schema_validator" "protobuf"
+  "libsodium" "libzmq" "libopenssl" "zlib" "libcurl")
 
 local_UNIFY=false
 
@@ -26,6 +29,7 @@ if [ $# -eq 1 ]; then
       # The extra space is necessary to not conflict with the other install scripts
       echo -n "${packages[@]} " >> "$apt_file_path"
       echo -n "${externals[@]} " >> "$ext_file_path"
+      echo -n "${pip_packages[@]} " >> "$pip_file_path"
       local_UNIFY=true
       ;;
     *)
@@ -34,14 +38,16 @@ if [ $# -eq 1 ]; then
   esac
 fi
 
-sudo_command
 
 if [[ $local_UNIFY = false ]]; then
+  sudo_command
   "$SUDO_CMD" apt-get update
   "$SUDO_CMD" dpkg --configure -a
   "$SUDO_CMD" apt-get install -y "${packages[@]}"
+  init_python
+  source "${DATAFED_PYTHON_ENV}/bin/activate"
   python3 -m pip install --upgrade pip
-  python3 -m pip install setuptools
+  python3 -m pip install "${pip_packages[@]}"
 
   for ext in "${externals[@]}"; do
     install_dep_by_name "$ext"
diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh
index b7a0bff14..c6b1621b3 100755
--- a/scripts/install_dependencies.sh
+++ b/scripts/install_dependencies.sh
@@ -11,10 +11,71 @@ source "${PROJECT_ROOT}/scripts/utils.sh"
 source "${PROJECT_ROOT}/scripts/dependency_install_functions.sh"
 source "${SOURCE}/dependency_versions.sh"
 
+Help()
+{
+  echo "$(basename $0) Will install all datafed dependencies"
+  echo
+  echo "Syntax: $(basename $0) [-h|a|w|c|r]"
+  echo "options:"
+  echo "-h, --help                         Print this help message"
+  echo "-a, --disable-arango-deps-install  Don't install arango"
+  echo "-w, --disable-web-deps-install     Don't install web deps"
+  echo "-c, --disable-core-deps-install    Don't install core deps"
+  echo "-r, --disable-repo-deps-install    Don't install repo deps"
+  echo "-z, --disable-authz-deps-install   Don't install authz deps"
+}
+
+local_INSTALL_ARANGO="TRUE"
+local_INSTALL_WEB="TRUE"
+local_INSTALL_CORE="TRUE"
+local_INSTALL_REPO="TRUE"
+local_INSTALL_AUTHZ="TRUE"
+
+VALID_ARGS=$(getopt -o hawcrz --long 'help',disable-arango-deps-install,disable-web-deps-install,disable-core-deps-install,disable-repo-debs-install,disable-authz-deps-install -- "$@")
+if [[ $? -ne 0 ]]; then
+      exit 1;
+fi
+eval set -- "$VALID_ARGS"
+while [ : ]; do
+  case "$1" in
+    -h | --help)
+        Help
+        exit 0
+        ;;
+    -a | --disable-arango-deps-install)
+        local_INSTALL_ARANGO="FALSE"
+        shift
+        ;;
+    -w | --disable-web-deps-install)
+        local_INSTALL_WEB="FALSE"
+        shift
+        ;;
+    -c | --disable-core-deps-install)
+        local_INSTALL_CORE="FALSE"
+        shift
+        ;;
+    -r | --disable-repo-deps-install)
+        local_INSTALL_REPO="FALSE"
+        shift
+        ;;
+    -z | --disable-authz-deps-install)
+        local_INSTALL_AUTHZ="FALSE"
+        shift
+        ;;
+    --) shift; 
+        break 
+        ;;
+    \?) # incorrect option
+        echo "Error: Invalid option"
+        exit;;
+  esac
+done
+
 sudo_command
 
 touch "$apt_file_path"
 touch "$ext_file_path"
+touch "$pip_file_path"
 
 # Defines SUDO_CMD which is empty if root
 # sudo path if exists
@@ -23,37 +84,57 @@ touch "$ext_file_path"
 "$SUDO_CMD" apt-get update
 "$SUDO_CMD" apt install -y wget git curl
 
-install_cmake
 # This script will install all of the dependencies needed by DataFed 1.0
 "$SUDO_CMD" dpkg --configure -a
 
-"$SUDO_CMD" "$SOURCE/install_core_dependencies.sh" unify
-"$SUDO_CMD" "$SOURCE/install_repo_dependencies.sh" unify
-"$SUDO_CMD" "$SOURCE/install_ws_dependencies.sh" unify
-"$SUDO_CMD" "$SOURCE/install_authz_dependencies.sh" unify
-
-all_packages=$(cat $apt_file_path)
+if [ "$local_INSTALL_CORE" == "TRUE" ]
+then
+  "$SUDO_CMD" "$SOURCE/install_core_dependencies.sh" unify
+fi
+if [ "$local_INSTALL_REPO" == "TRUE" ]
+then
+  "$SUDO_CMD" "$SOURCE/install_repo_dependencies.sh" unify
+fi
+if [ "$local_INSTALL_WEB" == "TRUE" ]
+then
+  "$SUDO_CMD" "$SOURCE/install_ws_dependencies.sh" unify
+fi
+if [ "$local_INSTALL_AUTHZ" == "TRUE" ]
+then
+  "$SUDO_CMD" "$SOURCE/install_authz_dependencies.sh" unify
+fi
+"$SUDO_CMD" "$SOURCE/install_docs_dependencies.sh" unify
+
+all_packages=$(cat "$apt_file_path")
 IFS=' ' read -r -a all_packages_array <<< "$all_packages"
 deduplicated_packages_array=($(printf "%s\n" "${all_packages_array[@]}" | sort -u))
-
-all_externals=$(cat $ext_file_path)
-IFS=' ' read -r -a all_externals_array <<< "$all_externals"
-
+echo "DEPENDENCIES (${deduplicated_packages_array[@]})"
 "$SUDO_CMD" apt-get install -y "${deduplicated_packages_array[@]}"
 
-echo "DEPENDENCIES (${deduplicated_externals_array[@]})"
-
-cd ~
+all_pip_packages=$(cat "$pip_file_path")
+IFS=' ' read -ra all_pip_packages_array <<< "$all_pip_packages"
+if [ ${#all_pip_packages_array[@]} -gt 0 ]; then
+  echo "DEPENDENCIES (${all_pip_packages_array[@]})"
+  init_python
+  source "${DATAFED_PYTHON_ENV}/bin/activate"
+  python3 -m pip install "${all_pip_packages_array[@]}"
+fi
 
-for ext in "${all_externals_array[@]}"; do
+all_externals=$(cat "$ext_file_path")
+IFS=' ' read -r -a all_externals_array <<< "$all_externals"
+# Deduplication must preserve order
+deduplicated_externals_array=($(echo "${all_externals_array[@]}" | awk '{ for (i=1;i<=NF;i++) if (!seen[$i]++) printf("%s ", $i) }'))
+echo "DEPENDENCIES (${deduplicated_externals_array[@]})"
+for ext in "${deduplicated_externals_array[@]}"; do
   echo "===== INSTALLING $ext ======"
   install_dep_by_name "$ext"
 done
 
-rm $apt_file_path
-rm $ext_file_path
-
-python3 -m pip install --upgrade pip
-python3 -m pip install setuptools sphinx sphinx-rtd-theme sphinx-autoapi
+rm "$apt_file_path"
+rm "$ext_file_path"
+rm "$pip_file_path"
 
-install_arangodb
+if [ "$local_INSTALL_ARANGO" == "TRUE" ]
+then
+  install_arangodb
+fi
diff --git a/scripts/install_docs_dependencies.sh b/scripts/install_docs_dependencies.sh
index 927104890..06ee7c3de 100755
--- a/scripts/install_docs_dependencies.sh
+++ b/scripts/install_docs_dependencies.sh
@@ -10,16 +10,45 @@ PROJECT_ROOT=$(realpath ${SOURCE}/..)
 source "${PROJECT_ROOT}/scripts/utils.sh"
 source "${PROJECT_ROOT}/scripts/dependency_install_functions.sh"
 
-sudo_command
-# This script will install all of the dependencies needed by DataFed 1.0
-"$SUDO_CMD" apt-get update
-"$SUDO_CMD" dpkg --configure -a
-"$SUDO_CMD" apt-get install -y 
-
-install_cmake
-cd ~
-
-install_protobuf
-cd ~
-python3 -m pip install --upgrade pip
-python3 -m pip install setuptools sphinx sphinx-rtd-theme sphinx-autoapi
+packages=("g++" "gcc" "make" "python3-pkg-resources" "python3-pip" "pkg-config" "python3-venv")
+externals=("cmake" "protobuf")
+pip_packages=("setuptools" "sphinx" "sphinx-rtd-theme" "sphinx-autoapi")
+
+local_UNIFY=false
+
+if [ $# -eq 1 ]; then
+  case "$1" in
+    -h|--help)
+      # If -h or --help is provided, print help
+      echo "Usage: $0 [-h|--help] [unify]"
+      ;;
+    unify)
+      # If 'unify' is provided, print the packages
+      # The extra space is necessary to not conflict with the other install scripts
+      echo -n "${packages[@]} " >> "$apt_file_path"
+      echo -n "${pip_packages[@]} " >> "$pip_file_path"
+      echo -n "${externals[@]} " >> "$ext_file_path"
+      local_UNIFY=true
+      ;;
+    *)
+      echo "Invalid Argument"
+      ;;
+  esac
+fi
+
+
+if [[ $local_UNIFY = false ]]; then
+  sudo_command
+  "$SUDO_CMD" apt-get update
+  "$SUDO_CMD" dpkg --configure -a
+  "$SUDO_CMD" apt-get install -y "${packages[@]}"
+  init_python
+  source "${DATAFED_PYTHON_ENV}/bin/activate"
+  python3 -m pip install --upgrade pip
+  python3 -m pip install "${pip_packages[@]}"
+
+  for ext in "${externals[@]}"; do
+    install_dep_by_name "$ext"
+  done
+fi
+
diff --git a/scripts/install_python_client_dependencies.sh b/scripts/install_python_client_dependencies.sh
index e16fa04a4..a8467a3f0 100755
--- a/scripts/install_python_client_dependencies.sh
+++ b/scripts/install_python_client_dependencies.sh
@@ -17,7 +17,7 @@ sudo_command
 "$SUDO_CMD" dpkg --configure -a
 "$SUDO_CMD" apt-get install -y libtool build-essential g++ gcc make libboost-all-dev \
 pkg-config autoconf automake unzip libcurl4-openssl-dev wget \
-rapidjson-dev libkrb5-dev git python3-pkg-resources python3-pip libssl-dev
+rapidjson-dev libkrb5-dev git python3-pkg-resources python3-pip python3-venv libssl-dev
 
 cd ~
 install_cmake
@@ -25,6 +25,8 @@ cd ~
 
 # Install cmake 3.17
 
+init_python
+source "${DATAFED_PYTHON_ENV}/bin/activate"
 python3 -m pip install --upgrade pip
 python3 -m pip install setuptools
 
diff --git a/scripts/install_repo_dependencies.sh b/scripts/install_repo_dependencies.sh
index 4a28c17c0..723a3ad81 100755
--- a/scripts/install_repo_dependencies.sh
+++ b/scripts/install_repo_dependencies.sh
@@ -10,7 +10,8 @@ PROJECT_ROOT=$(realpath ${SOURCE}/..)
 source "${PROJECT_ROOT}/scripts/utils.sh"
 source "${PROJECT_ROOT}/scripts/dependency_install_functions.sh"
 
-packages=("libtool" "wget" "build-essential" "g++" "gcc" "libboost-all-dev" "pkg-config" "autoconf" "automake" "make" "unzip" "git" "python3-pkg-resources" "libssl-dev" "python3-pip")
+packages=("libtool" "wget" "build-essential" "g++" "gcc" "libboost-all-dev" "pkg-config" "autoconf" "automake" "make" "unzip" "git" "python3-pkg-resources" "python3-pip" "python3-venv")
+pip_packages=("setuptools")
 externals=("cmake" "protobuf" "libsodium" "libzmq")
 
 local_UNIFY=false
@@ -26,6 +27,7 @@ if [ $# -eq 1 ]; then
       # The extra space is necessary to not conflict with the other install scripts
       echo -n "${packages[@]} " >> "$apt_file_path"
       echo -n "${externals[@]} " >> "$ext_file_path"
+      echo -n "${pip_packages[@]} " >> "$pip_file_path"
       local_UNIFY=true
       ;;
     *)
@@ -42,8 +44,10 @@ if [[ $local_UNIFY = false ]]; then
   "$SUDO_CMD" dpkg --configure -a
   "$SUDO_CMD" apt-get install -y "${packages[@]}"
 
+  init_python
+  source "${DATAFED_PYTHON_ENV}/bin/activate"
   python3 -m pip install --upgrade pip
-  python3 -m pip install setuptools
+  python3 -m pip install "${pip_packages[@]}"
 
   for ext in "${externals[@]}"; do
     install_dep_by_name "$ext"
diff --git a/scripts/install_ws_dependencies.sh b/scripts/install_ws_dependencies.sh
index c2e7e6bca..5f124a540 100755
--- a/scripts/install_ws_dependencies.sh
+++ b/scripts/install_ws_dependencies.sh
@@ -12,7 +12,7 @@ source "${SOURCE}/dependency_versions.sh"
 source "${PROJECT_ROOT}/scripts/dependency_install_functions.sh"
 
 packages=("curl" "python3" "g++" "make" "wget")
-externals=("cmake")
+externals=("cmake" "nvm" "node" "ws_node_packages")
 
 Help()
 {
@@ -74,14 +74,3 @@ if [[ $local_UNIFY = false ]]; then
     install_dep_by_name "$ext"
   done
 fi
-
-# The foxx services need node version 12 or greater so we aren't going to use the package manager
-# but instead will install ourselves
-
-# 1. Install nvm which will allow us to update node
-install_nvm
-install_node
-
-export NVM_DIR="${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm"
-export NODE_VERSION="$DATAFED_NODE_VERSION"
-"$NVM_DIR/nvm-exec" npm --prefix "${PROJECT_ROOT}/web" install "${PROJECT_ROOT}/web"
diff --git a/web/docker/Dockerfile b/web/docker/Dockerfile
index d31cc8f17..7e699672f 100644
--- a/web/docker/Dockerfile
+++ b/web/docker/Dockerfile
@@ -61,6 +61,7 @@ ENV DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH"
 ENV DATAFED_DEPENDENCIES_INSTALL_PATH="$DATAFED_DEPENDENCIES_INSTALL_PATH"
 ENV          DATAFED_DIR="$DATAFED_DIR"
 ENV            BUILD_DIR="$BUILD_DIR"
+ENV          DATAFED_DEFAULT_LOG_PATH="$DATAFED_INSTALL_PATH/logs"
 
 RUN apt install -y python3 make g++
 
@@ -76,21 +77,13 @@ COPY --chown=datafed:root ./scripts/install_ws.sh                ${BUILD_DIR}/sc
 COPY --chown=datafed:root ./cmake/Version.cmake                  ${BUILD_DIR}/cmake/Version.cmake
 COPY --chown=datafed:root ./scripts/utils.sh                     ${BUILD_DIR}/scripts/utils.sh
 COPY --chown=datafed:root ./scripts/export_dependency_version.sh ${BUILD_DIR}/scripts/export_dependency_version.sh
+COPY --from=ws-build --chown=datafed:root ${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm ${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm
 COPY --from=ws-build --chown=datafed:root ${BUILD_DIR}/web ${BUILD_DIR}/web
-COPY --from=ws-build --chown=datafed:root ${DATAFED_INSTALL_PATH} ${DATAFED_INSTALL_PATH}
-COPY --from=ws-build --chown=datafed:root ${DATAFED_DEPENDENCIES_INSTALL_PATH} ${DATAFED_DEPENDENCIES_INSTALL_PATH}
+COPY --from=ws-build --chown=datafed:root ${DATAFED_INSTALL_PATH}/web ${DATAFED_INSTALL_PATH}/web
 COPY --from=ws-build --chown=datafed:root /usr/bin/curl /usr/bin/curl
 
-RUN chown -R datafed:root ${DATAFED_INSTALL_PATH}/web
-RUN chown -R datafed:root ${BUILD_DIR}
-
-WORKDIR ${DATAFED_DIR}
-
-RUN echo "${DATAFED_DIR}"
-RUN . ${BUILD_DIR}/scripts/export_dependency_version.sh && \
-  . ${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm/nvm.sh && \
-	npm --allow-root --unsafe-perm --prefix ${DATAFED_INSTALL_PATH}/web install
+WORKDIR ${BUILD_DIR}
 
 USER root
 
-ENTRYPOINT ["/datafed/source/web/docker/entrypoint.sh", "/opt/datafed/web/datafed-ws.js", "/datafed/source/config/datafed-ws.cfg" ]
+ENTRYPOINT ["/datafed/source/web/docker/entrypoint.sh", "/datafed/source/web/datafed-ws.js", "/datafed/source/config/datafed-ws.cfg" ]
diff --git a/web/docker/Dockerfile.web-base.ubuntu b/web/docker/Dockerfile.web-base.ubuntu
index 455778c53..c50ad329c 100644
--- a/web/docker/Dockerfile.web-base.ubuntu
+++ b/web/docker/Dockerfile.web-base.ubuntu
@@ -5,7 +5,11 @@ ARG BUILD_DIR="/datafed/source"
 ARG DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
 
 ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
-ENV PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:${PATH}"
+
+# NOTE - it does not look like using $PATH variable inside this definition
+# resolves correctly as such they system paths are included explicitly when
+# changes are made to ENV PATH
+ENV PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
 RUN mkdir -p ${BUILD_DIR}
 RUN mkdir -p ${BUILD_DIR}/common/proto
 
@@ -16,14 +20,19 @@ COPY ./scripts/dependency_install_functions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/dependency_versions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/install_ws_dependencies.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/utils.sh ${BUILD_DIR}/scripts/
+COPY ./cmake/Web.cmake ${BUILD_DIR}/cmake/Web.cmake
+COPY ./cmake/Version.cmake ${BUILD_DIR}/cmake/Version.cmake
+COPY ./web/package.json.in ${BUILD_DIR}/web/package.json.in
+COPY ./web/version.js.in ${BUILD_DIR}/web/version.js.in
 COPY ./.gitmodules ${BUILD_DIR}/.gitmodules
 COPY ./.git ${BUILD_DIR}/.git
 
-ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DIR}/dependenies"
+ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DIR}/dependencies"
 
 # Docker dependency
 RUN echo "#!/bin/bash\n\$@" > /usr/bin/sudo && chmod +x /usr/bin/sudo
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh
 RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ${BUILD_DIR}/scripts/install_ws_dependencies.sh -n "${DATAFED_DIR}"
 
+RUN env
 RUN echo "web-base complete"
diff --git a/web/docker/Dockerfile.web.ubuntu b/web/docker/Dockerfile.web.ubuntu
index 9e9929678..f86bc172e 100644
--- a/web/docker/Dockerfile.web.ubuntu
+++ b/web/docker/Dockerfile.web.ubuntu
@@ -10,11 +10,12 @@ ARG DATAFED_INSTALL_PATH="/datafed/install"
 ENV DATAFED_DIR="$DATAFED_DIR"
 ENV BUILD_DIR="$BUILD_DIR"
 ENV DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH"
-ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DIR}/dependenies"
+ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DIR}/dependencies"
 
 RUN mkdir -p ${BUILD_DIR}/logs
 RUN mkdir -p ${BUILD_DIR}/common/proto
 RUN mkdir -p ${DATAFED_INSTALL_PATH}/web
+RUN mkdir -p ${DATAFED_DEPENDENCIES_INSTALL_PATH}
 
 # vim and nc are for debugging
 RUN apt-get install vim netcat -y
@@ -47,11 +48,11 @@ RUN chown -R datafed:datafed ${DATAFED_DIR}
 USER datafed
 
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh &&\
- cmake -S. -B build -DBUILD_REPO_SERVER=False -DBUILD_AUTHZ=False \
+ ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake -S. -B build -DBUILD_REPO_SERVER=False -DBUILD_AUTHZ=False \
                     -DBUILD_CORE_SERVER=False -DBUILD_WEB_SERVER=True \
                     -DBUILD_DOCS=False -DBUILD_PYTHON_CLIENT=False \
                     -DBUILD_FOXX=False -DBUILD_COMMON=False &&\
- cmake --build build
+ ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build
 
 #ENV NVM_DIR="$DATAFED_DIR/.nvm"
 #ENV NVM_INC=$DATAFED_DIR/.nvm/versions/node/v13.14.0/include/node
@@ -61,7 +62,8 @@ RUN ${BUILD_DIR}/scripts/generate_datafed.sh &&\
 # Note that building the web server this way assumes that a temporary core key 
 # has been passed in, this will need to be overwritten when the web server
 # is run.
-RUN cmake --build build --target install
+RUN cat "${BUILD_DIR}/config/datafed.sh"
+RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build --target install
 
 WORKDIR ${DATAFED_INSTALL_PATH}/web
 
diff --git a/web/docker/entrypoint.sh b/web/docker/entrypoint.sh
index 92482ca4a..4c538bd2f 100755
--- a/web/docker/entrypoint.sh
+++ b/web/docker/entrypoint.sh
@@ -7,6 +7,8 @@ if [ -n "$UID" ]; then
     usermod -u "$UID" datafed
 fi
 
+chown -R datafed:root "${DATAFED_INSTALL_PATH}/web"
+chown -R datafed:root "${BUILD_DIR}"
 
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
@@ -14,7 +16,6 @@ PROJECT_ROOT=$(realpath "${SOURCE}/../..")
 
 "${PROJECT_ROOT}/scripts/generate_datafed.sh"
 "${PROJECT_ROOT}/scripts/generate_ws_config.sh"
-"${PROJECT_ROOT}/scripts/install_ws.sh"
 . "${PROJECT_ROOT}/scripts/dependency_versions.sh"
 
 export NVM_DIR="${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm"


Test patch:
diff --git a/common/tests/security/tcp_secure/CMakeLists.txt b/common/tests/security/tcp_secure/CMakeLists.txt
index 634879b57..bebb9fa40 100644
--- a/common/tests/security/tcp_secure/CMakeLists.txt
+++ b/common/tests/security/tcp_secure/CMakeLists.txt
@@ -2,13 +2,22 @@
 include_directories(${CMAKE_SOURCE_DIR}/common/source)
 add_executable(test_tcp_secure_client test_tcp_secure_client.cpp)
 
-target_link_libraries(test_tcp_secure_client ${Boost_LIBRARIES} common libzmq-static protobuf::libprotobuf Threads::Threads)
-
-target_compile_definitions(test_tcp_secure_client PRIVATE BOOST_TEST_DYN_LINK)
+# Only needed if boost is shared and not static
+if(BUILD_SHARED_LIBS)
+  target_compile_definitions(test_tcp_secure_client PRIVATE BOOST_TEST_DYN_LINK)
+  target_link_libraries(test_tcp_secure_client ${DATAFED_BOOST_LIBRARIES} common libzmq protobuf::libprotobuf Threads::Threads)
+else()
+  target_link_libraries(test_tcp_secure_client ${DATAFED_BOOST_LIBRARIES} common libzmq-static protobuf::libprotobuf Threads::Threads)
+endif()
 
 add_executable(test_tcp_secure_server test_tcp_secure_server.cpp)
-target_link_libraries(test_tcp_secure_server ${Boost_LIBRARIES} common libzmq-static protobuf::libprotobuf Threads::Threads)
-target_compile_definitions(test_tcp_secure_server PRIVATE BOOST_TEST_DYN_LINK)
+# Only needed if boost is shared and not static
+if(BUILD_SHARED_LIBS)
+  target_link_libraries(test_tcp_secure_server ${DATAFED_BOOST_LIBRARIES} common libzmq protobuf::libprotobuf Threads::Threads)
+  target_compile_definitions(test_tcp_secure_server PRIVATE BOOST_TEST_DYN_LINK)
+else()
+  target_link_libraries(test_tcp_secure_server ${DATAFED_BOOST_LIBRARIES} common libzmq-static protobuf::libprotobuf Threads::Threads)
+endif()
 
 find_program(TCPDUMP_CMD NAMES tcpdump)
 find_program(TIMEOUT_CMD NAMES timeout)
diff --git a/common/tests/security/tcp_secure/test_tcp_secure.sh b/common/tests/security/tcp_secure/test_tcp_secure.sh
index 0c385a090..e0e1b6053 100755
--- a/common/tests/security/tcp_secure/test_tcp_secure.sh
+++ b/common/tests/security/tcp_secure/test_tcp_secure.sh
@@ -34,7 +34,7 @@ echo "TIMEOUT:       ${TIMEOUT_CMD}"
 echo "MAX_TEST_TIME: ${MAX_TEST_TIME_SEC}"
 
 # Grab the first 30 packets sent on the loop back interface (127.0.0.1) and port 7515
-match=$( ${TIMEOUT_CMD} ${MAX_TEST_TIME_SEC} ${TCPDUMP_CMD} -vvv -A port 7515 -i lo | grep token)
+match=$( "${TIMEOUT_CMD}" "${MAX_TEST_TIME_SEC}" "${TCPDUMP_CMD}" -vvv -A port 7515 -i lo | grep token)
 
 echo "Content of grep ${match}"
 # If '.magic_token' is returned from the network sniffer then we know that 
diff --git a/common/tests/unit/CMakeLists.txt b/common/tests/unit/CMakeLists.txt
index 6a03590b0..5e318b6d9 100644
--- a/common/tests/unit/CMakeLists.txt
+++ b/common/tests/unit/CMakeLists.txt
@@ -18,9 +18,15 @@ foreach(PROG
   include_directories(${PROJECT_SOURCE_DIR}/common/source)
   file(GLOB ${PROG}_SOURCES ${PROG}.cpp)
   add_executable(unit_${PROG} ${${PROG}_SOURCES})
-  target_link_libraries(unit_${PROG} PUBLIC ${Boost_LIBRARIES}  
-	  common libzmq-static protobuf::libprotobuf Threads::Threads) 
-  target_compile_definitions(unit_${PROG} PRIVATE BOOST_TEST_DYN_LINK)
+  if(BUILD_SHARED_LIBS)
+    target_link_libraries(unit_${PROG} PRIVATE ${DATAFED_BOOST_LIBRARIES}
+      common libzmq protobuf::libprotobuf Threads::Threads) 
+    target_compile_definitions(unit_${PROG} PRIVATE BOOST_TEST_DYN_LINK)
+  else()
+    target_link_libraries(unit_${PROG} PRIVATE ${DATAFED_BOOST_LIBRARIES}
+      common libzmq-static protobuf::libprotobuf Threads::Threads) 
+  endif()
+  # Only want this if using shared boost libraries
   add_test(unit_${PROG} unit_${PROG})
    
 endforeach(PROG)
diff --git a/common/tests/unit/test_DynaLog.cpp b/common/tests/unit/test_DynaLog.cpp
index d89a960d5..dc7494483 100644
--- a/common/tests/unit/test_DynaLog.cpp
+++ b/common/tests/unit/test_DynaLog.cpp
@@ -16,6 +16,15 @@ using namespace SDMS;
 
 BOOST_AUTO_TEST_SUITE(LogTest)
 
+BOOST_AUTO_TEST_CASE(testing_Enum) {
+  BOOST_CHECK(static_cast<unsigned int>(SDMS::LogLevel::TRACE) == 5);
+  BOOST_CHECK(static_cast<unsigned int>(SDMS::LogLevel::DEBUG) == 4);
+  BOOST_CHECK(static_cast<unsigned int>(SDMS::LogLevel::INFO) == 3);
+  BOOST_CHECK(static_cast<unsigned int>(SDMS::LogLevel::WARNING) == 2);
+  BOOST_CHECK(static_cast<unsigned int>(SDMS::LogLevel::ERROR) == 1);
+  BOOST_CHECK(static_cast<unsigned int>(SDMS::LogLevel::CRITICAL) == 0);
+}
+
 BOOST_AUTO_TEST_CASE(testing_LogOutput) {
 
   std::string file_name = "./log_output_test1.txt";
diff --git a/common/tests/unit/test_ProtoBufMap.cpp b/common/tests/unit/test_ProtoBufMap.cpp
index 79d503a1d..b780d8b2c 100644
--- a/common/tests/unit/test_ProtoBufMap.cpp
+++ b/common/tests/unit/test_ProtoBufMap.cpp
@@ -22,7 +22,9 @@ BOOST_AUTO_TEST_CASE(testing_ProtoBufFactory_ProtocolID) {
 
   uint8_t proto_id =
       proto_map.getProtocolID(MessageProtocol::GOOGLE_ANONONYMOUS);
+  BOOST_CHECK(proto_id == 1);
   proto_id = proto_map.getProtocolID(MessageProtocol::GOOGLE_AUTHORIZED);
+  BOOST_CHECK(proto_id == 2);
 }
 
 BOOST_AUTO_TEST_CASE(testing_ProtoBufFactory) {
diff --git a/common/tests/unit/test_SocketFactory.cpp b/common/tests/unit/test_SocketFactory.cpp
index c8147f71e..f7f333883 100644
--- a/common/tests/unit/test_SocketFactory.cpp
+++ b/common/tests/unit/test_SocketFactory.cpp
@@ -29,7 +29,7 @@ BOOST_AUTO_TEST_SUITE(SocketFactoryTest)
 
 class DummyCredential : public ICredentials {
 public:
-  DummyCredential(const std::string &pub_key) : m_pub_key(pub_key){};
+  explicit DummyCredential(const std::string &pub_key) : m_pub_key(pub_key){};
 
 private:
   std::string m_pub_key = "";
diff --git a/core/database/tests/test_setup.sh b/core/database/tests/test_setup.sh
index 5aecb1801..fbb85a3c6 100755
--- a/core/database/tests/test_setup.sh
+++ b/core/database/tests/test_setup.sh
@@ -6,8 +6,8 @@ set -f -o pipefail
 
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
-PROJECT_ROOT=$(realpath ${SOURCE}/../../../)
-source ${PROJECT_ROOT}/config/datafed.sh
+PROJECT_ROOT=$(realpath "${SOURCE}/../../../")
+source "${PROJECT_ROOT}/config/datafed.sh"
 
 Help()
 {
@@ -123,11 +123,11 @@ if [[ "$output" =~ .*"sdms".* ]]; then
 	echo "SDMS already exists do nothing"
 else
 	echo "Creating SDMS"
-  arangosh  --server.password ${local_DATAFED_DATABASE_PASSWORD} --server.username ${local_DATABASE_USER} --javascript.execute ${PROJECT_ROOT}/core/database/foxx/db_create.js
+  arangosh  --server.password "${local_DATAFED_DATABASE_PASSWORD}" --server.username "${local_DATABASE_USER}" --javascript.execute "${PROJECT_ROOT}/core/database/foxx/db_create.js"
   # Give time for the database to be created
   sleep 2
-  arangosh --server.password ${local_DATAFED_DATABASE_PASSWORD} --server.username ${local_DATABASE_USER} --javascript.execute-string 'db._useDatabase("sdms"); db.config.insert({"_key": "msg_daily", "msg" : "DataFed servers will be off-line for regular maintenance every Sunday night from 11:45 pm until 12:15 am EST Monday morning."}, {overwrite: true});'
-  arangosh  --server.password ${local_DATAFED_DATABASE_PASSWORD} --server.username ${local_DATABASE_USER} --javascript.execute-string "db._useDatabase(\"sdms\"); db.config.insert({ \"_key\": \"system\", \"_id\": \"config/system\", \"secret\": \"${local_DATAFED_ZEROMQ_SYSTEM_SECRET}\"}, {overwrite: true } );"
+  arangosh --server.password "${local_DATAFED_DATABASE_PASSWORD}" --server.username "${local_DATABASE_USER}" --javascript.execute-string 'db._useDatabase("sdms"); db.config.insert({"_key": "msg_daily", "msg" : "DataFed servers will be off-line for regular maintenance every Sunday night from 11:45 pm until 12:15 am EST Monday morning."}, {overwrite: true});'
+  arangosh --server.password "${local_DATAFED_DATABASE_PASSWORD}" --server.username "${local_DATABASE_USER}" --javascript.execute-string "db._useDatabase(\"sdms\"); db.config.insert({ \"_key\": \"system\", \"_id\": \"config/system\", \"secret\": \"${local_DATAFED_ZEROMQ_SYSTEM_SECRET}\"}, {overwrite: true } );"
 fi
 
 # There are apparently 3 different ways to deploy Foxx microservices,
@@ -151,16 +151,16 @@ NODE_VERSION="v14.21.3"
 export NVM_DIR="$HOME/.nvm"
 [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
 
-nvm install $NODE_VERSION
-nvm use $NODE_VERSION
+nvm install "$NODE_VERSION"
+nvm use "$NODE_VERSION"
 
 PATH_TO_PASSWD_FILE=${SOURCE}/database_temp.password
 # Install foxx service node module
 $NVM_DIR/nvm-exec npm install --global foxx-cli
-echo "$local_DATAFED_DATABASE_PASSWORD" > ${PATH_TO_PASSWD_FILE}
+echo "$local_DATAFED_DATABASE_PASSWORD" > "${PATH_TO_PASSWD_FILE}"
 
 # Check if database foxx services have already been installed
-existing_services=$(foxx list -a -u $local_DATABASE_USER -p ${PATH_TO_PASSWD_FILE} --database $local_DATABASE_NAME)
+existing_services=$(foxx list -a -u "$local_DATABASE_USER" -p "${PATH_TO_PASSWD_FILE}" --database "$local_DATABASE_NAME")
 echo "existing services ${existing_services}"
 
 if [[ "$existing_services" =~ .*"DataFed".* ]]
diff --git a/core/database/tests/test_teardown.sh b/core/database/tests/test_teardown.sh
index e307d2cd3..09ce68a17 100755
--- a/core/database/tests/test_teardown.sh
+++ b/core/database/tests/test_teardown.sh
@@ -5,8 +5,8 @@ set -uf -o pipefail
 
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
-PROJECT_ROOT=$(realpath ${SOURCE}/../../../)
-source ${PROJECT_ROOT}/config/datafed.sh
+PROJECT_ROOT=$(realpath "${SOURCE}/../../../")
+source "${PROJECT_ROOT}/config/datafed.sh"
 
-PATH_TO_PASSWD_FILE=${SOURCE}/database_temp.password
-rm ${PATH_TO_PASSWD_FILE}
+PATH_TO_PASSWD_FILE="${SOURCE}/database_temp.password"
+rm "${PATH_TO_PASSWD_FILE}"
diff --git a/core/server/tests/unit/CMakeLists.txt b/core/server/tests/unit/CMakeLists.txt
index 6151c9d4c..3c46fb112 100644
--- a/core/server/tests/unit/CMakeLists.txt
+++ b/core/server/tests/unit/CMakeLists.txt
@@ -6,8 +6,10 @@ foreach(PROG
 
   file(GLOB ${PROG}_SOURCES ${PROG}*.cpp)
   add_executable(unit_${PROG} ${${PROG}_SOURCES})
-  target_link_libraries(unit_${PROG} Boost::unit_test_framework datafed-core-lib)
-  target_compile_definitions(unit_${PROG} PRIVATE BOOST_TEST_DYN_LINK)
+  target_link_libraries(unit_${PROG} PUBLIC datafed-core-lib ${DATAFED_BOOST_LIBRARIES})
+  if(BUILD_SHARED_LIBS)
+    target_compile_definitions(unit_${PROG} PRIVATE BOOST_TEST_DYN_LINK)
+  endif()
   add_test(unit_${PROG} unit_${PROG})
-   
+
 endforeach(PROG)
diff --git a/tests/end-to-end/README.md b/tests/end-to-end/README.md
index 7af98c941..fbcea5878 100644
--- a/tests/end-to-end/README.md
+++ b/tests/end-to-end/README.md
@@ -1,8 +1,12 @@
 # Instructions
 
-The end to end tests require that two users be created. Because we cannot automate the authentication steps we will create entries in the database for two users to be test users, the passwords for these test users should only exist in the env.
+The end to end tests require that two users be created. Because we cannot
+automate the authentication steps we will create entries in the database for two
+users to be test users, the passwords for these test users should only exist in
+the env.
 
-The python API will be exclusively used to run the end to end tests, in this folder
+The python API will be exclusively used to run the end to end tests, in this
+folder
 
 To use the python API you will need to build it