Repository: ORNL/DataFed (language: python)

Issue:
Repo server and GCS download core key from web server



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 1ee074785..97ed71946 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +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
@@ -9,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
@@ -28,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
@@ -36,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 57a3dc820..d57dd3c2d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,6 +6,7 @@ stages:
   - build-deploy-base
   - provision-client
   - build
+  - end-to-end-setup-arango
   - end-to-end-setup
   - end-to-end-test
   - end-to-end-teardown
@@ -13,6 +14,7 @@ stages:
 variables:
   HARBOR_USER: 'robot$$datafed+harbor_datafed_gitlab_ci_registry'
   REGISTRY: 'camden.ornl.gov'
+  DATAFED_DEPENDENCIES_INSTALL_PATH: "/shared/install"
 ################################################################################
 # STAGE: ci-infrastructure-check
 ################################################################################
@@ -94,6 +96,17 @@ clear-repo-cache:
     - docker system prune -f
     - ./scripts/ci_purge_images.sh
 
+clear-python-client-cache:
+  stage: clear-docker-cache
+  needs: ["signal"]
+  tags:
+    - ci_5
+  script:
+    - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
+    - docker system prune -f
+    - ./scripts/ci_purge_images.sh
+
+
 ################################################################################
 # STAGE: build-deploy-base
 ################################################################################
@@ -114,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"]
@@ -129,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"]
@@ -144,18 +159,34 @@ 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"]
+  variables:
+    IMAGE_TAG: "datafed/python-client-base"
+    GIT_STRATEGY: clone
+  stage: build-deploy-base
+  tags:
+    - docker
+    - ci-datafed-client
+  script:
+    - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
+    - docker system prune -f
+    - 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}-${BRANCH_LOWER}:latest"
 
 build-gcs-base:
   needs: ["clear-repo-cache"]
   variables:
     GIT_SUBMODULE_STRATEGY: recursive
     GIT_STRATEGY: clone
-    DISTRO: "ubuntu:focal"
+    DISTRO: "ubuntu-20.04"
     GCS_TAG: "${REGISTRY}/datafed/gcs-ubuntu-focal"
     IMAGE_TAG2: "datafed/gcs-authz-base"
     IMAGE_TAG3: "datafed/gcs-authz"
@@ -163,36 +194,55 @@ build-gcs-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
-    - cd external/globus-connect-server-deploy/docker
-    - ./docker-build-gcs.sh "${DISTRO}"
-    - LATEST_IMAGE=$(docker images | grep ubuntu-focal | head -n1 | awk '{print $3}')
-    - docker tag "$LATEST_IMAGE" "$GCS_TAG" 
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - docker push "$GCS_TAG"
-    - cd ../../../ # back too root of project
-    - docker build -f repository/docker/Dockerfile.gcs-authz-base.ubuntu -t "${REGISTRY}/${IMAGE_TAG2}" .
-    - docker push "${REGISTRY}/${IMAGE_TAG2}"
-    - docker build -f repository/docker/Dockerfile.gcs-authz.ubuntu -t "${REGISTRY}/${IMAGE_TAG3}" .
-    - docker push "${REGISTRY}/${IMAGE_TAG3}"
+    - >
+      if ! docker pull "${GCS_TAG}:${DATAFED_GCS_SUBMODULE_VERSION}-${BRANCH_LOWER}:latest"; then
+        cd external/globus-connect-server-deploy/docker
+        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."
+      fi 
 
 ################################################################################
 # STAGE: provision client
 ################################################################################
 provision-client:
+
   needs: ["signal"]
   variables:
-    IMAGE_TAG: "datafed/repo-base"
     GIT_STRATEGY: clone
-    DATAFED_DEPENDENCIES_INSTALL_PATH: "${CI_PROJECT_DIR}/install"
   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
-
+  rules:
+    - changes:
+        - scripts/generate_datafed.sh
+        - scripts/install_client_dependencies.sh
+        - scripts/dependency_install_functions.sh
+        - scripts/dependency_versions.sh
+ 
 ################################################################################
 # STAGE: build
 ################################################################################
@@ -201,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"
@@ -249,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
@@ -261,13 +316,6 @@ build-foxx:
     DATAFED_ZEROMQ_SYSTEM_SECRET: "${CI_DATAFED_ZEROMQ_SYSTEM_SECRET}"
   tags:
     - ci-datafed-arango
-  rules:
-    - changes:
-        - CMakeLists.txt
-        - scripts/*
-        - core/database/*
-        - .gitlab-ci.yml
-      when: on_success
   script:
     - arangod --version
     - sudo systemctl status arangodb3
@@ -290,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
@@ -299,48 +347,114 @@ 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:
-  needs: ["build-gcs-base"]
   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"
+    GIT_STRATEGY: clone
+  stage: build
+  tags:
+    - docker
+    - ci-datafed-client
+  rules:
+    - changes:
+        - 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 --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
 ################################################################################
 
+end-to-end-arango-setup:
+  variables:
+    GIT_STRATEGY: clone
+  stage: end-to-end-setup-arango
+  tags:
+    - ci-datafed-arango
+  script:
+    - arangod --version
+    - ./scripts/run_arango_service.sh
+
 end-to-end-ws-setup:
   variables:
     IMAGE_TAG: "datafed/ws-"
     GIT_STRATEGY: clone
-    HOST_LOG_FILE_PATH: "$CI_PROJECT_DIR/logs"
+    HOST_LOG_FILE_PATH: "/shared/logs"
     CONTAINER_LOG_FILE_PATH: "/datafed/logs"
+    DATAFED_WEB_KEY_DIR: "/shared/keys"
+    DATAFED_WEB_CERT_NAME: "cert.crt"
+    DATAFED_WEB_KEY_NAME: "cert.key"
+    DATAFED_WEB_CERT_PATH: "${DATAFED_WEB_KEY_DIR}/${DATAFED_WEB_CERT_NAME}"
+    DATAFED_WEB_CSR_PATH: "${DATAFED_WEB_KEY_DIR}/cert.csr"
+    DATAFED_WEB_KEY_PATH: "${DATAFED_WEB_KEY_DIR}/${DATAFED_WEB_KEY_NAME}"
   stage: end-to-end-setup
+  needs: ["end-to-end-core-setup"]
+  #  dependencies: 
+  #  - end-to-end-core-setup
   tags:
     - ci-datafed-core
     - docker
@@ -348,31 +462,41 @@ end-to-end-ws-setup:
     - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - echo "$BRANCH_LOWER"
     - mkdir -p "$HOST_LOG_FILE_PATH"
+    - mkdir -p "${DATAFED_WEB_KEY_DIR}"
+    - ./scripts/ci_setup_web_certs.sh
+    - chmod o+w "${HOST_LOG_FILE_PATH}"
     - chown gitlab-runner "$HOST_LOG_FILE_PATH"
     - ./scripts/generate_datafed.sh
     - docker login  "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
-    - CERT_FILE_NAME=$(basename "${CI_DATAFED_WEB_CERT_PATH}")
-    - KEY_FILE_NAME=$(basename "${CI_DATAFED_WEB_KEY_PATH}")
+    - USER_ID=$(id -u)
+    - GROUP_ID=$(id -g)
+    - CORE_ADDRESS=$(hostname -I | awk '{print $1}')
+    - cat $CI_DATAFED_CORE_PUB_KEY > /shared/datafed-core-key.pub
+    - env
     - ./scripts/container_stop.sh -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}"
     - random_string=$(bash -c "cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w "10" | head -n 1")
-    - >
-      docker run -d
-      --name "ws-${BRANCH_LOWER}-${CI_COMMIT_SHORT_SHA}-${random_string}"
-      -e DATAFED_GLOBUS_APP_SECRET="GLOBUS_DATAFED_APP_SECRET"
-      -e DATAFED_GLOBUS_APP_ID="$GLOBUS_DATAFED_APP_ID"
-      -e DATAFED_ZEROMQ_SESSION_SECRET="$DATAFED_ZEROMQ_SESSION_SECRET"
-      -e DATAFED_ZEROMQ_SYSTEM_SECRET="$DATAFED_ZEROMQ_SYSTEM_SECRET"
-      -e DATAFED_DOMAIN="$CI_DATAFED_DOMAIN"
-      -e DATAFED_WEB_CERT_PATH="${CI_DATAFED_WEB_CERT_PATH}"
-      -e DATAFED_WEB_KEY_PATH="${CI_DATAFED_WEB_KEY_PATH}"
-      -e DATAFED_DEFAULT_LOG_PATH="$CONTAINER_LOG_FILE_PATH"
-      -p 443:443
-      -v "${HOST_LOG_FILE_PATH}:${CONTAINER_LOG_FILE_PATH}"
-      -v "${CI_DATAFED_CORE_PUB_KEY}:/source/install/keys/datafed-core-key.pub"
-      -v "${CI_DATAFED_WEB_CERT_PATH}:/datafed/install/keys/${CERT_FILE_NAME}"
-      -v "${CI_DATAFED_WEB_KEY_PATH}:/datafed/install/keys/${KEY_FILE_NAME}"
-      -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}"
-       </dev/null &>/dev/null &
+    - echo "#!/bin/bash" > run_web.sh
+    - echo "docker run -d \\" >> run_web.sh
+    - echo "--name \"ws-${BRANCH_LOWER}-${CI_COMMIT_SHORT_SHA}-${random_string}\" \\" >> run_web.sh
+    - echo "-e DATAFED_GLOBUS_APP_SECRET=\"$CI_DATAFED_GLOBUS_APP_SECRET\" \\" >> run_web.sh
+    - echo "-e DATAFED_GLOBUS_APP_ID=\"$CI_DATAFED_GLOBUS_APP_ID\" \\" >> run_web.sh
+    - echo "-e DATAFED_ZEROMQ_SESSION_SECRET=\"$CI_DATAFED_ZEROMQ_SESSION_SECRET\" \\" >> run_web.sh
+    - echo "-e DATAFED_ZEROMQ_SYSTEM_SECRET=\"$CI_DATAFED_ZEROMQ_SYSTEM_SECRET\" \\" >> run_web.sh
+    - echo "-e DATAFED_DOMAIN=\"$CI_DATAFED_DOMAIN\" \\" >> run_web.sh
+    - echo "-e DATAFED_WEB_CERT_PATH=\"/datafed/install/keys/${DATAFED_WEB_CERT_NAME}\" \\" >> run_web.sh
+    - echo "-e DATAFED_WEB_KEY_PATH=\"/datafed/install/keys/${DATAFED_WEB_KEY_NAME}\" \\" >> run_web.sh
+    - echo "-e DATAFED_DEFAULT_LOG_PATH=\"${CONTAINER_LOG_FILE_PATH}\" \\" >> run_web.sh
+    - echo "-e DATAFED_CORE_ADDRESS_PORT_INTERNAL=\"$CORE_ADDRESS:7513\" \\" >> run_web.sh
+    - echo "-e UID=\"$USER_ID\" \\" >> run_web.sh
+    - echo "-p 443:443 \\" >> run_web.sh
+    - echo "-v \"${HOST_LOG_FILE_PATH}:${CONTAINER_LOG_FILE_PATH}\" \\" >> run_web.sh
+    - echo "-v \"/shared/datafed-core-key.pub:/datafed/install/keys/datafed-core-key.pub\" \\" >> run_web.sh
+    - echo "-v \"${DATAFED_WEB_CERT_PATH}:/datafed/install/keys/${DATAFED_WEB_CERT_NAME}\" \\" >> run_web.sh
+    - echo "-v \"${DATAFED_WEB_KEY_PATH}:/datafed/install/keys/${DATAFED_WEB_KEY_NAME}\" \\" >> run_web.sh
+    - echo "-t \"${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}\" " >> run_web.sh
+    - chmod +x run_web.sh
+    - ./run_web.sh
+    - sleep 30
     - ./scripts/container_run_test.sh -e -c "1" -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" 
 
 # Note here we use network=host to run the docker container this is
@@ -381,48 +505,61 @@ end-to-end-core-setup:
   variables:
     IMAGE_TAG: "datafed/core-"
     GIT_STRATEGY: clone
-    HOST_LOG_FILE_PATH: "$CI_PROJECT_DIR/logs"
+    HOST_LOG_FILE_PATH: "/shared/logs"
     CONTAINER_LOG_FILE_PATH: "/datafed/logs"
+    DATAFED_DATABASE_HOST: "$CI_DATAFED_DATABASE_HOST"
   stage: end-to-end-setup
+  needs: ["end-to-end-arango-setup"]
   tags:
     - ci-datafed-core
     - docker
   script:
     - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - mkdir -p "$HOST_LOG_FILE_PATH"
+    - chmod o+w "${HOST_LOG_FILE_PATH}"
+    - USER_ID=$(id -u)
     - chown gitlab-runner "$HOST_LOG_FILE_PATH"
     - echo "$BRANCH_LOWER"
     - ./scripts/generate_datafed.sh
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
     - ./scripts/container_stop.sh -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}"
+    - ./scripts/ci_database_health_check.sh
+    - cat $CI_DATAFED_CORE_PUB_KEY > /shared/datafed-core-key.pub
+    - cat $CI_DATAFED_CORE_PRIV_KEY > /shared/datafed-core-key.priv
     - random_string=$(bash -c "cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w "10" | head -n 1")
-    - >
-      docker run -d
-      --name "core-${BRANCH_LOWER}-${CI_COMMIT_SHORT_SHA}-${random_string}"
-      -e DATAFED_GLOBUS_APP_SECRET="$CI_DATAFED_GLOBUS_APP_SECRET"
-      -e DATAFED_GLOBUS_APP_ID="$CI_DATAFED_GLOBUS_APP_ID"
-      -e DATAFED_ZEROMQ_SESSION_SECRET="$CI_DATAFED_ZEROMQ_SESSION_SECRET"
-      -e DATAFED_ZEROMQ_SYSTEM_SECRET="$CI_DATAFED_ZEROMQ_SYSTEM_SECRET"
-      -e DATAFED_DOMAIN="$CI_DATAFED_DOMAIN"
-      -e DATAFED_DATABASE_PASSWORD="$CI_DATAFED_DATABASE_PASSWORD"
-      -e DATAFED_DATABASE_IP_ADDRESS_PORT="$CI_DATAFED_DATABASE_IP_ADDRESS_PORT"
-      -e DATAFED_DEFAULT_LOG_PATH="$CONTAINER_LOG_FILE_PATH"
-      --network host
-      -v "${HOST_LOG_FILE_PATH}:${CONTAINER_LOG_FILE_PATH}"
-      -v "$CI_DATAFED_CORE_PUB_KEY":/source/install/keys/datafed-core-key.pub
-      -v "$CI_DATAFED_CORE_PRIV_KEY":/source/install/keys/datafed-core-key.priv
-      -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" 
-       </dev/null &>/dev/null &
+    - echo "#!/bin/bash" > run_core.sh
+    - echo "docker run -d \\" >> run_core.sh
+    - echo "--name \"core-${BRANCH_LOWER}-${CI_COMMIT_SHORT_SHA}-${random_string}\" \\" >> run_core.sh
+    - echo "-e DATAFED_GLOBUS_APP_SECRET=\"$CI_DATAFED_GLOBUS_APP_SECRET\" \\" >> run_core.sh
+    - echo "-e DATAFED_GLOBUS_APP_ID=\"$CI_DATAFED_GLOBUS_APP_ID\" \\" >> run_core.sh
+    - echo "-e DATAFED_ZEROMQ_SESSION_SECRET=\"$CI_DATAFED_ZEROMQ_SESSION_SECRET\" \\" >> run_core.sh
+    - echo "-e DATAFED_ZEROMQ_SYSTEM_SECRET=\"$CI_DATAFED_ZEROMQ_SYSTEM_SECRET\" \\" >> run_core.sh
+    - echo "-e DATAFED_DOMAIN=\"$CI_DATAFED_DOMAIN\" \\" >> run_core.sh
+    - echo "-e DATAFED_DATABASE_PASSWORD=\"$CI_DATAFED_DATABASE_PASSWORD\" \\" >> run_core.sh
+    - echo "-e DATAFED_DATABASE_IP_ADDRESS_PORT=\"$CI_DATAFED_DATABASE_IP_ADDRESS_PORT\" \\" >> run_core.sh
+    - echo "-e DATAFED_DEFAULT_LOG_PATH=\"$CONTAINER_LOG_FILE_PATH\" \\" >> run_core.sh
+    - echo "-e UID=\"$USER_ID\" \\" >> run_core.sh
+    - echo "-p 7513:7513 \\" >> run_core.sh
+    - echo "-p 7512:7512 \\" >> run_core.sh
+    - echo "-v \"${HOST_LOG_FILE_PATH}:${CONTAINER_LOG_FILE_PATH}\" \\" >> run_core.sh
+    - echo "-v \"/shared/datafed-core-key.pub\":/datafed/install/keys/datafed-core-key.pub \\" >> run_core.sh
+    - echo "-v \"/shared/datafed-core-key.priv\":/datafed/install/keys/datafed-core-key.priv \\" >> run_core.sh
+    - echo "-t \"${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}\"" >> run_core.sh
+    - chmod +x run_core.sh
+    - ./run_core.sh
+    - sleep 10
     - ./scripts/container_run_test.sh -e -c "1" -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" 
 
-
+# Repo server currently will crash on startup if it cannot connect to the core
+# server.
 end-to-end-repo-setup:
   variables:
     IMAGE_TAG: "datafed/repo-"
     GIT_STRATEGY: clone
-    HOST_LOG_FILE_PATH: "$CI_PROJECT_DIR/logs"
+    HOST_LOG_FILE_PATH: "/shared/logs"
     CONTAINER_LOG_FILE_PATH: "/datafed/logs"
   stage: end-to-end-setup
+  needs: ["end-to-end-core-setup"]
   tags:
     - ci-datafed-globus
     - docker
@@ -430,30 +567,40 @@ end-to-end-repo-setup:
     - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - echo "$BRANCH_LOWER"
     - mkdir -p "$HOST_LOG_FILE_PATH"
+    - chmod o+w "${HOST_LOG_FILE_PATH}"
+    - USER_ID=$(id -u)
     - chown gitlab-runner "$HOST_LOG_FILE_PATH"
     - ./scripts/generate_datafed.sh
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
     - ./scripts/container_stop.sh -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}"
     - random_string=$(bash -c "cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w "10" | head -n 1")
-    - >
-      docker run -d
-      --name "repo-${BRANCH_LOWER}-${CI_COMMIT_SHORT_SHA}-${random_string}"
-      -e DATAFED_GLOBUS_APP_SECRET="$CI_DATAFED_GLOBUS_APP_SECRET"
-      -e DATAFED_GLOBUS_APP_ID="$CI_DATAFED_GLOBUS_APP_ID"
-      -e DATAFED_ZEROMQ_SESSION_SECRET="$CI_DATAFED_ZEROMQ_SESSION_SECRET"
-      -e DATAFED_ZEROMQ_SYSTEM_SECRET="$CI_DATAFED_ZEROMQ_SYSTEM_SECRET"
-      -e DATAFED_DOMAIN="$CI_DATAFED_DOMAIN"
-      -v "${HOST_LOG_FILE_PATH}:${CONTAINER_LOG_FILE_PATH}"
-      -v "$CI_DATAFED_REPO_PUB_KEY":/source/install/keys/datafed-repo-key.pub
-      -v "$CI_DATAFED_REPO_PRIV_KEY":/source/install/keys/datafed-repo-key.priv
-      -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" 
-       </dev/null &>/dev/null &
+    - cat $CI_DATAFED_CORE_PUB_KEY > /shared/datafed-repo-key.pub
+    - cat $CI_DATAFED_CORE_PRIV_KEY > /shared/datafed-repo-key.priv
+    - echo "#!/bin/bash" > run_repo.sh
+    - echo "docker run \\" >> run_repo.sh
+    - echo "--name \"repo-${BRANCH_LOWER}-${CI_COMMIT_SHORT_SHA}-${random_string}\" \\" >> run_repo.sh
+    - echo "-e DATAFED_GLOBUS_APP_SECRET=\"$CI_DATAFED_GLOBUS_APP_SECRET\" \\" >> run_repo.sh
+    - echo "-e DATAFED_GLOBUS_APP_ID=\"$CI_DATAFED_GLOBUS_APP_ID\" \\" >> run_repo.sh
+    - echo "-e DATAFED_ZEROMQ_SESSION_SECRET=\"$CI_DATAFED_ZEROMQ_SESSION_SECRET\" \\" >> run_repo.sh
+    - echo "-e DATAFED_ZEROMQ_SYSTEM_SECRET=\"$CI_DATAFED_ZEROMQ_SYSTEM_SECRET\" \\" >> run_repo.sh
+    - echo "-e DATAFED_DOMAIN=\"$CI_DATAFED_DOMAIN\" \\" >> run_repo.sh
+    - echo "-e DATAFED_DEFAULT_LOG_PATH=\"$CONTAINER_LOG_FILE_PATH\" \\" >> run_repo.sh
+    - echo "-e UID=\"$USER_ID\" \\" >> run_repo.sh
+    - echo "-v \"${HOST_LOG_FILE_PATH}:${CONTAINER_LOG_FILE_PATH}\" \\" >> run_repo.sh
+    - echo "-v \"/shared/datafed-repo-key.pub\":/datafed/install/keys/datafed-repo-key.pub \\" >> run_repo.sh
+    - echo "-v \"/shared/datafed-repo-key.priv\":/datafed/install/keys/datafed-repo-key.priv \\" >> run_repo.sh
+    - echo "-t \"${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}\"" >> run_repo.sh
+    - chmod +x run_repo.sh
+    - ./run_repo.sh
+    - sleep 10
     - ./scripts/container_run_test.sh -e -c "1" -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" 
 
+# Requires setting up Globus Connect Server, requires firewall exceptions on
+# the machine running this.
 end-to-end-gcs-authz-setup:
   variables:
     IMAGE_TAG: "datafed/gcs-authz-"
-    HOST_LOG_FILE_PATH: "$CI_PROJECT_DIR/logs"
+    HOST_LOG_FILE_PATH: "/shared/logs"
     CONTAINER_LOG_FILE_PATH: "/datafed/logs"
     GIT_STRATEGY: clone
   stage: end-to-end-setup
@@ -464,73 +611,70 @@ end-to-end-gcs-authz-setup:
     - BRANCH_LOWER=$(echo "$CI_COMMIT_REF_NAME" | tr '[:upper:]' '[:lower:]')
     - echo "$BRANCH_LOWER"
     - mkdir -p "$HOST_LOG_FILE_PATH"
+    - USER_ID=$(id -u)
+    - chmod o+w "${HOST_LOG_FILE_PATH}"
     - chown gitlab-runner "$HOST_LOG_FILE_PATH"
     - ./scripts/generate_datafed.sh
     - docker login "${REGISTRY}" -u "${HARBOR_USER}" -p "${HARBOR_DATAFED_GITLAB_CI_REGISTRY}"
     - ./scripts/container_stop.sh -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}"
     - random_string=$(bash -c "cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w "10" | head -n 1")
-    - >
-      docker run -d
-      --name "gcs-authz-${BRANCH_LOWER}-${CI_COMMIT_SHORT_SHA}-${random_string}"
-      -e DATAFED_GLOBUS_APP_SECRET="$CI_DATAFED_GLOBUS_APP_SECRET"
-      -e DATAFED_GLOBUS_APP_ID="$CI_DATAFED_GLOBUS_APP_ID"
-      -e DATAFED_ZEROMQ_SESSION_SECRET="$CI_DATAFED_ZEROMQ_SESSION_SECRET"
-      -e DATAFED_ZEROMQ_SYSTEM_SECRET="$CI_DATAFED_ZEROMQ_SYSTEM_SECRET"
-      -e DATAFED_DOMAIN="$CI_DATAFED_DOMAIN"
-      -v "${HOST_LOG_FILE_PATH}:${CONTAINER_LOG_FILE_PATH}"
-      -v "$CI_DATAFED_REPO_PUB_KEY":/source/install/keys/datafed-repo-key.pub
-      -v "$CI_DATAFED_REPO_PRIV_KEY":/source/install/keys/datafed-repo-key.priv
-      -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" 
-       </dev/null &>/dev/null &
+    - cat $CI_DATAFED_CORE_PUB_KEY > /shared/datafed-repo-key.pub
+    - cat $CI_DATAFED_CORE_PRIV_KEY > /shared/datafed-repo-key.priv
+    - echo "#!/bin/bash" > run_globus.sh
+    - echo "docker run \\" >> run_globus.sh
+    - echo "--name \"gcs-authz-${BRANCH_LOWER}-${CI_COMMIT_SHORT_SHA}-${random_string}\" \\" >> run_globus.sh
+    - echo "-e DATAFED_GLOBUS_APP_SECRET=\"$CI_DATAFED_GLOBUS_APP_SECRET\" \\" >> run_globus.sh
+    - echo "-e DATAFED_GLOBUS_APP_ID=\"$CI_DATAFED_GLOBUS_APP_ID\" \\" >> run_globus.sh
+    - echo "-e DATAFED_ZEROMQ_SESSION_SECRET=\"$CI_DATAFED_ZEROMQ_SESSION_SECRET\" \\" >> run_globus.sh
+    - echo "-e DATAFED_ZEROMQ_SYSTEM_SECRET=\"$CI_DATAFED_ZEROMQ_SYSTEM_SECRET\" \\" >> run_globus.sh
+    - echo "-e DATAFED_DOMAIN=\"$CI_DATAFED_DOMAIN\" \\" >> run_globus.sh
+    - echo "-e UID=\"$USER_ID\" \\" >> run_globus.sh
+    - echo "-v \"${HOST_LOG_FILE_PATH}:${CONTAINER_LOG_FILE_PATH}\" \\" >> run_globus.sh
+    - echo "-v \"/shared/datafed-repo-key.pub\":/datafed/install/keys/datafed-repo-key.pub \\" >> run_globus.sh
+    - echo "-v \"/shared/datafed-repo-key.priv\":/datafed/install/keys/datafed-repo-key.priv \\" >> run_globus.sh
+    - echo "-t \"${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}\"" >> run_globus.sh
+    - chmod +x run_globus.sh
+    - ./run_globus.sh
+    - sleep 10
     - ./scripts/container_run_test.sh -e -c "1" -t "${REGISTRY}/${IMAGE_TAG}${BRANCH_LOWER}" 
 
-end-to-end-arango-setup:
+################################################################################
+# Actual Testing
+################################################################################
+end-to-end-client-test:
   variables:
     GIT_STRATEGY: clone
-  stage: end-to-end-setup
+    DATAFED_DATABASE_HOST: "${CI_DATAFED_DATABASE_HOST}"
+    DATAFED_DATABASE_ZEROMQ_SYSTEM_SECRET: "${CI_DATAFED_DATABASE_ZEROMQ_SYSTEM_SECRET}"
+    DATAFED_DATABASE_PASSWORD: "${CI_DATAFED_DATABASE_PASSWORD}"
+    DATAFED_USER89_PASSWORD: "${CI_DATAFED_USER89_PASSWORD}"
+    DATAFED_USER89_GLOBUS_REFRESH_TOKEN: "${CI_DATAFED_USER89_GLOBUS_REFRESH_TOKEN}"
+    DATAFED_USER89_GLOBUS_ACCESS_TOKEN: "${CI_DATAFED_USER89_GLOBUS_ACCESS_TOKEN}"
+    DATAFED_USER89_GLOBUS_UUID: "${CI_DATAFED_USER89_GLOBUS_UUID}"
+    DATAFED_USER99_PASSWORD: "${CI_DATAFED_USER99_PASSWORD}"
+    DATAFED_USER99_GLOBUS_REFRESH_TOKEN: "${CI_DATAFED_USER99_GLOBUS_REFRESH_TOKEN}"
+    DATAFED_USER99_GLOBUS_ACCESS_TOKEN: "${CI_DATAFED_USER99_GLOBUS_ACCESS_TOKEN}"
+    DATAFED_USER99_GLOBUS_UUID: "${CI_DATAFED_USER99_GLOBUS_UUID}"
+    DATAFED_ZEROMQ_SYSTEM_SECRET: "${CI_DATAFED_ZEROMQ_SYSTEM_SECRET}"
+  stage: end-to-end-test
   tags:
-    - ci-datafed-arango
+    - ci-datafed-client
   script:
-    - arangod --version
-    - ./scripts/run_arango_service.sh
+    - ./scripts/generate_datafed.sh
+    - env
+    - cat ./config/datafed.sh
+    - >
+      cmake -S. -B build
+      -DENABLE_FOXX_TESTS=OFF
+      -DBUILD_CORE_SERVER=OFF
+      -DBUILD_COMMON=OFF
+      -DBUILD_WEB_SERVER=OFF
+      -DBUILD_DOCS=OFF
+      -DBUILD_PYTHON_CLIENT=ON
+      -DBUILD_TESTS=ON
+      -DENABLE_END_TO_END_TESTS=ON
+      -DINSTALL_FOXX=OFF
+    - cmake --build build
+    - cmake --build build --target pydatafed
+    - cmake --build build --target test
 
-################################################################################
-# Actual Testing
-################################################################################
-#end-to-end-client-test:
-#  variables:
-#    GIT_STRATEGY: clone
-#    DATAFED_DATABASE_HOST: "${CI_DATAFED_DATABASE_HOST}"
-#    DATAFED_DATABASE_ZEROMQ_SYSTEM_SECRET: "${CI_DATAFED_DATABASE_ZEROMQ_SYSTEM_SECRET}"
-#    DATAFED_DATABASE_PASSWORD: "${CI_DATAFED_DATABASE_PASSWORD}"
-#    DATAFED_USER89_PASSWORD: "${CI_DATAFED_USER89_PASSWORD}"
-#    DATAFED_USER89_GLOBUS_REFRESH_TOKEN: "${CI_DATAFED_USER89_GLOBUS_REFRESH_TOKEN}"
-#    DATAFED_USER89_GLOBUS_ACCESS_TOKEN: "${CI_DATAFED_USER89_GLOBUS_ACCESS_TOKEN}"
-#    DATAFED_USER89_GLOBUS_UUID: "${CI_DATAFED_USER89_GLOBUS_UUID}"
-#    DATAFED_USER99_PASSWORD: "${CI_DATAFED_USER99_PASSWORD}"
-#    DATAFED_USER99_GLOBUS_REFRESH_TOKEN: "${CI_DATAFED_USER99_GLOBUS_REFRESH_TOKEN}"
-#    DATAFED_USER99_GLOBUS_ACCESS_TOKEN: "${CI_DATAFED_USER99_GLOBUS_ACCESS_TOKEN}"
-#    DATAFED_USER99_GLOBUS_UUID: "${CI_DATAFED_USER99_GLOBUS_UUID}"
-#    DATAFED_ZEROMQ_SYSTEM_SECRET: "${CI_DATAFED_ZEROMQ_SYSTEM_SECRET}"
-#  stage: end-to-end-test
-#  tags:
-#    - ci-datafed-client
-#  script:
-#    - ./scripts/generate_datafed.sh
-#    - env
-#    - cat ./config/datafed.sh
-#    - >
-#      cmake -S. -B build
-#      -DENABLE_FOXX_TESTS=OFF
-#      -DBUILD_CORE_SERVER=OFF
-#      -DBUILD_COMMON=OFF
-#      -DBUILD_WEB_SERVER=OFF
-#      -DBUILD_DOCS=OFF
-#      -DBUILD_PYTHON_CLIENT=ON
-#      -DBUILD_TESTS=ON
-#      -DENABLE_END_TO_END_TESTS=ON
-#      -DINSTALL_FOXX=OFF
-#    - cmake --build build
-#    - cmake --build build --target pydatafed
-#    - cmake --build build --target test
-#
diff --git a/.gitmodules b/.gitmodules
index e183f8616..a7921d851 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
 [submodule "external/globus-connect-server-deploy"]
 	path = external/globus-connect-server-deploy
 	url = https://github.com/globus/globus-connect-server-deploy.git
+[submodule "external/protobuf"]
+	path = external/protobuf
+	url = https://github.com/protocolbuffers/protobuf.git
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a6e9c6c1c..fa2614eec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,10 +3,17 @@
 ## MAJOR Breaking changes
 
 ## MINOR Feature
-1. [909] - Added Support for Google Analytics
-2. [916] - Reworked Docker build process to be faster and produce simpler and smaller images
+
+1. [912] - Adds initial compose file and docker files for python-client
+2. [909] - Added Support for Google Analytics
+3. [916] - Reworked Docker build process to be faster and produce simpler and
+   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
+
 1. [914] - Improve GitHub template
 2. [910] - Static code analysis and status checks fixed, improvements to CI
 3. [923] - Fixed missing flag in certificate refresh script
@@ -16,38 +23,50 @@
 7. [935] - Fixed CI branching with dynamic children, swiched to Harbor registry.
    Moved Blake's files into the correct folders.
 8. [924] - Fix log messaging format in web server
-
+9. [948] - Bug in getProjectId fixed, better handling of env variables in
+   python.
+10. [952] - Bug in globus cleanup python script fixed, was trying to use projects
+    instead of the auth_client.
 # v2023.10.23.15.50
 
 ## MINOR Feature
+
 1. [906] - Added backup and cert refresh scripts. 
 
 ## PATCH Bug Fixes/Technical Debt/Documentation
+
 1. [911] - Add GitHub template
-2. [913] - Fixed bug, when endpoint info returns an empty array check to seee if array is empty before accessing elements
+2. [913] - Fixed bug, when endpoint info returns an empty array check to see
+   if array is empty before accessing elements
 
 # v2023.8.21.10.40
 
 ## MAJOR Breaking changes
+
 1. [879] - Secured ZeroMQ messaging
 
 ## MINOR Feature
+
 1. [879] - Added GitLab CI file
 2. [879] - Added several files to generate configuration files
 3. [879] - Added several scripts for installing dependencies
 4. [879] - Added support for Globus Connect Server 5.4
-5. [879] - Added log support (core server/repo server/web server) partial support in python CLI
+5. [879] - Added log support (core server/repo server/web server) partial
+   support in python CLI
 6. [879] - Added proxy timeout capability to assist in testing
 7. [879] - Added correlation ids to messages for tracking
 8. [879] - Split log output into server specific files.
 
 ## PATCH Bug Fixes/Technical Debt/Documentation
-1. [879] - Fixed thread safety of repo list calls in core server which where causing
-seg faults
-2. [879] - Added better error reporting when attempting to delete repo with running tasks.
+
+1. [879] - Fixed thread safety of repo list calls in core server which where
+   causing seg faults
+2. [879] - Added better error reporting when attempting to delete repo with
+   running tasks.
 3. [879] - Added GitIgnore File
 4. [879] - Upgraded to C++ 17 std
-5. [879] - Split build and install of different DataFed components web, repo, core, foxx
+5. [879] - Split build and install of different DataFed components web, repo,
+   core, foxx
 6. [879] - Config files auto generated from single datafed.sh config
 7. [879] - Removed most calls to sdms and replaced with datafed for consistency
 8. [879] - Replaced thread pointers with threads in CoreServer.hpp
@@ -55,7 +74,10 @@ seg faults
 10. [879] - Authz will look for authz file in default location if the
     DATAFED_AUTHZ_CFG_FILE env variable is not specified
 11. [879] - Applied formatting, black, autopep8, js-beautify, clange-format10
-12. [879] - Added pipeline script to robustly handle the provisioning of CI infrastructure
+12. [879] - Added pipeline script to robustly handle the provisioning of CI
+    infrastructure
 13. [879] - Added workflows to check formatting and trigger formatting if needed
-14. [879] - Added tests: unit, foxx api tests, end to end tests, internal integration tests
-15. [879] - Standardized versioning, applied versioning to all APIs and clients and servers
+14. [879] - Added tests: unit, foxx api tests, end to end tests, internal
+    integration tests
+15. [879] - Standardized versioning, applied versioning to all APIs and clients
+    and servers
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 19d6d9572..5e1ca2e68 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,7 @@
 cmake_minimum_required (VERSION 3.17.0)
 project( DataFed CXX C )
 include(./cmake/Version.cmake)
+include(./cmake/Utils.cmake)
 
 set(DataFed_VERSION "${DATAFED_RELEASE_YEAR}.${DATAFED_RELEASE_MONTH}.${DATAFED_RELEASE_DAY}.${DATAFED_RELEASE_HOUR}.${DATAFED_RELEASE_MINUTE}")
 
@@ -15,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)
@@ -24,25 +26,30 @@ 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})
 set(INSTALL_CORE_SERVER ${BUILD_CORE_SERVER})
 set(INSTALL_WEB_SERVER ${BUILD_WEB_SERVER})
 
-function(get_version_from_script INPUT_STRING PATTERN_TO_MATCH OUTPUT_VERSION)
-    string(REGEX MATCH "${PATTERN_TO_MATCH}=(.*)" MATCHED_PART "${INPUT_STRING}")
-    if(MATCHED_PART)
-        string(STRIP "${CMAKE_MATCH_1}" SANITIZED_OUTPUT_VERSION)
-        string(REGEX REPLACE "\"\n.*" "" NEWLINE_REMOVED "${SANITIZED_OUTPUT_VERSION}")
-	string(REPLACE "\"" "" REMOVED_QUOTES "${NEWLINE_REMOVED}")
-	set(${OUTPUT_VERSION} "${REMOVED_QUOTES}" PARENT_SCOPE)
-    endif()
-endfunction()
+set(DATAFED_CONFIG_SH "${DataFed_SOURCE_DIR}/config/datafed.sh")
+if(NOT EXISTS ${DATAFED_CONFIG_SH})
+  message(FATAL_ERROR "Error: File '${DATAFED_CONFIG_SH}' does not exist. "
+    "Please run generate_datafed.sh first to populate defaults."
+    "${PROJECT_SOURCE_DIR}/scripts/generate_datafed.sh")
+endif()
 
 file(READ "${PROJECT_SOURCE_DIR}/scripts/dependency_versions.sh" DEPENDENCY_VERSIONS)
 
-get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_PROTOBUF_VERSION" PROTOBUF_VERSION)
+get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_DYNAMIC_LIBRARY_PROTOBUF_VERSION" PROTOBUF_LIBRARY_VERSION)
+get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_PROTOBUF_VERSION" PROTOBUF_COMPILER_VERSION)
 get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_NLOHMANN_JSON_VERSION" NLOHMANN_JSON_VERSION)
 get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_LIBZMQ_VERSION" LIBZMQ_VERSION)
 get_version_from_script(${DEPENDENCY_VERSIONS} "DATAFED_LIBSODIUM_VERSION" LIBSODIUM_VERSION)
@@ -50,6 +57,17 @@ 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()
+
+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}")
 
 if ( BUILD_TESTS ) 
   include(CTest)
@@ -59,11 +77,11 @@ endif()
 
 # Off by default because running it on production would overwrite things on the database
 if( NOT DEFINED ENABLE_FOXX_TESTS )
-	if( NOT DEFINED ENV{ENABLE_FOXX_TESTS} )
-		set(ENABLE_FOXX_TESTS FALSE)
-	else()
-		set(ENABLE_FOXX_TESTS ENV{ENABLE_FOXX_TESTS})
-	endif()
+  if( NOT DEFINED ENV{ENABLE_FOXX_TESTS} )
+    set(ENABLE_FOXX_TESTS FALSE)
+  else()
+    set(ENABLE_FOXX_TESTS ENV{ENABLE_FOXX_TESTS})
+  endif()
 else()
   # Avoid throwing warning by explicit set
   set(ENABLE_FOXX_TESTS ${ENABLE_FOXX_TESTS})
@@ -74,29 +92,10 @@ if( NOT DEFINED ENABLE_END_TO_END_TESTS )
   set(ENABLE_END_TO_END_TESTS FALSE)
 endif()
 
-message( "Building for " ${CMAKE_BUILD_TYPE} )
-
 if(CMAKE_MAKE_PROGRAM MATCHES "(make|gmake)")
     add_definitions( -Wall -Wextra )
 endif()
 
-# Set the path to the shell script
-set(DATAFED_SH_PATH ${PROJECT_SOURCE_DIR}/config/datafed.sh)
-
-if ( NOT EXISTS ${DATAFED_SH_PATH} )
-  message(FATAL_ERROR "No datafed.sh config file was found at "
-    "${DATAFED_SH_PATH} make sure you first run "
-    "${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_datafed.sh")
-endif()
-
-
-# Use execute_process to source the shell script and get the value
-execute_process(
-  COMMAND bash -c "source ${DATAFED_SH_PATH} && echo \$DATAFED_INSTALL_PATH"
-  OUTPUT_VARIABLE DATAFED_INSTALL_PATH
-  OUTPUT_STRIP_TRAILING_WHITESPACE
-  )
-
 configure_file(
   "${CMAKE_CURRENT_SOURCE_DIR}/common/proto/common/Version.proto.in"
   "${CMAKE_CURRENT_SOURCE_DIR}/common/proto/common/Version.proto"
@@ -106,77 +105,25 @@ 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()
 
 if ( BUILD_REPO_SERVER OR BUILD_CORE_SERVER OR BUILD_AUTHZ OR BUILD_COMMON OR BUILD_PYTHON_CLIENT) 
-  if ( DEFINED ENV{PBUF_INC_DIR} AND DEFINED ENV{PBUF_LIB_DIR})
-          message( "PBUF_INC_DIR:" $ENV{PBUF_INC_DIR} )
-          message( "PBUF_LIB_DIR:" $ENV{PBUF_LIB_DIR} )
-          include_directories( $ENV{PBUF_INC_DIR} )
-          link_directories( $ENV{PBUF_LIB_DIR} )
-    set( Protobuf_LIBRARY $ENV{PBUF_LIB_DIR} )
-  endif()
-
-  find_package(Protobuf REQUIRED VERSION "${PROTOBUF_VERSION}" )
-  message("Protobuf library ${Protobuf_LIBRARIES}")
-  set( PROTOBUF_IMPORT_DIRS ${PROTOBUF_INCLUDE_DIRS})
+  include(./cmake/Protobuf.cmake)
 endif() 
 
 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
   find_package(PkgConfig)
-  ## use pkg-config to get hints for 0mq locations
-  #pkg_check_modules(PC_ZeroMQ QUIET zmq)
-  pkg_check_modules(PkgConfig_ZMQ REQUIRED IMPORTED_TARGET libzmq)
-  #pkg_check_modules(PkgConfig_ZMQ_STATIC REQUIRED IMPORTED_TARGET libzmq-static)
-
-  if(NOT "${LIBZMQ_VERSION}" STREQUAL "${PkgConfig_ZMQ_VERSION}")
-	  message(FATAL_ERROR "Required ZeroMQ version ${LIBZMQ_VERSION}, not satisfied. Found version ${PkgConfig_ZMQ_VERSION}")
-  endif()
-
-  ## use the hint from above to find where 'zmq.h' is located
-  #find_path(ZeroMQ_INCLUDE_DIR
-          #NAMES zmq.h
-          #PATHS ${PC_ZeroMQ_INCLUDE_DIRS}
-          #)
-
-  ## use the hint from about to find the location of libzmq
-  #find_library(ZeroMQ_LIBRARY
-  #        NAMES zmq
-  #        PATHS ${PC_ZeroMQ_LIBRARY_DIRS}
-  #        )
-
-  #include_directories( ${ZeroMQ_INCLUDE_DIR} )
-  #link_directories( ${PC_ZeroMQ_LIBRARY_DIRS} )
-
-  #message( TRACE "PROTOBUF_IMPORT_DIRS:" ${PROTOBUF_IMPORT_DIRS} )
 
+  include(./cmake/ZeroMQ.cmake)
   if(NOT CMAKE_BUILD_TYPE)
     set(CMAKE_BUILD_TYPE Release)
   endif()
@@ -199,10 +146,11 @@ if( BUILD_COMMON )
 endif()
 
 if( BUILD_CORE_SERVER )
-  find_package(nlohmann_json CONFIG REQUIRED)
-  find_package(nlohmann_json_schema_validator CONFIG REQUIRED VERSION "${JSON_SCHEMA_VALIDATOR_VERSION}")
-  find_package(OpenSSL "${LOCAL_OPENSSL_VERSION}" REQUIRED )
-  find_package(CURL "${LOCAL_CURL_VERSION}" REQUIRED )
+  include(./cmake/JSON.cmake)
+  include(./cmake/JSONSchema.cmake)
+  include(./cmake/Zlib.cmake)
+  include(./cmake/OpenSSL.cmake)
+  include(./cmake/CURL.cmake)
 endif()
 
 if( BUILD_FOXX OR BUILD_CORE_SERVER )
@@ -239,9 +187,6 @@ endif()
 
 if( INSTALL_CORE_SERVER )
   install( TARGETS datafed-core DESTINATION ${DATAFED_INSTALL_PATH}/core )
-  # The below steps should exist outside of the build steps
-  #install( CODE "execute_process(COMMAND ${PROJECT_SOURCE_DIR}/scripts/install_lego_and_certificates.sh )")
-  #  install( CODE "execute_process(COMMAND ${PROJECT_SOURCE_DIR}/scripts/install_core_service.sh )")
 endif()
 
 if( INSTALL_WEB_SERVER )
@@ -249,8 +194,6 @@ if( INSTALL_WEB_SERVER )
   install( DIRECTORY ${PROJECT_SOURCE_DIR}/web/static DESTINATION ${DATAFED_INSTALL_PATH}/web )
   install( DIRECTORY ${PROJECT_SOURCE_DIR}/web/views DESTINATION ${DATAFED_INSTALL_PATH}/web )
   install( FILES ${PROJECT_SOURCE_DIR}/web/version.js DESTINATION ${DATAFED_INSTALL_PATH}/web )
-  #Installing services should be a separate step outside of the build
-  #install( CODE "execute_process(COMMAND ${PROJECT_SOURCE_DIR}/scripts/install_ws_service.sh )")
 endif()
 
 if( INSTALL_FOXX ) 
@@ -265,28 +208,54 @@ if( ENABLE_END_TO_END_TESTS )
   endif()
 endif()
 
+find_library(SODIUM NAMES libsodium.a PATHS ${DEPENDENCY_INSTALL_PATH}/lib)
+
 # This is not needed but it is used by zmq I think so we want to print what is found locally just in case
-if (PKG_CONFIG_FOUND)
-	pkg_check_modules(sodium_PKG QUIET libsodium)
-endif()
+#if (PKG_CONFIG_FOUND)
+#  pkg_check_modules(sodium_PKG QUIET libsodium)
+#endif()
 message("")
 message("Tested Dependency Versions")
-message("             protobuf Version: ${PROTOBUF_VERSION}")
-message("         nlohman json Version: ${NLOHMANN_JSON_VERSION}")
+message("             protobuf Version: ${PROTOBUF_LIBRARY_VERSION}")
+message("               protoc Version: ${PROTOBUF_COMPILER_VERSION}")
 message("               libzmq Version: ${LIBZMQ_VERSION}")
 message("            libsodium Version: ${LIBSODIUM_VERSION}")
+message("         nlohman json Version: ${NLOHMANN_JSON_VERSION}")
 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: ${Protobuf_VERSION}")
-message("         nlohman json Version: ${nlohmann_json_VERSION}")
-message("               libzmq Version: ${PkgConfig_ZMQ_VERSION}")
-message("            libsodium Version: ${sodium_PKG_VERSION}")
-message("json_schema_validator Version: ${nlohmann_json_schema_validator_VERSION}")
-message("                 curl Version: ${CURL_VERSION_STRING}")
+message("             protobuf Version: ${DATAFED_PROTOBUF_VERSION_ACTUAL}")
+message("               protoc Version: ${DATAFED_PROTOBUF_COMPILER_VERSION_ACTUAL}")
+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: ${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}")
+message("                protoc Executable: ${DATAFED_PROTOC_EXECUTABLE_PATH}")
+message("             protobuf Include Dir: ${DATAFED_PROTOBUF_INCLUDE_PATH}")
+message("                   libzmq Library: ${DATAFED_ZEROMQ_LIBRARY_PATH}")
+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("                   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/JSON.cmake b/cmake/JSON.cmake
new file mode 100644
index 000000000..61014280c
--- /dev/null
+++ b/cmake/JSON.cmake
@@ -0,0 +1,25 @@
+
+function(find_json_library)
+
+  set(JSON_INCLUDE_DIR "" )
+  set(JSON_LIB_DIR "" )
+  set(JSON_LIB_CMAKE_DIR "" )
+  set(JSON_LIBRARY_PATH "" )
+
+  if(EXISTS ${DEPENDENCY_INSTALL_PATH})
+    set(JSON_LIB_CMAKE_DIR "${DEPENDENCY_INSTALL_PATH}/lib/cmake")
+    set(JSON_INCLUDE_DIR "${DEPENDENCY_INSTALL_PATH}/include")
+    set(JSON_LIB_DIR "${DEPENDENCY_INSTALL_PATH}/lib")
+  endif()
+
+
+  find_package(nlohmann_json CONFIG REQUIRED PATHS "${JSON_LIB_CMAKE_DIR}")
+
+  # NOTE interfaces do not have a physical location associated with the library
+  get_target_property(JSON_INCLUDE_PATH nlohmann_json::nlohmann_json INTERFACE_INCLUDE_DIRECTORIES )
+
+  set(DATAFED_JSON_INCLUDE_PATH "${JSON_INCLUDE_PATH}" PARENT_SCOPE)
+  set(DATAFED_JSON_VERSION_ACTUAL "${nlohmann_json_VERSION}" PARENT_SCOPE)
+endfunction()
+
+find_json_library()
diff --git a/cmake/JSONSchema.cmake b/cmake/JSONSchema.cmake
new file mode 100644
index 000000000..c326982ea
--- /dev/null
+++ b/cmake/JSONSchema.cmake
@@ -0,0 +1,27 @@
+
+function(find_json_schema_library)
+
+  set(JSON_SCHEMA_INCLUDE_DIR "" )
+  set(JSON_SCHEMA_LIB_DIR "" )
+  set(JSON_SCHEMA_LIB_CMAKE_DIR "" )
+  set(JSON_SCHEMA_LIBRARY_PATH "" )
+
+  if(EXISTS ${DEPENDENCY_INSTALL_PATH})
+    set(JSON_SCHEMA_LIB_CMAKE_DIR "${DEPENDENCY_INSTALL_PATH}/lib/cmake")
+    set(JSON_SCHEMA_INCLUDE_DIR "${DEPENDENCY_INSTALL_PATH}/include")
+    set(JSON_SCHEMA_LIB_DIR "${DEPENDENCY_INSTALL_PATH}/lib")
+  endif()
+
+  find_package(nlohmann_json_schema_validator CONFIG REQUIRED VERSION "${JSON_SCHEMA_VALIDATOR_VERSION}" PATHS "${JSON_SCHEMA_LIB_CMAKE_DIR}")
+
+  # NOTE interfaces do not have a physical location associated with the library
+  get_target_property(JSON_SCHEMA_INCLUDE_PATH nlohmann_json_schema_validator INTERFACE_INCLUDE_DIRECTORIES)
+  get_target_property(JSON_SCHEMA_LIBRARY_PATH nlohmann_json_schema_validator IMPORTED_LOCATION_NOCONFIG)
+
+  set(DATAFED_JSON_SCHEMA_INCLUDE_PATH "${JSON_SCHEMA_INCLUDE_PATH}" PARENT_SCOPE)
+  set(DATAFED_JSON_SCHEMA_LIBRARY_PATH "${JSON_SCHEMA_LIBRARY_PATH}" PARENT_SCOPE)
+  set(DATAFED_JSON_SCHEMA_VERSION_ACTUAL "${nlohmann_json_schema_validator_VERSION}" PARENT_SCOPE)
+
+endfunction()
+
+find_json_schema_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/Protobuf.cmake b/cmake/Protobuf.cmake
new file mode 100644
index 000000000..b327f8431
--- /dev/null
+++ b/cmake/Protobuf.cmake
@@ -0,0 +1,69 @@
+
+function(find_protobuf_library)
+  set(PROTOBUF_INCLUDE_DIR "" )
+  set(PROTOBUF_LIB_DIR "" )
+  set(PROTOBUF_LIB_CMAKE_DIR "" )
+  set(PROTOBUF_LIBRARY_PATH "" )
+  set(PROTOC_LIBRARY_PATH "" )
+  set(PROTOC_EXECUTABLE_PATH "" )
+
+  if(EXISTS ${DEPENDENCY_INSTALL_PATH})
+    set(PROTOBUF_LIB_CMAKE_DIR "${DEPENDENCY_INSTALL_PATH}/lib/cmake")
+    set(PROTOBUF_INCLUDE_DIR "${DEPENDENCY_INSTALL_PATH}/include")
+    set(PROTOBUF_LIB_DIR "${DEPENDENCY_INSTALL_PATH}/lib")
+  endif()
+
+  find_package(Protobuf REQUIRED CONFIG ${PROTOBUF_LIBRARY_VERSION} PATHS "${PROTOBUF_LIB_CMAKE_DIR}" COMPONENTS libprotobuf libprotoc)
+
+  # This is necessary because they decided to increment the protoc tool starting
+  # with the minor version number but kept the library incrementing with a major
+  # version cmake fails with using find_package with a REQUIRED statement
+  # because looks at both of these numbers which are different so here
+  # we compare the version of protoc differently from the library
+  if(NOT ${Protobuf_VERSION} STREQUAL ${PROTOBUF_LIBRARY_VERSION} )
+    message(FATAL_ERROR "Versions of proto library (${Protobuf_VERSION}) and the required version (${PROTOBUF_LIBRARY_VERSION}) do not match.")
+  endif()
+
+  get_target_property(PROTOBUF_LIBRARY_PATH protobuf::libprotobuf IMPORTED_LOCATION_NOCONFIG)
+  get_target_property(PROTOC_LIBRARY_PATH protobuf::libprotoc IMPORTED_LOCATION_NOCONFIG)
+  get_target_property(PROTOC_EXECUTABLE_PATH protobuf::protoc IMPORTED_LOCATION_NOCONFIG)
+
+  include_directories("${PROTOBUF_INCLUDE_DIR}")
+  get_target_property(PROTOC_PATH protobuf::protoc IMPORTED_LOCATION_NOCONFIG)
+
+  set(ENV{LD_LIBRARY_PATH} "${PROTOBUF_LIB_DIR}:$ENV{LD_LIBRARY_PATH}")
+  execute_process(
+    COMMAND ${PROTOC_PATH} --version
+    OUTPUT_VARIABLE PROTOBUF_COMPILER_VERSION_OUTPUT
+    ERROR_VARIABLE PROTOBUF_VERSION_ERROR
+    RESULT_VARIABLE PROTOBUF_VERSION_RESULT
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_STRIP_TRAILING_WHITESPACE
+    )
+
+  # Remove library name from output
+  # Regular expression to match the first word
+  set(regex "^[^ ]+ ")
+
+  # Remove the first word using string(REGEX REPLACE ...)
+  string(REGEX REPLACE "${regex}" "" PROTOBUF_COMPILER_VERSION_ACTUAL "${PROTOBUF_COMPILER_VERSION_OUTPUT}")
+
+  if(NOT ${PROTOBUF_COMPILER_VERSION_ACTUAL} STREQUAL ${PROTOBUF_COMPILER_VERSION} )
+    message(FATAL_ERROR "Versions of protoc compiler (${PROTOBUF_COMPILER_VERSION_ACTUAL}) and the required version (${PROTOBUF_COMPILER_VERSION}) do not match.")
+  endif()
+
+  set( PROTOBUF_IMPORT_DIRS ${PROTOBUF_INCLUDE_DIRS})
+
+  set(DATAFED_PROTOBUF_INCLUDE_DIR "${PROTOBUF_INCLUDE_DIR}" PARENT_SCOPE)
+  set(DATAFED_PROTOBUF_LIB_DIR "${PROTOBUF_LIB_DIR}" PARENT_SCOPE)
+  set(DATAFED_PROTOBUF_LIB_CMAKE_DIR "${PROTOBUF_LIB_CMAKE_DIR}" PARENT_SCOPE)
+  set(DATAFED_PROTOBUF_LIBRARY_PATH "${PROTOBUF_LIBRARY_PATH}" PARENT_SCOPE)
+  set(DATAFED_PROTOBUF_INCLUDE_PATH "${PROTOBUF_INCLUDE_DIR}" PARENT_SCOPE)
+  set(DATAFED_PROTOC_LIBRARY_PATH "${PROTOC_LIBRARY_PATH}" PARENT_SCOPE)
+  set(DATAFED_PROTOC_EXECUTABLE_PATH "${PROTOC_EXECUTABLE_PATH}" PARENT_SCOPE)
+  set(DATAFED_PROTOBUF_COMPILER_VERSION_ACTUAL "${PROTOBUF_COMPILER_VERSION_ACTUAL}" PARENT_SCOPE)
+
+  set(DATAFED_PROTOBUF_VERSION_ACTUAL "${Protobuf_VERSION}" PARENT_SCOPE)
+endfunction()
+
+find_protobuf_library()
diff --git a/cmake/Utils.cmake b/cmake/Utils.cmake
new file mode 100644
index 000000000..9ae7fabab
--- /dev/null
+++ b/cmake/Utils.cmake
@@ -0,0 +1,47 @@
+
+
+# Function is designed to pull the value of a key value pair file i.e.
+#
+# file.sh
+#
+# PROTO_VERSION="v2.1.0"
+# ZMQ_VERSION="v5.53.3"
+#
+# Read the script first
+# 
+# file(READ "file.sh" SCRIPT_CONTENT)
+# get_version_from_script(${SCRIPT_CONTENT} "PROTO_VERSION" value)
+# message("$value")
+#
+# Will output "v2.1.0"
+function(get_version_from_script INPUT_STRING PATTERN_TO_MATCH OUTPUT_VERSION)
+  string(REGEX MATCH "${PATTERN_TO_MATCH}=(.*)" MATCHED_PART "${INPUT_STRING}")
+  if(MATCHED_PART)
+    string(STRIP "${CMAKE_MATCH_1}" SANITIZED_OUTPUT_VERSION)
+    string(REGEX REPLACE "\"\n.*" "" NEWLINE_REMOVED "${SANITIZED_OUTPUT_VERSION}")
+    string(REPLACE "\"" "" REMOVED_QUOTES "${NEWLINE_REMOVED}")
+    set(${OUTPUT_VERSION} "${REMOVED_QUOTES}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+# Function will get exported value from a shell script
+#
+# i.e. if datafed.sh has 
+#
+# datafed.sh
+# export MY_NAME="Barry"
+#
+# set(DATAFED_CONFIG_SH "config/datafed.sh") 
+# get_value_from_datafed_sh "MY_NAME" name)
+# message("$name")
+#
+# Will output "Barry"
+function(get_value_from_datafed_sh INPUT_KEY OUTPUT_VALUE)
+  execute_process(
+    COMMAND bash "-c" "source ${DATAFED_CONFIG_SH} && echo \$${INPUT_KEY}"
+    OUTPUT_VARIABLE OUTPUT_VAR
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+  set(${OUTPUT_VALUE} "${OUTPUT_VAR}" PARENT_SCOPE)
+endfunction()
+
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
new file mode 100644
index 000000000..3b0ad50ba
--- /dev/null
+++ b/cmake/ZeroMQ.cmake
@@ -0,0 +1,82 @@
+function(find_zeromq_library)
+  set(ZEROMQ_INCLUDE_DIR "" )
+  set(ZEROMQ_LIB_DIR "" )
+  set(ZEROMQ_LIB_CMAKE_DIR "" )
+  set(ZEROMQ_LIBRARY_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()
+
+  find_package(ZeroMQ REQUIRED CONFIG PATHS "${ZEROMQ_LIB_CMAKE_DIR}")
+
+  get_target_property(ZEROMQ_LIBRARY_PATH libzmq-static LOCATION)
+  get_target_property(ZEROMQ_DEPENDENCIES libzmq-static INTERFACE_LINK_LIBRARIES)
+
+  if(NOT "${LIBZMQ_VERSION}" STREQUAL "${ZeroMQ_VERSION}")
+    message(FATAL_ERROR "Required ZeroMQ version ${LIBZMQ_VERSION}, not satisfied. Found version ${ZeroMQ_VERSION}")
+  endif()
+
+  string(REPLACE ";" "\n" DEP_LIST "${ZEROMQ_DEPENDENCIES}")
+  string(REGEX REPLACE "\n+" ";" DEP_LIST "${DEP_LIST}")
+  # Iterate through the list
+  foreach(item IN LISTS DEP_LIST)
+    # Search for the pattern in each item
+    string(FIND ${item} "sodium" pattern_index)
+    # If the pattern is found
+    if(NOT ${pattern_index} EQUAL -1)
+      set(ZEROMQ_SODIUM_LIBRARY_PATH "${item}")
+    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 ${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)
+  set(DATAFED_ZEROMQ_LIB_CMAKE_DIR "${ZEROMQ_LIB_CMAKE_DIR}"  PARENT_SCOPE)
+  set(DATAFED_ZEROMQ_LIBRARY_PATH "${ZEROMQ_LIBRARY_PATH}"  PARENT_SCOPE)
+  set(DATAFED_ZEROMQ_DEPENDENCEIS "${ZEROMQ_DEPENDENCIES}" PARENT_SCOPE)
+  set(DATAFED_ZEROMQ_SODIUM_LIBRARY_PATH "${ZEROMQ_SODIUM_LIBRARY_PATH}" PARENT_SCOPE)
+  set(DATAFED_ZEROMQ_SODIUM_VERSION "${SODIUM_VERSION_OUTPUT}" PARENT_SCOPE)
+  set(DATAFED_ZEROMQ_VERSION_ACTUAL "${ZeroMQ_VERSION}" PARENT_SCOPE)
+endfunction()
+
+find_zeromq_library()
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/sodium_version.cpp b/cmake/sodium_version.cpp
new file mode 100644
index 000000000..dbcbd2654
--- /dev/null
+++ b/cmake/sodium_version.cpp
@@ -0,0 +1,13 @@
+#include <iostream>
+#include <sodium.h>
+
+int main() {
+  if (sodium_init() == -1) {
+    std::cerr << "sodium_init() failed" << std::endl;
+    return 1;
+  }
+
+  std::cout << sodium_version_string() << 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 2f0fc8d6e..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_LIBRARIES} 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 9029c87bb..ac65a08de 100644
--- a/common/proto/common/CMakeLists.txt
+++ b/common/proto/common/CMakeLists.txt
@@ -1,9 +1,21 @@
 cmake_minimum_required (VERSION 3.17.0)
 
-# The below command will by default generate the proto files in this folder
-# we want to place them in the binary folder in /proto/common
-PROTOBUF_GENERATE_CPP( ProtoSourcesCpp ProtoHeadersCpp ${ProtoFiles} )
-add_library( datafed-protobuf ${ProtoSourcesCpp} )
+add_library( protobuf-target ${ProtoFiles} )
+
+# Create the .cpp and .hpp files
+protobuf_generate(
+  TARGET protobuf-target
+  LANGUAGE cpp 
+  IMPORT_DIRS "${DataFed_SOURCE_DIR}/common/proto/common"
+  OUT_VAR protobuf-generated-files
+)
+
+# make sure that datafed-protobuf is dependent on the cpp files when it compiles
+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 -lprotobuf )
+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
new file mode 100644
index 000000000..5a1e61143
--- /dev/null
+++ b/compose/README.md
@@ -0,0 +1,299 @@
+# Compose Dev environment
+
+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.
+
+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.
+
+For the redirect url. if you are running the core services on your laptop you
+can use:
+
+https://localhost/ui/authn
+
+When registering a Globus Application for DataFed the domain used in
+the redirect must be consistent with the .env file for the "DATAFED_DOMAIN"
+variable.
+
+E.g. 
+
+redirect: https://localhost/ui/authn
+DATAFED_DOMAIN: localhost
+
+If a public IP is assigned
+
+redirect: https://192.83.46.54/ui/authn
+DATAFED_DOMAIN: 192.83.46.54
+
+If a domain is assigned such as "awesome_datafed.com"
+
+redirect: https://awesome_datafed.com/ui/authn
+DATAFED_DOMAIN: awesome_datafed.com
+
+### 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.
+
+### 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.
+
+You need to have installed, globus_sdk for python, as well as the
+globus-connect-server54 package. See instructions for installing that here.
+https://docs.globus.org/globus-connect-server/v5/
+
+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".
+The following three things need to be done before the generate_globus_files.sh
+should be run.
+
+1. You will need to have globus-connect-server54 installed to do this step.
+2. You will also need the Globus python developer kit globus_sdk.
+3. In addition, your user account will need admin rights on the Globus subscription group.
+
+#### Step 1 
+On Ubuntu this can be done with.
+
+```bash
+sudo apt-get install globus-connect-server54
+```
+
+#### Step 2 
+
+```bash
+pip install globus_sdk
+```
+
+Finally, we can generate the globus files by running the script.
+
+```bash
+./generate_globus_files.sh
+```
+
+You will be required to paste a url into a browser and provide a code back in
+the terminal.
+
+### 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
+
+WARNING: Before running compose file make sure that port 80 is open you can do
+this using the command
+
+```bash
+sudo netstat -tlupn | grep ':80'
+```
+
+It is very likely that when we installed globus-connect-server54 with apt it 
+started apache2 and started it running. You should disable it on the host
+before launching the compose instance.
+
+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.
+
+NOTE If you get an error from the repo server along the lines of
+
+```
+ERROR /datafed/source/repository/server/main.cpp:main:154 { "thread_name":
+"repo_server", "message": "Exception: Could not open file:
+/opt/datafed/keys/datafed-core-key.pub" }
+```
+
+it is possible that the DATAFED_DOMAIN name field is incorrect your .env file.
+
+### 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
new file mode 100755
index 000000000..7b155465f
--- /dev/null
+++ b/compose/build_images_for_compose.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+set -euf -o pipefail
+
+SCRIPT=$(realpath "$0")
+SOURCE=$(dirname "$SCRIPT")
+PROJECT_ROOT=$(realpath "${SOURCE}/../")
+
+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}/core/docker/Dockerfile" \
+  --build-arg DEPENDENCIES="datafed-dependencies" \
+  --build-arg RUNTIME="datafed-runtime" \
+  "${PROJECT_ROOT}" \
+  -t datafed-core:latest
+docker build -f \
+  "${PROJECT_ROOT}/web/docker/Dockerfile" \
+  --build-arg DEPENDENCIES="datafed-dependencies" \
+  --build-arg RUNTIME="datafed-runtime" \
+  "${PROJECT_ROOT}" \
+  -t datafed-web: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
new file mode 100644
index 000000000..c7bb2e1da
--- /dev/null
+++ b/compose/compose_core.yml
@@ -0,0 +1,102 @@
+version: '3.9'
+
+
+services:
+
+  datafed-web:
+    depends_on: ["datafed-core"]
+    environment:
+      DATAFED_GLOBUS_APP_SECRET: "${DATAFED_GLOBUS_APP_SECRET}"
+      DATAFED_GLOBUS_APP_ID: "${DATAFED_GLOBUS_APP_ID}"
+      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_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}"
+    image: datafed-web:latest
+    ports:
+      - 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
+
+  datafed-core:
+    image: datafed-core:latest
+    depends_on:
+      datafed-foxx:
+        condition: service_healthy
+    environment:
+      DATAFED_GLOBUS_APP_SECRET: "${DATAFED_GLOBUS_APP_SECRET}"
+      DATAFED_GLOBUS_APP_ID: "${DATAFED_GLOBUS_APP_ID}"
+      DATAFED_ZEROMQ_SESSION_SECRET: "${DATAFED_ZEROMQ_SESSION_SECRET}"
+      DATAFED_ZEROMQ_SYSTEM_SECRET: "${DATAFED_ZEROMQ_SYSTEM_SECRET}"
+      DATAFED_DOMAIN: "${DATAFED_DOMAIN}"
+      DATAFED_WEB_CERT_PATH: "${DATAFED_WEB_CERT_PATH}"
+      DATAFED_WEB_KEY_PATH: "${DATAFED_WEB_KEY_PATH}"
+      DATAFED_DEFAULT_LOG_PATH: "${DATAFED_CONTAINER_LOG_PATH}"
+      UID: "${DATAFED_UID}"
+      DATAFED_DATABASE_PASSWORD: "${DATAFED_DATABASE_PASSWORD}"
+      DATAFED_DATABASE_IP_ADDRESS: "${DATAFED_DATABASE_IP_ADDRESS}"
+      DATAFED_DATABASE_IP_ADDRESS_PORT: "${DATAFED_DATABASE_IP_ADDRESS}:${DATAFED_DATABASE_PORT}"
+    ports:
+      - 7513 # Communication web server
+      - 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-internal
+
+  datafed-foxx:
+    image: datafed-foxx:latest
+    depends_on: ["arango"]
+    environment:
+      DATAFED_ZEROMQ_SYSTEM_SECRET: "${DATAFED_ZEROMQ_SYSTEM_SECRET}"
+      DATAFED_DOMAIN: "${DATAFED_DOMAIN}"
+      DATAFED_DEFAULT_LOG_PATH: "${DATAFED_CONTAINER_LOG_PATH}"
+      UID: "${DATAFED_UID}"
+      DATAFED_DATABASE_PASSWORD: "${DATAFED_DATABASE_PASSWORD}"
+      DATAFED_DATABASE_IP_ADDRESS: "${DATAFED_DATABASE_IP_ADDRESS}"
+      DATAFED_DATABASE_HOST: "arango"
+    healthcheck:
+      test: ["CMD", "/bin/bash", "-c", "[ -f /tmp/.foxx_is_installed ]"]
+      interval: 10s
+      timeout: 5s
+      retries: 20
+    volumes:
+      - foxx_tmp:/tmp 
+    networks:
+      - datafed-internal
+
+  arango:
+    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:
+  foxx_tmp:
+  arango_db:
+
+networks:
+  datafed-internal:
+    driver: bridge
+  
diff --git a/compose/compose_repo.yml b/compose/compose_repo.yml
new file mode 100644
index 000000000..4d535bb41
--- /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: "/mnt/datafed"
+      UID: "${DATAFED_UID}"
+      HOST_HOSTNAME: "localhost"
+    image: datafed-repo:latest
+    volumes:
+      - ./keys:/opt/datafed/keys
+      - ./logs:${DATAFED_CONTAINER_LOG_PATH}
+      - ${DATAFED_HOST_COLLECTION_MOUNT}:/mnt/datafed
+    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: "/mnt/datafed"
+      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}:/mnt/datafed
diff --git a/compose/generate_env.sh b/compose/generate_env.sh
new file mode 100755
index 000000000..8beae8fe6
--- /dev/null
+++ b/compose/generate_env.sh
@@ -0,0 +1,212 @@
+#!/bin/bash
+SCRIPT=$(realpath "$0")
+SOURCE=$(dirname "$SCRIPT")
+PROJECT_ROOT=$(realpath "${SOURCE}/..")
+
+if [ -f ".env" ]
+then
+  echo ".env already exist! Will not overwrite!"
+  exit 1
+fi
+
+local_DATAFED_WEB_KEY_DIR="${PROJECT_ROOT}/compose/keys"
+if [ ! -d "$local_DATAFED_WEB_KEY_DIR" ]
+then
+  mkdir -p "$local_DATAFED_WEB_KEY_DIR"
+fi
+
+if [ -z "${DATAFED_COMPOSE_DOMAIN}" ]
+then
+  local_DATAFED_COMPOSE_DOMAIN="localhost"
+else
+  local_DATAFED_COMPOSE_DOMAIN=$(printenv DATAFED_COMPOSE_DOMAIN)
+fi
+
+local_DATAFED_WEB_CERT_NAME="cert.crt"
+local_DATAFED_WEB_KEY_NAME="cert.key"
+
+local_DATAFED_WEB_CERT_PATH="${local_DATAFED_WEB_KEY_DIR}/${local_DATAFED_WEB_CERT_NAME}"
+local_DATAFED_WEB_CSR_PATH="${local_DATAFED_WEB_KEY_DIR}/cert.csr"
+local_DATAFED_WEB_KEY_PATH="${local_DATAFED_WEB_KEY_DIR}/${local_DATAFED_WEB_KEY_NAME}"
+
+if [ ! -e "$local_DATAFED_WEB_CERT_PATH" ] || [ ! -e "$local_DATAFED_WEB_KEY_PATH" ]
+then
+  if [ -e "$local_DATAFED_WEB_CERT_PATH" ]
+  then
+    rm "${local_DATAFED_WEB_CERT_PATH}"
+  fi
+  if [ -e "$local_DATAFED_WEB_KEY_PATH" ]
+  then
+    rm "${local_DATAFED_WEB_KEY_PATH}"
+  fi
+  if [ -e "$local_DATAFED_WEB_CSR_PATH" ]
+  then
+    rm "${local_DATAFED_WEB_CSR_PATH}"
+  fi
+  openssl genrsa -out "$local_DATAFED_WEB_KEY_PATH" 2048
+  openssl req -new -key "$local_DATAFED_WEB_KEY_PATH" \
+    -out "${local_DATAFED_WEB_CSR_PATH}" \
+    -subj "/C=US/ST=TN/L=Oak Ridge/O=ORNL/OU=DLT/CN=${local_DATAFED_COMPOSE_DOMAIN}"
+  openssl x509 -req -days 3650 \
+     -in "${local_DATAFED_WEB_CSR_PATH}" \
+     -signkey "$local_DATAFED_WEB_KEY_PATH" \
+     -out "$local_DATAFED_WEB_CERT_PATH"
+fi
+
+if [ -z "${DATAFED_COMPOSE_USER89_PASSWORD}" ]
+then
+  local_DATAFED_COMPOSE_USER89_PASSWORD="" # For End to end testing
+else
+  local_DATAFED_COMPOSE_USER89_PASSWORD=$(printenv DATAFED_COMPOSE_USER89_PASSWORD)
+fi
+if [ -z "${DATAFED_COMPOSE_REPO_FORM_PATH}" ]
+then
+  local_DATAFED_COMPOSE_REPO_FORM_PATH="" # Where the repo form is located also needed for testing
+else
+  local_DATAFED_COMPOSE_REPO_FORM_PATH=$(printenv DATAFED_COMPOSE_REPO_FORM_PATH)
+fi
+if [ -z "${DATAFED_COMPOSE_GLOBUS_APP_SECRET}" ]
+then
+  local_DATAFED_COMPOSE_GLOBUS_APP_SECRET=""
+else
+  local_DATAFED_COMPOSE_GLOBUS_APP_SECRET=$(printenv DATAFED_COMPOSE_GLOBUS_APP_SECRET)
+fi
+if [ -z "${DATAFED_COMPOSE_GLOBUS_APP_ID}" ]
+then
+  local_DATAFED_COMPOSE_GLOBUS_APP_ID=""
+else
+  local_DATAFED_COMPOSE_GLOBUS_APP_ID=$(printenv DATAFED_COMPOSE_GLOBUS_APP_ID)
+fi
+if [ -z "${DATAFED_GLOBUS_KEY_DIR}" ]
+then
+  local_DATAFED_GLOBUS_KEY_DIR="${PROJECT_ROOT}/compose/globus"
+else
+  local_DATAFED_GLOBUS_KEY_DIR=$(printenv DATAFED_GLOBUS_KEY_DIR)
+fi
+if [ -z "${DATAFED_COMPOSE_ZEROMQ_SESSION_SECRET}" ]
+then
+  local_DATAFED_COMPOSE_ZEROMQ_SESSION_SECRET=""
+else
+  local_DATAFED_COMPOSE_ZEROMQ_SESSION_SECRET=$(printenv DATAFED_COMPOSE_ZEROMQ_SESSION_SECRET)
+fi
+if [ -z "${DATAFED_COMPOSE_ZEROMQ_SYSTEM_SECRET}" ]
+then
+  local_DATAFED_COMPOSE_ZEROMQ_SYSTEM_SECRET=""
+else
+  local_DATAFED_COMPOSE_ZEROMQ_SYSTEM_SECRET=$(printenv DATAFED_COMPOSE_ZEROMQ_SYSTEM_SECRET)
+fi
+if [ -z "${DATAFED_COMPOSE_HTTPS_SERVER_PORT}" ]
+then
+  local_DATAFED_COMPOSE_HTTPS_SERVER_PORT="443"
+else
+  local_DATAFED_COMPOSE_HTTPS_SERVER_PORT=$(printenv DATAFED_COMPOSE_HTTPS_SERVER_PORT)
+fi
+if [ -z "${DATAFED_COMPOSE_CONTAINER_LOG_PATH}" ]
+then
+  local_DATAFED_COMPOSE_CONTAINER_LOG_PATH="/opt/datafed/logs"
+else
+  local_DATAFED_COMPOSE_CONTAINER_LOG_PATH=$(printenv DATAFED_COMPOSE_CONTAINER_LOG_PATH)
+fi
+if [ -z "${DATAFED_COMPOSE_DATABASE_PASSWORD}" ]
+then
+  local_DATAFED_COMPOSE_DATABASE_PASSWORD="butterscotch"
+else
+  local_DATAFED_COMPOSE_DATABASE_PASSWORD=$(printenv DATAFED_COMPOSE_DATABASE_PASSWORD)
+fi
+
+if [ -z "${DATAFED_COMPOSE_DATABASE_IP_ADDRESS}" ]
+then
+  local_DATAFED_COMPOSE_DATABASE_IP_ADDRESS="http://arango"
+else
+  local_DATAFED_COMPOSE_DATABASE_IP_ADDRESS=$(printenv DATAFED_COMPOSE_DATABASE_IP_ADDRESS)
+fi
+
+if [ -z "${DATAFED_COMPOSE_DATABASE_PORT}" ]
+then
+  local_DATAFED_COMPOSE_DATABASE_PORT="8529"
+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_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/CMakeLists.txt b/core/database/CMakeLists.txt
index 47e8fd332..34527dabd 100644
--- a/core/database/CMakeLists.txt
+++ b/core/database/CMakeLists.txt
@@ -10,9 +10,7 @@ configure_file(
   "${CMAKE_CURRENT_SOURCE_DIR}/foxx/manifest.json"
   @ONLY)
 
-message("ENABLE FOX TEST ${ENABLE_FOXX_TESTS}")
 if( ENABLE_FOXX_TESTS )
-  message("Adding tests")
   add_test(NAME foxx_setup COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_setup.sh")
   add_test(NAME foxx_teardown COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_teardown.sh")
   add_test(NAME foxx_version COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_foxx.sh" -t "version")
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 79b0120f9..bab738f2a 100644
--- a/core/docker/Dockerfile
+++ b/core/docker/Dockerfile
@@ -7,19 +7,21 @@ 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 core-build
 
+SHELL ["/bin/bash", "-c"]
+
 ARG DATAFED_DIR
 ARG BUILD_DIR
 ARG DATAFED_INSTALL_PATH
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH
 
+ENV DATAFED_INSTALL_PATH="${DATAFED_INSTALL_PATH}"
 # For communicating with repo server
 EXPOSE 7512
 # For listening to web server
@@ -36,7 +38,7 @@ COPY ./core/docker/entrypoint.sh       ${BUILD_DIR}/core/docker/
 COPY ./core/server                     ${BUILD_DIR}/core/server
 
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh && \
-	cmake -S. -B build						\
+	${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake -S. -B build						\
 		-DBUILD_REPO_SERVER=False		\
 		-DBUILD_AUTHZ=False					\
 		-DBUILD_CORE_SERVER=True		\
@@ -44,11 +46,13 @@ RUN ${BUILD_DIR}/scripts/generate_datafed.sh && \
 		-DBUILD_DOCS=False					\
 		-DBUILD_PYTHON_CLIENT=False	\
 		-DBUILD_FOXX=False
-RUN cmake --build build -j 8
-RUN cmake --build build --target install
+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 core
 
+SHELL ["/bin/bash", "-c"]
+
 ARG DATAFED_DIR
 ARG DATAFED_INSTALL_PATH
 ARG BUILD_DIR
@@ -56,30 +60,28 @@ ARG LIB_DIR
 
 # 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_DIR="$DATAFED_DIR"
 ENV            BUILD_DIR="$BUILD_DIR"
 ENV              LIB_DIR="$LIB_DIR"
-
-# copy necessary shared libraries
-COPY --from=core-build /libraries/libprotobuf.so           /libraries/libprotobuf.so
-COPY --from=core-build /libraries/libzmq.so                /libraries/libzmq.so
-COPY --from=core-build /libraries/libsodium.so             /libraries/libsodium.so
-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
 
 USER datafed
 
-COPY --chown=datafed:root ./scripts/generate_datafed.sh     ${DATAFED_DIR}/scripts/generate_datafed.sh
-COPY --chown=datafed:root ./scripts/generate_core_config.sh ${DATAFED_DIR}/scripts/generate_core_config.sh
-COPY --chown=datafed:root ./scripts/install_core.sh         ${DATAFED_DIR}/scripts/install_core.sh
-COPY --chown=datafed:root ./cmake/Version.cmake             ${DATAFED_DIR}/cmake/Version.cmake
-COPY --from=core-build --chown=datafed:root ${BUILD_DIR}/core/docker/entrypoint.sh    ${BUILD_DIR}/core/entrypoint.sh
+COPY --chown=datafed:root ./scripts/generate_datafed.sh     ${BUILD_DIR}/scripts/generate_datafed.sh
+COPY --chown=datafed:root ./scripts/generate_core_config.sh ${BUILD_DIR}/scripts/generate_core_config.sh
+COPY --chown=datafed:root ./scripts/install_core.sh         ${BUILD_DIR}/scripts/install_core.sh
+COPY --chown=datafed:root ./cmake/Version.cmake             ${BUILD_DIR}/cmake/Version.cmake
+COPY --from=core-build --chown=datafed:root ${BUILD_DIR}/core/docker/entrypoint.sh    ${BUILD_DIR}/core/docker/entrypoint.sh
 COPY --from=core-build --chown=datafed:root ${DATAFED_INSTALL_PATH}/core/datafed-core ${DATAFED_INSTALL_PATH}/core/datafed-core
 
-ENTRYPOINT ["${BUILD_DIR}/core/entrypoint.sh"]
-CMD ["${DATAFED_INSTALL_PATH}/core/datafed-core","--cfg","${DATAFED_INSTALL_PATH}/core/datafed-core.cfg"]
+USER root
+
+RUN chown -R datafed:root /datafed
+RUN chown -R datafed:root /opt
+
+# 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 9f3eed307..ae790e3b7 100644
--- a/core/docker/Dockerfile.core-base.ubuntu
+++ b/core/docker/Dockerfile.core-base.ubuntu
@@ -2,7 +2,14 @@ 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}"
+
+# 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}
@@ -11,6 +18,9 @@ COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/dependency_install_functions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/dependency_versions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/install_core_dependencies.sh ${BUILD_DIR}/scripts/
+COPY ./scripts/utils.sh ${BUILD_DIR}/scripts/
+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_DIR}/scripts/generate_datafed.sh
diff --git a/core/docker/Dockerfile.core.ubuntu b/core/docker/Dockerfile.core.ubuntu
index 60a8891b5..5edaccf7b 100644
--- a/core/docker/Dockerfile.core.ubuntu
+++ b/core/docker/Dockerfile.core.ubuntu
@@ -1,4 +1,6 @@
-FROM camden.ornl.gov/datafed/core-base:latest as build
+ARG BASE_CORE_IMAGE="camden.ornl.gov/datafed/core-base:latest"
+
+FROM "$BASE_CORE_IMAGE" as build
 
 ARG DATAFED_DIR="/datafed"
 ARG BUILD_DIR="/datafed/source"
@@ -7,11 +9,13 @@ ARG DATAFED_INSTALL_PATH="/datafed/install"
 # 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/core"
+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
@@ -43,14 +47,16 @@ 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
 
-WORKDIR ${DATAFED_INSTALL_PATHl}/core
+USER root
 
 ENTRYPOINT ["/datafed/source/core/docker/entrypoint.sh"]
 
diff --git a/core/docker/entrypoint.sh b/core/docker/entrypoint.sh
index 0b45898f2..efdd65c2c 100755
--- a/core/docker/entrypoint.sh
+++ b/core/docker/entrypoint.sh
@@ -2,16 +2,30 @@
 
 set -euf -o pipefail
 
+if [ -n "$UID" ]; then
+    usermod -u $UID datafed
+fi
+
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
-PROJECT_ROOT=$(realpath ${SOURCE}/../..)
+PROJECT_ROOT=$(realpath "${SOURCE}/../..")
 
-${PROJECT_ROOT}/scripts/generate_datafed.sh
-${PROJECT_ROOT}/scripts/generate_core_config.sh
-${PROJECT_ROOT}/scripts/install_core.sh
+env
+
+"${PROJECT_ROOT}/scripts/generate_datafed.sh"
+"${PROJECT_ROOT}/scripts/generate_core_config.sh"
+"${PROJECT_ROOT}/scripts/install_core.sh"
 
 log_path="$DATAFED_DEFAULT_LOG_PATH"
 
+if [ ! -d "${log_path}" ]
+then
+  su -c "mkdir -p ${log_path}" datafed
+fi
+
+echo "Number of arguments is $#"
+echo "arguments are $@"
+
 if [ "$#" -eq 0 ]; then
   echo "No arguments were passed, running bash"
   exec "bash"
@@ -22,9 +36,13 @@ datafed_core_exec=$(basename "$1")
 if [ "${datafed_core_exec}" = "datafed-core" ]
 then
   # Send output to log file
-  "$@"  2>&1 | tee "$log_path/datafed-core.log"
+  # For this to work all commands must be passed in as a single string
+  su datafed -c '"$@"' -- argv0 "$@" 2>&1 | su datafed -c "tee $log_path/datafed-core.log"
 else
   echo "Not sending output to datafed-core.log"
   # If not do not by default send to log file
-  exec "$@"
+  su datafed -c '"$@"' -- argv0 "$@"
 fi
+
+echo "Give a few minutes to debug the problem"
+sleep 10000
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 e442ad328..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 -lprotobuf
-	Threads::Threads ${CURL_LIBRARIES} ${Boost_LIBRARIES} PkgConfig::PkgConfig_ZMQ 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 9ad2356ca..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;
 
-  google::protobuf::util::Status 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;
 
-    google::protobuf::util::Status 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"));
 
-  google::protobuf::util::Status 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/index.rst b/doc_source/index.rst
new file mode 100644
index 000000000..7cf70b455
--- /dev/null
+++ b/doc_source/index.rst
@@ -0,0 +1,87 @@
+.. DataFed documentation master file, created by
+   sphinx-quickstart on Mon Dec 14 13:53:32 2020.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+======================================
+DataFed - A Scientific Data Federation
+======================================
+
+DataFed is a *federated* and *scalable* scientific data management and collaboration system that
+addresses the critical need for holistic and FAIR-principled "big data" handling within, and across,
+scientific domains and facilities with the goal of enhancing the *productivity* and *reproducibility*
+of data-oriented scientific research. DataFed supports the early lifecycle stages of "working"
+scientific data and serves as a tool to ease the burden associated with capturing, organizing, and
+sharing potentially large volumes of heterogeneous scientific data. DataFed provides an environment
+in which scientific data can be precisely controlled and refined in preparation for eventual data
+publishing.
+
+
+DataFed Documentation
+=====================
+
+This site contains all user and administrative documentation for the DataFed system - including
+client interfaces and application programming interfaces. The system introduction and
+overview documents introduce DataFed-specific terminology and key concepts that are references
+by subsequent documents on this site.
+
+.. toctree::
+   :maxdepth: 2
+   :caption: About DataFed
+
+   system/introduction
+   system/overview
+   system/usecases
+   system/papers
+   system/getting_started
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Web Portal
+
+   user/web/portal
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Client Package
+
+   user/client/install
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Command Line Interface
+
+   user/cli/guide
+   user/cli/reference
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Python Interface
+
+   user/python/high_level_guide
+   user/python/notebooks
+   autoapi/index.rst
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Administration
+
+   admin/general
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Development
+
+   dev/project
+   dev/environment_setup
+   dev/design
+   dev/release
+   dev/roadmap
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
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/doc_source/source/dev/environment_setup.rst b/doc_source/source/dev/environment_setup.rst
new file mode 100644
index 000000000..84e866935
--- /dev/null
+++ b/doc_source/source/dev/environment_setup.rst
@@ -0,0 +1,34 @@
+=============================
+Development Environment Setup
+=============================
+
+Globus Setup
+============
+
+#. Log in to `Globus <https://app.globus.org>`_.
+#. Navigate to Settings > Developers > Add Project.
+#. Select an accurate name and contact email for your project.
+#. Navigate to Add an App > Advanced Registration.
+#. Choose an accurate name for your app, and in the redirects field add the single redirect ``https://localhost/ui/authn``
+#. Click Add Client Secret and choose a descriptive name.
+#. Take note of the Secret that it generates and the Client UUID of the Globus application and remember them for future steps.
+
+Compose Setup
+=============
+
+#. Download the `DataFed Github Repository <https://github.com/ORNL/DataFed>`_.
+#. Checkout to the ``devel`` branch.
+#. Navigate into the ``compose`` folder and run ``./build_images_for_compose.sh``
+
+   * Note: If you are building the images for the first time, it will take a while, however subsequent builds will use cached dependencies to accelerate the process.
+#. Run the ``./generate_env.sh`` script, which will generate a ``.env`` file at this location and a self-signed certificate for the web interface to use.
+#. Edit the ``.env`` file and set values for the following environment variables:
+
+   * ``DATAFED_GLOBUS_APP_SECRET``: The app secret from the Globus setup
+   * ``DATAFED_GLOBUS_APP_ID``: The app UUID from the Globus setup
+   * ``DATAFED_ZEROMQ_SESSION_SECRET``: This can be any string
+   * ``DATAFED_ZEROMQ_SYSTEM_SECRET``: This can be any string
+#. Edit any other values in the ``.env`` file to fit your development needs.
+#. Finally, run ``docker compose -f ./compose_core.yml up``, and after a few minutes the DataFed containers should be started and the web application should be accessible from https://localhost:443/
+
+   * Note: Since the web interface is using a self-signed certificate your browser may not trust it, however, in this case it is safe to bypass this protection to visit the web app.
diff --git a/doc_source/source/index.rst b/doc_source/source/index.rst
index 9df859a4d..7cf70b455 100644
--- a/doc_source/source/index.rst
+++ b/doc_source/source/index.rst
@@ -73,6 +73,7 @@ by subsequent documents on this site.
    :caption: Development
 
    dev/project
+   dev/environment_setup
    dev/design
    dev/release
    dev/roadmap
diff --git a/dockerfiles/Dockerfile.base b/docker/Dockerfile.base
similarity index 100%
rename from dockerfiles/Dockerfile.base
rename to docker/Dockerfile.base
diff --git a/dockerfiles/Dockerfile.core b/docker/Dockerfile.core
similarity index 100%
rename from dockerfiles/Dockerfile.core
rename to docker/Dockerfile.core
diff --git a/dockerfiles/Dockerfile.datafed-centos7 b/docker/Dockerfile.datafed-centos7
similarity index 100%
rename from dockerfiles/Dockerfile.datafed-centos7
rename to docker/Dockerfile.datafed-centos7
diff --git a/dockerfiles/dependencies.Dockerfile b/docker/Dockerfile.dependencies
similarity index 61%
rename from dockerfiles/dependencies.Dockerfile
rename to docker/Dockerfile.dependencies
index 602ebd524..2120c96cc 100644
--- a/dockerfiles/dependencies.Dockerfile
+++ b/docker/Dockerfile.dependencies
@@ -1,15 +1,13 @@
 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              LIB_DIR="/opt/datafed/dependencies/lib"
 
 FROM ubuntu:focal
 
-ARG NVM_DIR
+SHELL ["/bin/bash", "-c"]
 ARG DATAFED_DIR
 ARG BUILD_DIR
 ARG DATAFED_INSTALL_PATH
@@ -18,6 +16,7 @@ ARG LIB_DIR
 
 ENV BUILD_DIR="${BUILD_DIR}"
 ENV DATAFED_DIR="${DATAFED_DIR}"
+ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
 ENV LIB_DIR="${LIB_DIR}"
 
 RUN mkdir -p ${BUILD_DIR}
@@ -32,15 +31,34 @@ WORKDIR ${BUILD_DIR}
 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/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/utils.sh ${BUILD_DIR}/scripts/utils.sh
+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
 
 RUN echo "#!/bin/bash\n\$@" > /usr/bin/sudo && chmod +x /usr/bin/sudo
 
 # run build scripts
-RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC ${BUILD_DIR}/scripts/install_dependencies.sh
+RUN ${BUILD_DIR}/scripts/generate_datafed.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
@@ -56,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
new file mode 100644
index 000000000..c1a7726c8
--- /dev/null
+++ b/docker/Dockerfile.foxx
@@ -0,0 +1,63 @@
+# 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 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 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 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 DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_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}
+COPY ./scripts/dependency_versions.sh  ${BUILD_DIR}/scripts/
+COPY ./scripts/generate_datafed.sh     ${BUILD_DIR}/scripts/
+COPY ./cmake                           ${BUILD_DIR}/cmake
+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/
+
+# 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 ${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 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 ${BUILD_DIR}
+
+ENTRYPOINT ${BUILD_DIR}/docker/entrypoint_foxx.sh
diff --git a/dockerfiles/Dockerfile.kickstart-centos7 b/docker/Dockerfile.kickstart-centos7
similarity index 100%
rename from dockerfiles/Dockerfile.kickstart-centos7
rename to docker/Dockerfile.kickstart-centos7
diff --git a/dockerfiles/runtime.Dockerfile b/docker/Dockerfile.runtime
similarity index 76%
rename from dockerfiles/runtime.Dockerfile
rename to docker/Dockerfile.runtime
index a372c683d..4d2a6861e 100644
--- a/dockerfiles/runtime.Dockerfile
+++ b/docker/Dockerfile.runtime
@@ -2,15 +2,14 @@ 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"
 
 FROM ubuntu:focal
 
+SHELL ["/bin/bash", "-c"]
 ARG DATAFED_DIR
 ARG DATAFED_INSTALL_PATH
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH
 ARG BUILD_DIR
 
 ENV BUILD_DIR="${BUILD_DIR}"
@@ -33,5 +32,4 @@ RUN chown -R datafed:root ${DATAFED_DIR}
 WORKDIR ${DATAFED_DIR}
 
 RUN apt update
-RUN apt install -y grep libcurl4
-# ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib:/usr/local/lib"
+RUN apt install -y grep libcurl4 wget
diff --git a/dockerfiles/Dockerfile.web b/docker/Dockerfile.web
similarity index 100%
rename from dockerfiles/Dockerfile.web
rename to docker/Dockerfile.web
diff --git a/dockerfiles/README.md b/docker/README.md
similarity index 100%
rename from dockerfiles/README.md
rename to docker/README.md
diff --git a/docker/entrypoint_foxx.sh b/docker/entrypoint_foxx.sh
new file mode 100755
index 000000000..ed8387eb9
--- /dev/null
+++ b/docker/entrypoint_foxx.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+set -euf -o pipefail
+
+# 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}/../")
+
+install_flag="/tmp/.foxx_is_installed"
+if [ ! -f "$install_flag" ]
+then
+  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					\
+    -DBUILD_CORE_SERVER=False		\
+    -DBUILD_WEB_SERVER=False		\
+    -DBUILD_DOCS=False					\
+    -DBUILD_PYTHON_CLIENT=False	\
+    -DBUILD_FOXX=True           \
+    -DINSTALL_FOXX=True
+
+
+  "${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
+  "${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake" --build build --target install
+  
+  touch "$install_flag"
+fi
+
+sleep 1000
+
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..1d42a0df7 160000
--- a/external/globus-connect-server-deploy
+++ b/external/globus-connect-server-deploy
@@ -1,1 +1,1 @@
-Subproject commit 20132721ab9413078dc721a8d9e1d5a04a7e1c3f
+Subproject commit 1d42a0df7cd8389718205b038621677ebb575306
diff --git a/external/protobuf b/external/protobuf
new file mode 160000
index 000000000..a9b006bdd
--- /dev/null
+++ b/external/protobuf
@@ -0,0 +1,1 @@
+Subproject commit a9b006bddd52e289029f16aa77b77e8e0033d9ee
diff --git a/python/datafed_pkg/datafed/CMakeLists.txt b/python/datafed_pkg/datafed/CMakeLists.txt
index b50898682..9e066deea 100644
--- a/python/datafed_pkg/datafed/CMakeLists.txt
+++ b/python/datafed_pkg/datafed/CMakeLists.txt
@@ -7,25 +7,36 @@ foreach(file ${SrcFiles})
 endforeach()
 
 # Collect top-level proto files as dependencies
-file( GLOB ProtoFiles ${CMAKE_SOURCE_DIR}/common/proto/common/*.proto )
+file( GLOB ProtoFiles ${DataFed_SOURCE_DIR}/common/proto/common/*.proto )
 
-# Create proto target and rules to generate py proto files
-PROTOBUF_GENERATE_PYTHON( ProtoFilesPy ${ProtoFiles} )
+# OBJECT - is needed because we don't want to compile to a binary
+# because we are dealing with python
+add_library(protobuf-target-py OBJECT ${ProtoFiles})
+protobuf_generate(
+    LANGUAGE python
+    TARGET protobuf-target-py
+    IMPORT_DIRS "${DataFed_SOURCE_DIR}/common/proto/common"
+    OUT_VAR protobuf-generated-files-py
+    PROTOC_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}"
+  )
 
-add_custom_target( pydatafed_src DEPENDS ${ProtoFilesPy} )
+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:' ${ProtoFilesPy}
-    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 sed -i -r 's:^import.*_pb2:from . \\0:' ${protobuf-generated-files-py}
+    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/datafed_pkg/datafed/CommandLib.py b/python/datafed_pkg/datafed/CommandLib.py
index 3ee98befe..a4016e52b 100644
--- a/python/datafed_pkg/datafed/CommandLib.py
+++ b/python/datafed_pkg/datafed/CommandLib.py
@@ -1660,6 +1660,7 @@ def _buildSearchRequest(
         offset=0,
         count=20,
     ):
+
         if coll_mode and (schema is not None or meta is not None or meta_err):
             raise Exception(
                 "Cannot specify metadata terms when searching for collection."
diff --git a/python/datafed_pkg/requirements.txt b/python/datafed_pkg/requirements.txt
index 9735f7929..c928d7e3c 100644
--- a/python/datafed_pkg/requirements.txt
+++ b/python/datafed_pkg/requirements.txt
@@ -1,4 +1,4 @@
-protobuf>=3,<=3.20
+protobuf>=4.21.1
 pyzmq>=16
 wget>=3
 click>=7
diff --git a/repository/docker/Dockerfile.gcs-base.ubuntu b/python/docker/Dockerfile.python-client-base.ubuntu
similarity index 53%
rename from repository/docker/Dockerfile.gcs-base.ubuntu
rename to python/docker/Dockerfile.python-client-base.ubuntu
index 3456a0906..53102ac43 100644
--- a/repository/docker/Dockerfile.gcs-base.ubuntu
+++ b/python/docker/Dockerfile.python-client-base.ubuntu
@@ -2,15 +2,20 @@ 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}
 
 WORKDIR ${BUILD_DIR}
 
-COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/
+COPY ./scripts/dependency_install_functions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/dependency_versions.sh ${BUILD_DIR}/scripts/
-COPY ./scripts/install_gcs.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_gcs.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
new file mode 100644
index 000000000..d8b7151a6
--- /dev/null
+++ b/python/docker/Dockerfile.python-client.ubuntu
@@ -0,0 +1,55 @@
+ARG BASE_PYTHON_CLIENT_IMAGE="camden.ornl.gov/datafed/python-client-base:latest"
+
+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}
+RUN mkdir -p ${DATAFED_INSTALL_PATH}/keys
+
+# Create datafed user, prefer more secure login options than password
+# Recommended to mount ssh public key on run
+RUN adduser --disabled-password --gecos "" datafed
+
+WORKDIR ${BUILD_DIR}
+
+COPY ./common ${BUILD_DIR}/common
+COPY ./CMakeLists.txt ${BUILD_DIR}
+COPY ./scripts/dependency_versions.sh ${BUILD_DIR}/scripts/
+COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/
+COPY ./cmake ${BUILD_DIR}/cmake
+COPY ./python ${BUILD_DIR}/python
+
+# All files should be owned by the datafed user
+RUN chown -R datafed:datafed ${DATAFED_DIR}
+
+USER datafed
+
+RUN ${BUILD_DIR}/scripts/generate_datafed.sh &&\
+ ${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 &&\
+ ${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
+
+# Install datafed client
+RUN python3 -m pip install .
+
+WORKDIR /home/datafed
+
+ENTRYPOINT ["/datafed/source/python/docker/entrypoint.sh"]
+
diff --git a/python/docker/entrypoint.sh b/python/docker/entrypoint.sh
new file mode 100755
index 000000000..f9573344f
--- /dev/null
+++ b/python/docker/entrypoint.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+set -euf -o pipefail
+
+SCRIPT=$(realpath "$0")
+SOURCE=$(dirname "$SCRIPT")
+
+# Entry point file expects that the directory where the DataFed source file 
+# is passed in as the first argument
+
+echo "SOURCE BUILD DIR $BUILD_DIR"
+
+# Here we will rerun datafed configuration script to create a new set of
+# default variables that are useful for setting up the DataFed Python client
+# ini file
+
+"${BUILD_DIR}/scripts/generate_datafed.sh"
+source "${BUILD_DIR}/config/datafed.sh"
+
+mkdir -p "/home/datafed/.datafed"
+
+# At this point we will create an ini file
+cat << EOF > "/home/datafed/.datafed/datafed-client.ini"
+[server]
+host = ${DATAFED_DOMAIN}
+port = ${DATAFED_SERVER_PORT}
+config_dir = /home/datafed/.datafed
+
+[client]
+config_dir = /home/datafed/.datafed
+
+EOF
+
+if [ "$#" -eq 0 ]; then
+  echo "No arguments were passed, running bash"
+  exec "/home/datafed/.local/bin/datafed --cfg  /home/datafed/.datafed/datafed-client.ini"
+fi
+
+"$@"
+
diff --git a/repository/docker/Dockerfile b/repository/docker/Dockerfile
index 73a73ff47..74a3d5249 100644
--- a/repository/docker/Dockerfile
+++ b/repository/docker/Dockerfile
@@ -7,19 +7,20 @@ 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 repo-build
 
+SHELL ["/bin/bash", "-c"]
 ARG DATAFED_DIR
 ARG BUILD_DIR
 ARG DATAFED_INSTALL_PATH
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH
 
+ENV DATAFED_INSTALL_PATH="$DATAFED_INSTALL_PATH"
 # This port is needed to communicate with the DataFed core server
 EXPOSE 7512
 # Not quite sure what 9000 is doing that 7512 isn't, difference between egress
@@ -37,7 +38,7 @@ COPY ./cmake                                   ${BUILD_DIR}/cmake
 COPY ./repository/server                       ${BUILD_DIR}/repository/server
 
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh && \
-	cmake -S. -B build						\
+	${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake -S. -B build						\
 	-DBUILD_REPO_SERVER=True		\
 	-DBUILD_AUTHZ=False					\
 	-DBUILD_CORE_SERVER=False		\
@@ -45,18 +46,22 @@ RUN ${BUILD_DIR}/scripts/generate_datafed.sh && \
 	-DBUILD_DOCS=False					\
 	-DBUILD_PYTHON_CLIENT=False	\
 	-DBUILD_FOXX=False
-RUN cmake --build build
-RUN cmake --build build --target install
+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
 
+SHELL ["/bin/bash", "-c"]
 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"
@@ -64,14 +69,14 @@ ENV              LIB_DIR="$LIB_DIR"
 WORKDIR /datafed
 
 # copy necessary shared libraries
-COPY --from=repo-build /libraries/libprotobuf.so           /libraries/libprotobuf.so
-COPY --from=repo-build /libraries/libzmq.so                /libraries/libzmq.so
-COPY --from=repo-build /libraries/libsodium.so             /libraries/libsodium.so
+#COPY --from=repo-build /libraries/libprotobuf.a           /libraries/libprotobuf.a
+#COPY --from=repo-build /libraries/libzmq.a                /libraries/libzmq.a
+#COPY --from=repo-build /libraries/libsodium.a             /libraries/libsodium.a
 COPY --from=repo-build /libraries/libboost_program_options.so /libraries/libboost_program_options.so
 COPY --from=repo-build /libraries/libboost_filesystem.so      /libraries/libboost_filesystem.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 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
 RUN ${BUILD_DIR}/scripts/copy_dependency.sh boost_filesystem to
 
@@ -79,12 +84,16 @@ RUN ldconfig
 
 USER datafed
 
-COPY --chown=datafed:root ./repository/docker/entrypoint_repo.sh ${BUILD_DIR}/repository/entrypoint.sh
 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/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 7fd8604ac..635725150 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,64 +52,86 @@ 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.so          /libraries/libprotobuf.so
-COPY --from=dependencies /libraries/libprotoc.so						/libraries/libprotoc.so
-COPY --from=dependencies /libraries/libzmq.so               /libraries/libzmq.so
-COPY --from=dependencies /libraries/libsodium.so            /libraries/libsodium.so
-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 &&	\
-	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	cmake --build build
-RUN cmake --build build --target install
+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 wget -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
@@ -111,4 +139,11 @@ COPY --chown=datafed:root ./repository/docker/entrypoint_authz.sh ${BUILD_DIR}/r
 
 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 fd62494e4..4ca8ecb4b 100644
--- a/repository/docker/Dockerfile.gcs-authz-base.ubuntu
+++ b/repository/docker/Dockerfile.gcs-authz-base.ubuntu
@@ -1,8 +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}"
 
+# 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}
@@ -11,6 +22,9 @@ COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/dependency_versions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/dependency_install_functions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/install_authz_dependencies.sh ${BUILD_DIR}/scripts/
+COPY ./scripts/utils.sh ${BUILD_DIR}/scripts/
+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_DIR}/scripts/generate_datafed.sh
diff --git a/repository/docker/Dockerfile.gcs-authz.ubuntu b/repository/docker/Dockerfile.gcs-authz.ubuntu
index 56ec69e2d..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
@@ -33,6 +36,7 @@ WORKDIR ${BUILD_DIR}
 COPY ./scripts/dependency_versions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/generate_authz_config.sh ${BUILD_DIR}/scripts/generate_authz_config.sh
 COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/generate_datafed.sh
+COPY ./scripts/utils.sh ${BUILD_DIR}/scripts/
 COPY ./CMakeLists.txt ${BUILD_DIR}
 COPY ./cmake ${BUILD_DIR}/cmake
 COPY ./repository/CMakeLists.txt ${BUILD_DIR}/repository/CMakeLists.txt
@@ -43,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.repo-base.ubuntu b/repository/docker/Dockerfile.repo-base.ubuntu
index 4c6b40d21..838a14beb 100644
--- a/repository/docker/Dockerfile.repo-base.ubuntu
+++ b/repository/docker/Dockerfile.repo-base.ubuntu
@@ -1,16 +1,27 @@
 FROM ubuntu:focal as build
 
 ARG BUILD_DIR="/source"
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
 
 RUN mkdir -p ${BUILD_DIR}
 RUN mkdir -p ${BUILD_DIR}/repository/server
 
+ENV DATAFED_DEPENDENCIES_INSTALL_PATH="${DATAFED_DEPENDENCIES_INSTALL_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/
 COPY ./scripts/dependency_versions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/dependency_install_functions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/install_repo_dependencies.sh ${BUILD_DIR}/scripts/
+COPY ./scripts/utils.sh ${BUILD_DIR}/scripts/
+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_DIR}/scripts/generate_datafed.sh
diff --git a/repository/docker/Dockerfile.repo.ubuntu b/repository/docker/Dockerfile.repo.ubuntu
index 4e663c37d..f6359a137 100644
--- a/repository/docker/Dockerfile.repo.ubuntu
+++ b/repository/docker/Dockerfile.repo.ubuntu
@@ -1,4 +1,6 @@
-FROM camden.ornl.gov/datafed/repo-base:latest as build
+ARG REPO_BASE_IMAGE="camden.ornl.gov/datafed/repo-base:latest"
+
+FROM "$REPO_BASE_IMAGE" as build
 
 ARG DATAFED_DIR="/datafed"
 ARG BUILD_DIR="/datafed/source"
@@ -8,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 "${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
@@ -40,13 +44,19 @@ COPY ./repository/docker/entrypoint_repo.sh ${BUILD_DIR}/repository/docker/
 # All files should be owned by the datafed user
 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"
+
+USER root
 
 ENTRYPOINT ["/datafed/source/repository/docker/entrypoint_repo.sh"]
 
diff --git a/repository/docker/entrypoint_authz.sh b/repository/docker/entrypoint_authz.sh
index 3a072d107..758513b21 100755
--- a/repository/docker/entrypoint_authz.sh
+++ b/repository/docker/entrypoint_authz.sh
@@ -8,8 +8,158 @@ SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 PROJECT_ROOT=$(realpath ${SOURCE}/../..)
 # Translate datafed env variables to globus env variables
-GLOBUS_CLIENT_ID="$DATAFED_GLOBUS_APP_ID"
-${PROJECT_ROOT}/scripts/generate_datafed.sh
-${BUILD_DIR}/scripts/globus/setup_globus.sh
 
-exec "$@"
+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
+
+if [ ! -f "${DATAFED_INSTALL_PATH}/keys/datafed-core-key.pub" ]
+then
+  echo "datafed-core-key.pub not found, downloading from the core server"
+  wget --no-check-certificate "https://${DATAFED_DOMAIN}/datafed-core-key.pub" -P "${DATAFED_INSTALL_PATH}/keys/"
+fi
+
+"${PROJECT_ROOT}/scripts/generate_datafed.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
+  mkdir -p "${log_path}"
+fi
+
+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 4eebc6225..e728d9d9d 100755
--- a/repository/docker/entrypoint_repo.sh
+++ b/repository/docker/entrypoint_repo.sh
@@ -1,13 +1,49 @@
 #!/bin/bash
 
 set -euf -o pipefail
-
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
-PROJECT_ROOT=$(realpath ${SOURCE}/../..)
+PROJECT_ROOT=$(realpath "${SOURCE}/../..")
+
+"${PROJECT_ROOT}/scripts/generate_datafed.sh"
+"${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}" ]
+then
+  su -c "mkdir -p ${log_path}" datafed
+fi
+
+if [ ! -f "${DATAFED_INSTALL_PATH}/keys/datafed-core-key.pub" ]
+then
+  echo "datafed-core-key.pub not found, downloading from the core server"
+  wget --no-check-certificate "https://${DATAFED_DOMAIN}/datafed-core-key.pub" -P "${DATAFED_INSTALL_PATH}/keys/"
+fi
 
-${PROJECT_ROOT}/scripts/generate_datafed.sh
-${PROJECT_ROOT}/scripts/generate_repo_config.sh
-${PROJECT_ROOT}/scripts/install_repo.sh
+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
 
-exec "$@"
+# 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 85d7a7c9c..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 ${Protobuf_LIBRARIES} Threads::Threads ${PkgConfig_ZMQ_LIBRARIES} ${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 c0404cecb..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 ${Protobuf_LIBRARIES} Threads::Threads ${PkgConfig_ZMQ_LIBRARIES} ${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} ${PkgConfig_ZMQ_INCLUDE_DIRS} ${Protobuf_INCLUDE_DIRS})
+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/ci_database_health_check.sh b/scripts/ci_database_health_check.sh
new file mode 100755
index 000000000..cd782aa50
--- /dev/null
+++ b/scripts/ci_database_health_check.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+DATABASE_PORT="8529"
+DATAFED_DATABASE="sdms"
+RESPONSE=""
+
+if [ -z "${DATAFED_DATABASE_HOST}" ]
+then
+  local_DATAFED_DATABASE_HOST=""
+else
+  local_DATAFED_DATABASE_HOST=$(printenv DATAFED_DATABASE_HOST)
+fi
+
+establish_connection() {
+
+  local URL="http://${local_DATAFED_DATABASE_HOST}:${DATABASE_PORT}/_db/${DATAFED_DATABASE}/api/0/version"   
+  local CONNECTION="FALSE"
+  local count=0
+  local max_count=40
+  while [ "$CONNECTION" == "FALSE" ]
+  do
+    echo "Attempt $count - Checking Database Connection at: $URL"
+    RESPONSE=$(curl -s "$URL")
+    local error_code="$?"
+    ((count++))
+    if [ "$error_code" = "0" ]
+    then
+      echo "Connection made"
+      CONNECTION="TRUE"
+    else
+      sleep 5
+    fi
+
+    if [ "$count" -eq "$max_count" ]
+    then
+      echo "Max attempts made exiting!"
+      exit 1
+    fi
+  done
+}
+
+foxx_provisioned() {
+
+  local URL="http://${local_DATAFED_DATABASE_HOST}:${DATABASE_PORT}/_db/${DATAFED_DATABASE}/api/0/version"   
+  local FOXX_PROVISIONED="FALSE"
+  local count=0
+  local max_count=20
+  while [ "$FOXX_PROVISIONED" == "FALSE" ]
+  do
+    echo "Attempt $count - Checking foxx endpoint exists: $URL"
+    RESPONSE=$(curl -s "$URL")
+    local error=$(echo "$RESPONSE" | jq -r .error)
+    ((count++))
+    if [ "$error" == "true" ]
+    then
+      sleep 5
+    else
+      echo "Endpoint exists!"
+      FOXX_PROVISIONED="TRUE"
+    fi
+
+    if [ "$count" -eq "$max_count" ]
+    then
+      echo "Max attempts made exiting!"
+      exit 1
+    fi
+  done
+}
+
+establish_connection
+foxx_provisioned
+exit 0
diff --git a/scripts/ci_setup_web_certs.sh b/scripts/ci_setup_web_certs.sh
new file mode 100755
index 000000000..aa7c1f8db
--- /dev/null
+++ b/scripts/ci_setup_web_certs.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+if [ ! -e "$DATAFED_WEB_CERT_PATH" ] || [ ! -e "$DATAFED_WEB_KEY_PATH" ]
+then
+  if [ -e "$DATAFED_WEB_CERT_PATH" ]
+  then
+    rm "${DATAFED_WEB_CERT_PATH}"
+  fi
+  if [ -e "$DATAFED_WEB_KEY_PATH" ]
+  then
+    rm "${DATAFED_WEB_KEY_PATH}"
+  fi
+  if [ -e "$DATAFED_WEB_CSR_PATH" ]
+  then
+    rm "${DATAFED_WEB_CSR_PATH}"
+  fi
+  openssl genrsa -out "$DATAFED_WEB_KEY_PATH" 2048
+  openssl req -new -key "$DATAFED_WEB_KEY_PATH" \
+    -out "${DATAFED_WEB_CSR_PATH}" \
+    -subj "/C=US/ST=TN/L=Oak Ridge/O=ORNL/OU=DLT/CN=${DI_DATAFED_DOMAIN}"
+  openssl x509 -req -days 3650 \
+     -in "${DATAFED_WEB_CSR_PATH}" \
+     -signkey "$DATAFED_WEB_KEY_PATH" \
+     -out "$DATAFED_WEB_CERT_PATH"
+fi
+
diff --git a/scripts/clear_db.sh b/scripts/clear_db.sh
index b4a5a20b4..48b7eb067 100755
--- a/scripts/clear_db.sh
+++ b/scripts/clear_db.sh
@@ -39,14 +39,24 @@ fi
 # Delete database and API from arangodb
 if command -v arangosh &> /dev/null
 then
-	exists=$(arangosh --server.endpoint "http+tcp://${local_DATAFED_DATABASE_HOST}:${local_DATAFED_DATABASE_PORT}" \
-			 --server.username "$local_DATABASE_USER" \
+	output=$(arangosh --server.endpoint "http+tcp://${local_DATAFED_DATABASE_HOST}:${local_DATAFED_DATABASE_PORT}" \
+			 --server.username "$local_DATABASE_USER"  \
 			 --server.password "$local_DATAFED_DATABASE_PASSWORD" \
-			 --javascript.execute "db._databases().includes('$local_DATABASE_NAME')")
+       --javascript.execute-string "console.log(db._databases().includes('$local_DATABASE_NAME'))" \
+       --log.use-json-format true
+     )
 
+  if [[ "$output" =~ "ERROR" ]]; then
+    echo "An error was detected."
+    echo "$output"
+    exit 1
+  fi
+
+  exists=$(echo "$output" | jq -r '.message')
+
+  echo "Does it exist $exists"
 	if [ "$exists" = "true" ]; then
-	  arangosh  --server.endpoint
-    "tcp://${local_DATAFED_DATABASE_HOST}:${local_DATAFED_DATABASE_PORT}" \
+	  arangosh  --server.endpoint "tcp://${local_DATAFED_DATABASE_HOST}:${local_DATAFED_DATABASE_PORT}" \
       --server.password "${local_DATAFED_DATABASE_PASSWORD}" \
       --server.username "${local_DATABASE_USER}" \
       --javascript.execute-string "db._dropDatabase('$local_DATABASE_NAME');"
diff --git a/scripts/container_stop.sh b/scripts/container_stop.sh
index 96ee1b18d..101881b8a 100755
--- a/scripts/container_stop.sh
+++ b/scripts/container_stop.sh
@@ -58,7 +58,7 @@ while [ : ]; do
   esac
 done
 
-if [ "$local_TAG_FLAG_DETECTED" = "1" ] && [ "$local_NAME_FLAG_DETECTED" = "1" ]
+if [ "$local_TAG_FLAG_DETECTED" == "1" ] && [ "$local_NAME_FLAG_DETECTED" == "1" ]
 then
   echo "ERROR can only specify -t or -n cannot specify both"
   exit 1
@@ -69,24 +69,24 @@ CONTAINER_IDS=($(docker container ls --format "{{.ID}}"))
 CONTAINER_IMAGES=($(docker container ls --format "{{.Image}}"))
 CONTAINER_NAMES=($(docker container ls --format "{{.Names}}"))
 
-if [ "$local_TAG_FLAG_DETECTED" = "1" ]
+if [ "$local_TAG_FLAG_DETECTED" == "1" ]
 then
 
   for (( i=0; i<${#CONTAINER_IDS[@]}; i++)); do
     IMAGE="${CONTAINER_IMAGES[$i]}"
-    if [ "$IMAGE" = "local_CONTAINER_TAG" ]
+    if [ "$IMAGE" == "$local_CONTAINER_TAG" ]
     then
       echo "Stopping ${CONTAINER_IDS[$i]} ${CONTAINER_NAMES[$i]} $IMAGE"
       docker container stop --time "$SLEEP_TIME" "${CONTAINER_IDS[$i]}"
     fi
   done
 
-elif [ "$local_NAME_FLAG_DETECTED" = "1" ] 
+elif [ "$local_NAME_FLAG_DETECTED" == "1" ] 
 then
 
   for (( i=0; i<${#CONTAINER_IDS[@]}; i++)); do
     NAME="${CONTAINER_NAMES[$i]}"
-    if [ "$NAME" = "local_CONTAINER_NAME" ]
+    if [ "$NAME" == "$local_CONTAINER_NAME" ]
     then
       echo "Stopping ${CONTAINER_IDS[$i]} ${CONTAINER_NAMES[$i]} ${CONTAINER_IMAGES[$i]}"
       docker container stop --time "$SLEEP_TIME" "${CONTAINER_IDS[$i]}"
@@ -102,7 +102,7 @@ else
 
 fi
 
-if [ ! "${#CONTAINER_IDS[@]}" = "0" ]
+if [ ! "${#CONTAINER_IDS[@]}" == "0" ]
 then
   sleep "$SLEEP_TIME"
 fi
@@ -112,24 +112,24 @@ CONTAINER_IDS=($(docker container ls --format "{{.ID}}"))
 CONTAINER_IMAGES=($(docker container ls --format "{{.Image}}"))
 CONTAINER_NAMES=($(docker container ls --format "{{.Names}}"))
 
-if [ "$local_TAG_FLAG_DETECTED" = "1" ]
+if [ "$local_TAG_FLAG_DETECTED" == "1" ]
 then
 
   for (( i=0; i<${#CONTAINER_IDS[@]}; i++)); do
     IMAGE="${CONTAINER_IMAGES[$i]}"
-    if [ "$IMAGE" = "local_CONTAINER_TAG" ]
+    if [ "$IMAGE" == "$local_CONTAINER_TAG" ]
     then
       echo "ERROR still running ${CONTAINER_IDS[$i]} ${CONTAINER_NAMES[$i]} $IMAGE"
       exit 1
     fi
   done
 
-elif [ "$local_NAME_FLAG_DETECTED" = "1" ] 
+elif [ "$local_NAME_FLAG_DETECTED" == "1" ] 
 then
 
   for (( i=0; i<${#CONTAINER_IDS[@]}; i++)); do
     NAME="${CONTAINER_NAMES[$i]}"
-    if [ "$NAME" = "local_CONTAINER_NAME" ]
+    if [ "$NAME" == "$local_CONTAINER_NAME" ]
     then
       echo "ERROR still running ${CONTAINER_IDS[$i]} ${CONTAINER_NAMES[$i]} ${CONTAINER_IMAGES[$i]}"
       exit 1
@@ -138,7 +138,7 @@ then
 
 else
 
-  if [ ! "${#CONTAINER_IDS[@]}" = "0" ]
+  if [ ! "${#CONTAINER_IDS[@]}" == "0" ]
   then
     docker container ls
     exit 1
diff --git a/scripts/copy_dependency.sh b/scripts/copy_dependency.sh
index 5ffedeb15..d4ce4aa67 100755
--- a/scripts/copy_dependency.sh
+++ b/scripts/copy_dependency.sh
@@ -5,9 +5,27 @@
 source "$BUILD_DIR/scripts/dependency_versions.sh"
 
 # Define the list of supported libraries and their environment variable names
-supported_libraries=("protobuf" "protoc" "libsodium" "libzmq" "boost_program_options" "boost_filesystem")
-library_env_variables=("DATAFED_FULL_PROTOBUF_VERSION" "DATAFED_FULL_PROTOBUF_VERSION" "DATAFED_LIB_LIBSODIUM_VERSION" "DATAFED_LIB_LIBZMQ_VERSION" "DATAFED_BOOST" "DATAFED_BOOST")
-library_names=("libprotobuf.so" "libprotoc.so" "libsodium.so" "libzmq.so" "libboost_program_options.so" "libboost_filesystem.so")
+supported_libraries=("protobuf" \
+  "protoc" \
+  "libsodium" \
+  "libzmq" \
+  "boost_program_options" \
+  "boost_filesystem")
+
+library_env_variables=("DATAFED_DYNAMIC_LIBRARY_PROTOBUF_VERSION" \
+  "DATAFED_DYNAMIC_LIBRARY_PROTOBUF_VERSION" \
+  "DATAFED_LIB_LIBSODIUM_VERSION" \
+  "DATAFED_LIB_LIBZMQ_VERSION" \
+  "DATAFED_BOOST" \
+  "DATAFED_BOOST")
+
+library_names=("libprotobuf.a" \
+  "libprotoc.a" \
+  "libsodium.a" \
+  "libzmq.a" \
+  "libboost_program_options.so" \
+  "libboost_filesystem.so")
+
 library_locations=("$LIB_DIR" "$LIB_DIR" "$LIB_DIR" "$LIB_DIR" "/lib/x86_64-linux-gnu" "/lib/x86_64-linux-gnu")
 
 LIBRARIES_BASE_PATH="/libraries"
@@ -56,12 +74,18 @@ destination_filename=""
 
 if [ "$direction" == "from" ]; then
   # Copy from versioned to generic filename
-  source_filename="$library_location/$library_name.$library_version"
+  source_filename="$library_location/$library_name"
   destination_filename="$LIBRARIES_BASE_PATH/$library_name"
+  if [[ "$library_name" == *".so" ]]; then
+    source_filename="$source_filename.$library_version"
+  fi
 elif [ "$direction" == "to" ]; then
   # Copy from generic to versioned filename
   source_filename="$LIBRARIES_BASE_PATH/$library_name"
-  destination_filename="$LIB_DIR/$library_name.$library_version"
+  destination_filename="$LIB_DIR/$library_name"
+  if [[ "$library_name" == *".so" ]]; then
+    destination_filename="$destination_filename.$library_version"
+  fi
 else
   echo "Invalid direction. Use 'to' or 'from'."
   exit 1
diff --git a/scripts/dependency_install_functions.sh b/scripts/dependency_install_functions.sh
index 1699548f5..ce6924ee9 100644
--- a/scripts/dependency_install_functions.sh
+++ b/scripts/dependency_install_functions.sh
@@ -2,11 +2,19 @@
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 source "${SOURCE}/dependency_versions.sh"
+PROJECT_ROOT=$(realpath "${SOURCE}/..")
+source "${SOURCE}/utils.sh"
 
+sudo_command
 # these are the dependencies to be installed by apt
-apt_file_path="/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="/tmp/ext_deps"
+export ext_file_path="${PROJECT_ROOT}/tmp/ext_deps"
+
+if [ ! -d "${PROJECT_ROOT}/tmp" ]; then
+    mkdir -p "${PROJECT_ROOT}/tmp" 
+fi
 
 if [ ! -e "${PROJECT_ROOT}/config/datafed.sh" ]
 then
@@ -17,144 +25,308 @@ fi
 source "${PROJECT_ROOT}/config/datafed.sh"
 
 if [ ! -e "$DATAFED_DEPENDENCIES_INSTALL_PATH" ] || [ ! -d "$DATAFED_DEPENDENCIES_INSTALL_PATH" ]; then
-    mkdir -p "$DATAFED_DEPENDENCIES_INSTALL_PATH"
+    parent_dir=$(dirname "${DATAFED_DEPENDENCIES_INSTALL_PATH}")
+    if [ -w "${parent_dir}" ]; then
+      mkdir -p "$DATAFED_DEPENDENCIES_INSTALL_PATH"
+    else
+      echo "Sudo command $SUDO_CMD"
+      "$SUDO_CMD" mkdir -p "$DATAFED_DEPENDENCIES_INSTALL_PATH"
+      user=$(whoami)  
+      "$SUDO_CMD" chown "$user" "$DATAFED_DEPENDENCIES_INSTALL_PATH" 
+    fi
+fi
+
+# 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 ".cmake_installed-${DATAFED_CMAKE_VERSION}" ]; then
+  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
-    cp -r cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64/bin /usr/local
-    cp -r cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64/share /usr/local
+    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}"
 
     # Cleanup
-    rm -rf cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64 
-    rm -rf cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64.tar.gz
+    rm -rf "cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64"
+    rm -rf "cmake-${DATAFED_CMAKE_VERSION}-Linux-x86_64.tar.gz"
 
     # Mark cmake as installed
-    touch ".cmake_installed-${DATAFED_CMAKE_VERSION}"
+    touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.cmake_installed-${DATAFED_CMAKE_VERSION}"
   fi
+  export PATH="${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin:${PATH}"
 }
 
 install_protobuf() {
-  if [ ! -e ".protobuf_installed-${DATAFED_PROTOBUF_VERSION}" ]; then
-    if [ -d protobuf ]
+  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 rm -rf protobuf 
+      "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/protobuf"
     fi
-    git clone  https://github.com/google/protobuf.git
-    cd protobuf
-    git checkout v${DATAFED_PROTOBUF_VERSION}
+    # 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
-    cmake -S cmake/ -B build -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_SHARED_LIBS=ON
+    # 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 \
+      -Dprotobuf_BUILD_TESTS=OFF \
+      -DABSL_PROPAGATE_CXX_STD=ON \
+      -DCMAKE_INSTALL_PREFIX="${DATAFED_DEPENDENCIES_INSTALL_PATH}"
     cmake --build build -j 8
-    sudo cmake --build build --target install
+    if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
+      cmake --build build --target install
+    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
-    python3 setup.py build
-    python3 setup.py test
-    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 ]
     then
-      sudo rm build/install_manifest.txt
+      "$SUDO_CMD" rm build/install_manifest.txt
     fi
-    cd ../
+    cd "${PROJECT_ROOT}"
 
     # Mark protobuf as installed
-    touch ".protobuf_installed-${DATAFED_PROTOBUF_VERSION}"
+    touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.protobuf_installed-${DATAFED_PROTOBUF_VERSION}"
+    cd "$original_dir"
   fi
 }
 
 install_libsodium() {
-  if [ ! -e ".libsodium_installed-${DATAFED_LIBSODIUM_VERSION}" ]; then
-    if [ -d libsodium ]
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libsodium_installed-${DATAFED_LIBSODIUM_VERSION}" ]; then
+    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 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
-    ./configure
+    # 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
-    sudo make install
-    sudo ldconfig
-    cd ../
+    if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
+      make install
+    else
+      "$SUDO_CMD" make install
+    fi
     
     # Mark libsodium as installed
-    touch ".libsodium_installed-${DATAFED_LIBSODIUM_VERSION}"
+    touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libsodium_installed-${DATAFED_LIBSODIUM_VERSION}"
+    cd "$original_dir"
   fi
 }
 
 install_libzmq() {
-  if [ ! -e ".libzmq_installed-${DATAFED_LIBZMQ_VERSION}" ]; then
-    if [ -d libzmq ]
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libzmq_installed-${DATAFED_LIBZMQ_VERSION}" ]; then
+    local original_dir=$(pwd)
+    if [ -d "${PROJECT_ROOT}/external/libzmq" ]
+    then
+      "$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
+    # 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
+    if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
+      cmake --build build --target install
+    else
+      "$SUDO_CMD" cmake --build build --target install
+    fi
+
+    if [ -d "${PROJECT_ROOT}/external/cppzmq" ]
     then
-      rm -rf libzmq 
+      # sudo required because of egg file
+      "$SUDO_CMD" rm -rf "${PROJECT_ROOT}/external/cppzmq"
     fi
-    git clone https://github.com/zeromq/libzmq.git
-    cd libzmq
-    git checkout v${DATAFED_LIBZMQ_VERSION}
-    cmake -S. -B build -DBUILD_STATIC=ON -DBUILD_SHARED=ON
+    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
-    sudo cmake --build build --target install
+    if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
+      cmake --build build --target install
+    else
+      "$SUDO_CMD" cmake --build build --target install
+    fi
     
+    cd "$original_dir"
     # Mark libzmq as installed
-    touch ".libzmq_installed-${DATAFED_LIBZMQ_VERSION}"
+    touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.libzmq_installed-${DATAFED_LIBZMQ_VERSION}"
   fi
 }
 
 install_nlohmann_json() {
-  if [ ! -e ".nlohmann_json_installed-${DATAFED_NLOHMANN_JSON_VERSION}" ]; then
-    if [ -d json ]
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.nlohmann_json_installed-${DATAFED_NLOHMANN_JSON_VERSION}" ]; then
+    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)"
-    cmake -S . -B build
+    # 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
-    sudo cmake --build build --target install
-    cd ../
-    
+    if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
+      cmake --build build --target install
+    else
+      "$SUDO_CMD" cmake --build build --target install
+    fi
+    # 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 ".nlohmann_json_installed-${DATAFED_NLOHMANN_JSON_VERSION}"
+    touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.nlohmann_json_installed-${DATAFED_NLOHMANN_JSON_VERSION}"
+    cd "$original_dir"
   fi
 }
 
 install_json_schema_validator() {
-  if [ ! -e ".json_schema_validator_installed-${DATAFED_JSON_SCHEMA_VALIDATOR_VERSION}" ]; then
-    if [ -d json-schema-validator ]
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.json_schema_validator_installed-${DATAFED_JSON_SCHEMA_VALIDATOR_VERSION}" ]; then
+    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}
-    cmake -S . -B build
+    # 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
-    sudo cmake --build build --target install
-    cd ../
-    
+    if [ -w "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]; then
+      cmake --build build --target install
+    else
+      "$SUDO_CMD" cmake --build build --target install
+    fi
+    # 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 ".json_schema_validator_installed-${DATAFED_JSON_SCHEMA_VALIDATOR_VERSION}"
+    touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.json_schema_validator_installed-${DATAFED_JSON_SCHEMA_VALIDATOR_VERSION}"
+    cd "$original_dir"
   fi
 }
 
 install_gcs() {
   if [ ! -e ".gcs_installed-${DATAFED_GLOBUS_VERSION}" ]; then
-    sudo apt update
-    sudo apt install -y curl git gnupg
-    curl -LOs https://downloads.globus.org/globus-connect-server/stable/installers/repo/deb/globus-repo_${DATAFED_GLOBUS_VERSION}_all.deb
-    sudo dpkg -i globus-repo_${DATAFED_GLOBUS_VERSION}_all.deb
-    sudo apt-key add /usr/share/globus-repo/RPM-GPG-KEY-Globus
+    "$SUDO_CMD" apt update
+    "$SUDO_CMD" apt install -y curl git gnupg
+    curl -LOs \
+    "https://downloads.globus.org/globus-connect-server/stable/installers/repo/deb/globus-repo_${DATAFED_GLOBUS_VERSION}_all.deb"
+    "$SUDO_CMD" dpkg -i "globus-repo_${DATAFED_GLOBUS_VERSION}_all.deb"
+    "$SUDO_CMD" apt-key add /usr/share/globus-repo/RPM-GPG-KEY-Globus
     # Need a second update command after adding the globus GPG key
-    sudo apt update
-    sudo apt-get install globus-connect-server54 -y
+    "$SUDO_CMD" apt update
+    "$SUDO_CMD" apt-get install globus-connect-server54 -y
     
     # Mark gcs as installed
     touch ".gcs_installed-${DATAFED_GLOBUS_VERSION}"
@@ -163,61 +335,198 @@ install_gcs() {
 
 install_nvm() {
   # By default this will place NVM in $HOME/.nvm
-  if [ ! -e ".nvm_installed-${DATAFED_NVM_VERSION}" ]; then
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.nvm_installed-${DATAFED_NVM_VERSION}" ]; then
     # By setting NVM_DIR beforehand when the scirpt is run it 
     # will use it to set the install path
     export NVM_DIR="${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm"
     mkdir -p "${NVM_DIR}"
     curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/${DATAFED_NVM_VERSION}/install.sh" | bash
     # Mark nvm as installed
-    touch ".nvm_installed-${DATAFED_NVM_VERSION}"
+    touch "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.nvm_installed-${DATAFED_NVM_VERSION}"
+  else
+    export NVM_DIR="${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm"
   fi
 }
 
-install_node() {
-  # By default this will place NVM in $HOME/.nvm
-  if [ ! -e ".nvm_installed-${DATAFED_NVM_VERSION}" ]; then
-    echo "You must first install nvm before installing node."
+install_ws_node_packages() {
+
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.nvm_installed-${DATAFED_NVM_VERSION}" ]; then
+    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
-  if [ ! -e ".node_installed-${DATAFED_NODE_VERSION}" ]; then
+
+  # 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"
 
     [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
     nvm install "$DATAFED_NODE_VERSION"
     # Mark node as installed
-    touch ".node_installed-${DATAFED_NODE_VERSION}"
+    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
   fi
 }
 
 install_foxx_cli() {
-  if [ ! -e ".nvm_installed-${DATAFED_NVM_VERSION}" ]; then
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.nvm_installed-${DATAFED_NVM_VERSION}" ]; then
     echo "You must first install nvm before installing foxx_cli."
     exit 1
   fi
-  if [ ! -e ".node_installed-${DATAFED_NODE_VERSION}" ]; then
+  if [ ! -e "${DATAFED_DEPENDENCIES_INSTALL_PATH}/.node_installed-${DATAFED_NODE_VERSION}" ]; then
     echo "You must first install node before installing foxx_cli"
     exit 1
   fi
   # By default this will place NVM in $HOME/.nvm
-  if [ ! -e ".foxx_cli_installed" ]; then
+  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 ".foxx_cli_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
+    export NODE_VERSION="$DATAFED_NODE_VERSION"
   fi
 }
 
 install_arangodb() {
   curl -OL https://download.arangodb.com/arangodb38/DEBIAN/Release.key
-  sudo apt-key add - < Release.key
-  echo 'deb https://download.arangodb.com/arangodb38/DEBIAN/ /' | sudo tee /etc/apt/sources.list.d/arangodb.list
-  sudo apt-get install apt-transport-https
-  sudo apt-get update
-  sudo apt-get install arangodb3
+  "$SUDO_CMD" apt-key add - < Release.key
+  echo 'deb https://download.arangodb.com/arangodb38/DEBIAN/ /' | "$SUDO_CMD" tee /etc/apt/sources.list.d/arangodb.list
+  "$SUDO_CMD" apt-get install apt-transport-https
+  "$SUDO_CMD" apt-get update
+  "$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() {
@@ -243,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 91a2cdb9c..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"
@@ -10,10 +10,21 @@ DATAFED_LIB_LIBSODIUM_VERSION="23.3.0"
 DATAFED_LIBZMQ_VERSION="4.3.4"
 # this version is different from above due to the fact libzmq names its shared library diffrently than the actual api version
 DATAFED_LIB_LIBZMQ_VERSION="5.2.4"
+DATAFED_LIB_ZMQCPP_VERSION="4.10.0"
+DATAFED_NVM_VERSION="v0.39.7"
 DATAFED_NODE_VERSION="v14.21.3"
 DATAFED_NVM_VERSION="v0.39.7"
-DATAFED_PROTOBUF_VERSION="3.17.3"
-DATAFED_FULL_PROTOBUF_VERSION="3.17.3.0"
-DATAFED_LIBCURL="7.68.0"
+# Git tag
+DATAFED_PROTOBUF_VERSION="25.2"
+# Dynamic library extension .so.{DATAFED_FULL_PROTOBUF_VERSION}
+DATAFED_DYNAMIC_LIBRARY_PROTOBUF_VERSION="25.2.0"
+# Full version
+DATAFED_FULL_PROTOBUF_VERSION="4.25.2"
+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/export_dependency_version.sh b/scripts/export_dependency_version.sh
new file mode 100755
index 000000000..60335144f
--- /dev/null
+++ b/scripts/export_dependency_version.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+# Assumes sourcing not running
+SCRIPT=$( realpath "${BASH_SOURCE[0]}" )
+SCRIPT_DIR=$( dirname "${SCRIPT}" )
+PROJECT_ROOT=$(realpath "${SCRIPT_DIR}/..")
+. "${PROJECT_ROOT}/scripts/utils.sh"
+# WARNING
+# For this script to work it must be called with source
+# source export_dependency_version_numbers
+export_dependency_version_numbers
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 1e53c704a..e829ee9fe 100755
--- a/scripts/generate_datafed.sh
+++ b/scripts/generate_datafed.sh
@@ -20,6 +20,7 @@ fi
 
 # This is a build config variable
 local_DATAFED_DEPENDENCIES_INSTALL_PATH=""
+
 if [ -z "${DATAFED_DEPENDENCIES_INSTALL_PATH}" ]
 then
   local_DATAFED_DEPENDENCIES_INSTALL_PATH="/opt/datafed/dependencies"
@@ -91,7 +92,7 @@ fi
 local_DATAFED_SERVER_PORT=""
 if [ -z "${DATAFED_SERVER_PORT}" ]
 then
-  local_DATAFED_SERVER_PORT=""
+  local_DATAFED_SERVER_PORT="7512"
 else
   local_DATAFED_SERVER_PORT=$(printenv DATAFED_SERVER_PORT)
 fi
@@ -158,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"
@@ -184,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
 # ************************************************
@@ -201,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
@@ -214,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
@@ -272,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.
@@ -291,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/generate_ws_config.sh b/scripts/generate_ws_config.sh
index f14a00e4c..33211d675 100755
--- a/scripts/generate_ws_config.sh
+++ b/scripts/generate_ws_config.sh
@@ -45,15 +45,30 @@ Help()
 local_DATAFED_WEB_CERT_PATH="${DATAFED_INSTALL_PATH}/keys/datafed-server-test.ornl.gov.crt"
 local_DATAFED_WEB_KEY_PATH="${DATAFED_INSTALL_PATH}/keys/datafed-server-test.ornl.gov.key"
 
-local_DATAFED_HTTPS_SERVER_PORT="443"
+if [ ! -z "${DATAFED_WEB_KEY_PATH}" ]
+then
+    local_DATAFED_WEB_KEY_PATH=$(printenv DATAFED_WEB_KEY_PATH)
+fi
 
-if [ -z "${DATAFED_GLOBUS_APP_ID}" ]
+if [ ! -z "${DATAFED_WEB_CERT_PATH}" ]
+then
+    local_DATAFED_WEB_CERT_PATH=$(printenv DATAFED_WEB_CERT_PATH)
+fi
+
+if [ -z "${DATAFED_DOMAIN}" ]
 then
   local_DATAFED_SERVER_DOMAIN_NAME="datafed.ornl.gov"
 else
   local_DATAFED_SERVER_DOMAIN_NAME=$(printenv DATAFED_DOMAIN)
 fi
 
+if [ -z "${DATAFED_HTTPS_SERVER_PORT}" ]
+then
+  local_DATAFED_HTTPS_SERVER_PORT="443"
+else
+  local_DATAFED_HTTPS_SERVER_PORT=$(printenv DATAFED_HTTPS_SERVER_PORT)
+fi
+
 if [ -z "${DATAFED_GLOBUS_APP_ID}" ]
 then
   local_DATAFED_GLOBUS_APP_ID=""
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..e8704cd15
--- /dev/null
+++ b/scripts/globus/globus_cleanup.py
@@ -0,0 +1,207 @@
+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:
+
+    project_id = utils.getProjectId(ac_rt, 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..c25a11c86
--- /dev/null
+++ b/scripts/globus/initialize_globus_endpoint.py
@@ -0,0 +1,189 @@
+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
+default_DATAFED_GCS_ROOT_NAME = "DataFed Repo"
+DATAFED_GCS_ROOT_NAME = os.getenv(
+    "DATAFED_GCS_ROOT_NAME", default_DATAFED_GCS_ROOT_NAME
+)
+if len(DATAFED_GCS_ROOT_NAME) == 0:
+    CRED_FILE_PATH = default_DATAFED_GCS_ROOT_NAME
+
+default_PROJECT_NAME = DATAFED_GCS_ROOT_NAME + " Project"
+PROJECT_NAME = os.getenv("DATAFED_GLOBUS_PROJECT_NAME", default_PROJECT_NAME)
+if len(PROJECT_NAME) == 0:
+    PROJECT_NAME = default_PROJECT_NAME
+
+# This is for confidential client
+default_CLIENT_NAME = DATAFED_GCS_ROOT_NAME + " Setup Client"
+CLIENT_NAME = os.getenv("DATAFED_GLOBUS_CLIENT_NAME", default_CLIENT_NAME)
+if len(CLIENT_NAME) == 0:
+    CLIENT_NAME = default_CLIENT_NAME
+
+# Name of the client secret used by the confidential client
+default_CRED_NAME = DATAFED_GCS_ROOT_NAME + " Cred"
+CRED_NAME = os.getenv("DATAFED_GLOBUS_CRED_NAME", default_CRED_NAME)
+if len(CRED_NAME) == 0:
+    CRED_NAME = default_CRED_NAME
+
+# Name of the file where we will store confidential client credentials
+default_CRED_FILE_PATH = os.path.abspath("./globus/client_cred.json")
+CRED_FILE_PATH = os.getenv("DATAFED_GLOBUS_CRED_FILE_PATH", default_CRED_FILE_PATH)
+if len(CRED_FILE_PATH) == 0:
+    CRED_FILE_PATH = default_CRED_FILE_PATH
+
+# Name to give to endpoint
+default_ENDPOINT_NAME = DATAFED_GCS_ROOT_NAME + " Endpoint"
+ENDPOINT_NAME = os.getenv("DATAFED_GLOBUS_ENDPOINT_NAME", default_ENDPOINT_NAME)
+if len(ENDPOINT_NAME) == 0:
+    ENDPOINT_NAME = default_ENDPOINT_NAME
+
+# Path to deployment key
+default_DEPLOYMENT_KEY_PATH = os.path.abspath("./globus/deployment-key.json")
+DEPLOYMENT_KEY_PATH = os.getenv(
+    "DATAFED_GLOBUS_DEPLOYMENT_KEY_PATH", default_DEPLOYMENT_KEY_PATH
+)
+if len(DEPLOYMENT_KEY_PATH) == 0:
+    DEPLOYMENT_KEY_PATH = default_DEPLOYMENT_KEY_PATH
+
+# Path to deployment key
+default_DATAFED_GLOBUS_CONTROL_PORT = "443"
+DATAFED_GLOBUS_CONTROL_PORT = os.getenv(
+    "DATAFED_GLOBUS_CONTROL_PORT", default_DATAFED_GLOBUS_CONTROL_PORT
+)
+if len(DATAFED_GLOBUS_CONTROL_PORT) == 0:
+    DATAFED_GLOBUS_CONTROL_PORT = default_DATAFED_GLOBUS_CONTROL_PORT
+
+DATAFED_GLOBUS_SUBSCRIPTION = os.getenv("DATAFED_GLOBUS_SUBSCRIPTION")
+if len(DATAFED_GLOBUS_SUBSCRIPTION) == 0:
+    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)
+
+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..745b9496d
--- /dev/null
+++ b/scripts/globus/utils.py
@@ -0,0 +1,383 @@
+import subprocess
+import json
+import os
+import sys
+
+
+def getProjectId(auth_client, project_name):
+    projects = auth_client.get_projects()
+    for project in projects:
+        if project["display_name"] == project_name:
+            return project["id"]
+    return None
+
+
+def projectExists(auth_client, project_name):
+    project_id = getProjectId(auth_client, 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"],
+    }
+
+    # Check that the folder exists
+    folder_path = os.path.dirname(cred_file)
+    if not os.path.exists(folder_path):
+        try:
+            os.makedirs(folder_path)
+            print(f"Folder '{folder_path}' created successfully.")
+        except OSError as e:
+            print(f"Failed to create folder '{folder_path}': {e}")
+
+    print(f"Creating cred file {cred_file}")
+
+    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 92ee5bd9c..68142b5a4 100755
--- a/scripts/install_authz_dependencies.sh
+++ b/scripts/install_authz_dependencies.sh
@@ -7,10 +7,14 @@ SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 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" "libzmq3-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
 
@@ -24,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
       ;;
@@ -33,17 +38,18 @@ if [ $# -eq 1 ]; then
   esac
 fi
 
-sudo apt-get update
-sudo dpkg --configure -a
-sudo 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 574e0457c..2c475fa4b 100755
--- a/scripts/install_client_dependencies.sh
+++ b/scripts/install_client_dependencies.sh
@@ -7,12 +7,19 @@ SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 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 apt-get update
-sudo dpkg --configure -a
+"$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.sh b/scripts/install_core.sh
index 63748abcd..2781f4dd9 100755
--- a/scripts/install_core.sh
+++ b/scripts/install_core.sh
@@ -4,23 +4,24 @@ set -euf -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"
 
 # Make sure paths exist
-mkdir -p ${DATAFED_INSTALL_PATH}/core
-mkdir -p ${DATAFED_INSTALL_PATH}/keys
-mkdir -p ${DATAFED_DEFAULT_LOG_PATH}
+mkdir -p "${DATAFED_INSTALL_PATH}/core"
+mkdir -p "${DATAFED_INSTALL_PATH}/keys"
+mkdir -p "${DATAFED_DEFAULT_LOG_PATH}"
 
 # Copy configuration files
-cp "$PROJECT_ROOT/config/datafed-core.cfg" ${DATAFED_INSTALL_PATH}/core
+cp "$PROJECT_ROOT/config/datafed-core.cfg" "${DATAFED_INSTALL_PATH}/core"
 
 
 # Move keys to Default:/opt/datafed/keys if they do not already exist
-if [ ! -f ${DATAFED_INSTALL_PATH}/keys/datafed-core-key.priv ]
+if [ ! -f "${DATAFED_INSTALL_PATH}/keys/datafed-core-key.priv" ]
 then
   # Generate keys
-  ${DATAFED_INSTALL_PATH}/core/datafed-core --gen-keys
-  mv datafed-core-key.pub ${DATAFED_INSTALL_PATH}/keys/
-  mv datafed-core-key.priv ${DATAFED_INSTALL_PATH}/keys/
+  echo "No keys for core server were detected in ${DATAFED_INSTALL_PATH}/keys/ creating them"
+  "${DATAFED_INSTALL_PATH}/core/datafed-core" --gen-keys
+  mv datafed-core-key.pub "${DATAFED_INSTALL_PATH}/keys/"
+  mv datafed-core-key.priv "${DATAFED_INSTALL_PATH}/keys/"
 fi
diff --git a/scripts/install_core_dependencies.sh b/scripts/install_core_dependencies.sh
index a3baa4c95..c79ee55d2 100755
--- a/scripts/install_core_dependencies.sh
+++ b/scripts/install_core_dependencies.sh
@@ -7,10 +7,14 @@ SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 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" "libzmq3-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
 
@@ -25,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
       ;;
     *)
@@ -33,12 +38,16 @@ if [ $# -eq 1 ]; then
   esac
 fi
 
+
 if [[ $local_UNIFY = false ]]; then
-  sudo apt-get update
-  sudo dpkg --configure -a
-  sudo apt-get install -y "${packages[@]}"
+  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 5c4e0cb0c..4ae49ac84 100755
--- a/scripts/install_dependencies.sh
+++ b/scripts/install_dependencies.sh
@@ -7,46 +7,134 @@ SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 PROJECT_ROOT=$(realpath ${SOURCE}/..)
 
+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"
 
-sudo apt-get update
-sudo apt install -y wget git curl
+# Defines SUDO_CMD which is empty if root
+# sudo path if exists
+# throws error otherwise
 
-install_cmake
-# This script will install all of the dependencies needed by DataFed 1.0
-sudo dpkg --configure -a
-
-sudo "$SOURCE/install_core_dependencies.sh" unify
-sudo "$SOURCE/install_repo_dependencies.sh" unify
-sudo "$SOURCE/install_ws_dependencies.sh" unify
-sudo "$SOURCE/install_authz_dependencies.sh" unify
+"$SUDO_CMD" apt-get update
+"$SUDO_CMD" apt install -y wget git curl
 
-all_packages=$(cat $apt_file_path)
+# This script will install all of the dependencies needed by DataFed 1.0
+"$SUDO_CMD" dpkg --configure -a
+
+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)
+echo "DEPENDENCIES (${deduplicated_packages_array[@]})"
+"$SUDO_CMD" apt-get install -y "${deduplicated_packages_array[@]}"
+
+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
+
+all_externals=$(cat "$ext_file_path")
 IFS=' ' read -r -a all_externals_array <<< "$all_externals"
-
-sudo apt-get install -y "${deduplicated_packages_array[@]}"
-
+# 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[@]})"
-
-cd ~
-
-for ext in "${all_externals_array[@]}"; do
+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 d68d1e18e..06ee7c3de 100755
--- a/scripts/install_docs_dependencies.sh
+++ b/scripts/install_docs_dependencies.sh
@@ -7,17 +7,48 @@ SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 PROJECT_ROOT=$(realpath ${SOURCE}/..)
 
+source "${PROJECT_ROOT}/scripts/utils.sh"
 source "${PROJECT_ROOT}/scripts/dependency_install_functions.sh"
 
-# This script will install all of the dependencies needed by DataFed 1.0
-sudo apt-get update
-sudo dpkg --configure -a
-sudo apt-get install -y 
+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")
 
-install_cmake
-cd ~
+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
 
-install_protobuf
-cd ~
-python3 -m pip install --upgrade pip
-python3 -m pip install setuptools sphinx sphinx-rtd-theme sphinx-autoapi
diff --git a/scripts/install_foxx.sh b/scripts/install_foxx.sh
index 09ad12519..78d84414c 100755
--- a/scripts/install_foxx.sh
+++ b/scripts/install_foxx.sh
@@ -34,74 +34,6 @@ Help()
   echo "NOTE: Do not run this script with sudo!"
 }
 
-# Only recognized x.x.x format where all "x" are integers
-# Returns true if first version is greater or equal to second version
-#
-# semantic_version_compatible "1.2.3" "1.1.8"
-# echo $? 
-# Should print 1
-#
-# semantic_version_compatible "1.2.3" "1.2.8"
-# echo $? 
-# Should print 0
-#
-#semantic_version_compatible "1.1.1" "1.1.1"
-#echo "Should return true 1.1.1 >= 1.1.1"
-#
-#semantic_version_compatible "1.2.1" "1.1.1"
-#echo "Should return true 1.2.1 >= 1.1.1"
-#
-#semantic_version_compatible "1.2.1" "3.1.1"
-#echo "Should return false 1.2.1 >= 3.1.1"
-#
-#semantic_version_compatible "v1.2.1" "v3.1.1"
-#echo "Should return false v1.2.1 >= v3.1.1"
-#
-#semantic_version_compatible "v1.2.1" "1.1.1"
-#echo "Should return true v1.2.1 >= 1.1.1"
-
-
-semantic_version_compatible() {
-  local VER1=$1
-  local VER2=$2
-
-  # Remove any preceding v from version i.e. v1.1.2
-  VER1=$(echo $VER1 | sed 's/v//g' )
-  VER2=$(echo $VER2 | sed 's/v//g' )
-
-  maj_1=$(echo $VER1 | sed 's/\./ /g' | awk '{print $1}')
-  min_1=$(echo $VER1 | sed 's/\./ /g' | awk '{print $2}')
-  patch_1=$(echo $VER1 | sed 's/\./ /g' | awk '{print $3}')
-  maj_2=$(echo $VER2 | sed 's/\./ /g' | awk '{print $1}')
-  min_2=$(echo $VER2 | sed 's/\./ /g' | awk '{print $2}')
-  patch_2=$(echo $VER2 | sed 's/\./ /g' | awk '{print $3}')
-
-  if [ "$maj_1" -gt "$maj_2" ]
-  then
-    return 1
-  elif [ "$maj_1" -lt "$maj_2" ]
-  then
-    return 0
-  fi
-
-  if [ "$min_1" -gt "$min_2" ]
-  then
-    return 1
-  elif [ "$min_1" -lt "$min_2" ]
-  then
-    return 0
-  fi
-
-  if [ "$patch_1" -gt "$patch_2" ]
-  then
-    return 1
-  elif [ "$patch_1" -lt "$patch_2" ]
-  then
-    return 0
-  fi
-  return 1
-}
-
 local_DATABASE_NAME="sdms"
 local_DATABASE_USER="root"
 local_DATABASE_PORT="8529"
@@ -203,11 +135,27 @@ then
   exit 1
 fi
 
+basic_auth="$local_DATABASE_USER:$local_DATAFED_DATABASE_PASSWORD"
+url="http://${local_DATAFED_DATABASE_HOST}:${local_DATABASE_PORT}/_api/database/user"
+code=$(curl -s -o /dev/null -w "%{http_code}" --user "$basic_auth" "$url")
+
+if [[ "$code" != "200" ]]; then
+  echo "Error detected in attempting to connect to database at $url"
+  echo "HTTP code is: $code"
+  exit 1
+fi
+
 # We are now going to initialize the DataFed database in Arango, but only if sdms database does
 # not exist
-output=$(curl --dump - \
-  --user "$local_DATABASE_USER:$local_DATAFED_DATABASE_PASSWORD"
-  "http://${local_DATAFED_DATABASE_HOST}:${local_DATABASE_PORT}/_api/database/user")
+output=$(curl -s -o /dev/null -i --dump - --user "$basic_auth" "$url")
+
+echo "Output: $output"
+
+if [[ "$output" == "" ]]; then
+  echo "curl command failed $url exiting"
+  exit 1
+fi
+
 
 if [[ "$output" =~ .*"sdms".* ]]; then
 	echo "SDMS already exists do nothing"
@@ -237,29 +185,17 @@ fi
 #
 # The web deployment requires manual interaction, and I could not figure out the 
 # syntax for the REST http endpoints with curl so we are going to try the node module
-actual_version=$(node --version)
-semantic_version_compatible "$actual_version" "$DATAFED_NODE_VERSION"
-compatible=$?
 
-#if [ "$compatible" -eq "0" ]
-#then
-  # 1. Install nvm which will allow us to update node
+# 1. Install nvm which will allow us to update node
 install_nvm
 install_node
 install_foxx_cli
-#else 
-  # We are assuming that if the correct version of node is installed then the
-  # correct version of npm is also installed
-#  npm install --global foxx-cli --prefix ~/
-#fi
 
 FOXX_PREFIX=""
-{
-	# Determine if exists globally first
-	which foxx
-} || {
-	FOXX_PREFIX="${NPM_CONFIG_PREFIX}/bin/"
-}
+if ! command -v foxx > /dev/null 2>&1; then
+    FOXX_PREFIX="${DATAFED_DEPENDENCIES_INSTALL_PATH}/npm/bin/"
+fi
+
 
 PATH_TO_PASSWD_FILE="${SOURCE}/database_temp.password"
 
@@ -295,9 +231,6 @@ echo "$local_DATAFED_DATABASE_PASSWORD" > "${PATH_TO_PASSWD_FILE}"
       "/api/${local_FOXX_MAJOR_API_VERSION}" "${PROJECT_ROOT}/core/database/foxx/"
     echo "foxx replace -u ${local_DATABASE_USER} -p ${PATH_TO_PASSWD_FILE} --database ${local_DATABASE_NAME} /api/${local_FOXX_MAJOR_API_VERSION} ${PROJECT_ROOT}/core/database/foxx"
   fi
-
-  
-
   rm "${PATH_TO_PASSWD_FILE}"
 } || { # catch
   rm "${PATH_TO_PASSWD_FILE}"
diff --git a/scripts/install_lego_and_certificates.sh b/scripts/install_lego_and_certificates.sh
index b49edc834..6f19b0c3d 100755
--- a/scripts/install_lego_and_certificates.sh
+++ b/scripts/install_lego_and_certificates.sh
@@ -4,8 +4,9 @@ set -euf -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"
+source "${PROJECT_ROOT}/scripts/utils.sh"
 
 Help()
 {
@@ -78,9 +79,11 @@ then
   exit 1
 fi
 
-sudo add-apt-repository --yes ppa:longsleep/golang-backports
-sudo apt-get update
-sudo apt-get install golang-go
+sudo_commands
+
+"$SUDO_CMD" add-apt-repository --yes ppa:longsleep/golang-backports
+"$SUDO_CMD" apt-get update
+"$SUDO_CMD" apt-get install golang-go
 
 #This was verified for go 1.17
 export GO111MODULE=on
@@ -89,13 +92,13 @@ GOBIN=/usr/local/bin/ go install github.com/go-acme/lego/v4/cmd/lego@latest
 # Create the folder
 if [ ! -d "${DATAFED_INSTALL_PATH}/keys" ]
 then
-	sudo mkdir -p "${DATAFED_INSTALL_PATH}/keys"
+	"$SUDO_CMD" mkdir -p "${DATAFED_INSTALL_PATH}/keys"
 fi
 
 # Check if the datafed-ws server is already running, will need to stop it if we want
 # to use port 443 to start the domain name
 datafed_ws_service=$(systemctl list-unit-files --type service | grep datafed-ws | awk '{print $1}')
-[[ "$datafed_ws_service" == 'datafed-ws.service' ]] && sudo systemctl stop datafed-ws.service
+[[ "$datafed_ws_service" == 'datafed-ws.service' ]] && "$SUDO_CMD" systemctl stop datafed-ws.service
 
 # This should create a folder in ~/.lego/certificates, that contains the
 # certificate files you need, we are going to copy them over to the
@@ -109,7 +112,7 @@ cert_file="datafed-server-test.ornl.gov.crt"
 key_file="datafed-server-test.ornl.gov.key"
 if [ ! -f "${DATAFED_INSTALL_PATH}/keys/$cert_file" ] || [ ! -f "${DATAFED_INSTALL_PATH}/keys/$key_file" ]
 then
-  sudo lego --accept-tos --email="$DATAFED_LEGO_EMAIL" --domains="$local_DATAFED_DOMAIN" --path "${DATAFED_INSTALL_PATH}/keys/" --tls run 
+  "$SUDO_CMD" lego --accept-tos --email="$DATAFED_LEGO_EMAIL" --domains="$local_DATAFED_DOMAIN" --path "${DATAFED_INSTALL_PATH}/keys/" --tls run 
   mv ${DATAFED_INSTALL_PATH}/keys/certificates/$cert_file ${DATAFED_INSTALL_PATH}/keys/
   mv ${DATAFED_INSTALL_PATH}/keys/certificates/$key_file  ${DATAFED_INSTALL_PATH}/keys/
   rm -rf ${DATAFED_INSTALL_PATH}/keys/certificates
diff --git a/scripts/install_python_client_dependencies.sh b/scripts/install_python_client_dependencies.sh
new file mode 100755
index 000000000..a8467a3f0
--- /dev/null
+++ b/scripts/install_python_client_dependencies.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# Exit on error
+set -e
+
+SCRIPT=$(realpath "$0")
+SOURCE=$(dirname "$SCRIPT")
+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 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 python3-venv libssl-dev
+
+cd ~
+install_cmake
+cd ~
+
+# Install cmake 3.17
+
+init_python
+source "${DATAFED_PYTHON_ENV}/bin/activate"
+python3 -m pip install --upgrade pip
+python3 -m pip install setuptools
+
+install_protobuf
+cd ~
+
+
diff --git a/scripts/install_repo_dependencies.sh b/scripts/install_repo_dependencies.sh
index 6580db90c..723a3ad81 100755
--- a/scripts/install_repo_dependencies.sh
+++ b/scripts/install_repo_dependencies.sh
@@ -7,10 +7,12 @@ SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
 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" "libzmq3-dev" "python3-pip")
-externals=("cmake" "protobuf" "libzmq")
+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
 
@@ -25,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
       ;;
     *)
@@ -34,13 +37,17 @@ if [ $# -eq 1 ]; then
   esac
 fi
 
+sudo_command
+
 if [[ $local_UNIFY = false ]]; then
-  sudo apt-get update
-  sudo dpkg --configure -a
-  sudo apt-get install -y "${packages[@]}"
+  "$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_ws.sh b/scripts/install_ws.sh
index 61d1a35e6..18b248cac 100755
--- a/scripts/install_ws.sh
+++ b/scripts/install_ws.sh
@@ -4,72 +4,27 @@ set -uf -o pipefail
 
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
-PROJECT_ROOT=$(realpath ${SOURCE}/..)
-source ${PROJECT_ROOT}/config/datafed.sh
-source ${SOURCE}/dependency_versions.sh
+PROJECT_ROOT=$(realpath "${SOURCE}/..")
+source "${PROJECT_ROOT}/config/datafed.sh"
+source "${SOURCE}/dependency_versions.sh"
+source "${SOURCE}/dependency_install_functions.sh"
 
 #NVM_DIR=/home/cades/.nvm
 #[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
-export NVM_DIR="/home/cades/.nvm"
-source ${NVM_DIR}/nvm.sh
+#export NVM_DIR="/home/cades/.nvm"
+#source ${NVM_DIR}/nvm.sh
 # Make sure paths exist
-mkdir -p ${DATAFED_INSTALL_PATH}/web
-mkdir -p ${DATAFED_INSTALL_PATH}/keys
-mkdir -p ${DATAFED_DEFAULT_LOG_PATH}
+mkdir -p "${DATAFED_INSTALL_PATH}/web"
+mkdir -p "${DATAFED_INSTALL_PATH}/keys"
+mkdir -p "${DATAFED_DEFAULT_LOG_PATH}"
 
 # Install web node modules
-cp "$PROJECT_ROOT/web/package.json" ${DATAFED_INSTALL_PATH}/web/
-
-nvm use $DATAFED_NODE_VERSION
-export npm_config_cache=${DATAFED_INSTALL_PATH}/web
-# Check if npm exists
-{
-  npm_path=$(which npm)
-} || {
-  echo "npm_path not found."
-}
-if [ -z "$npm_path" ]
-then
-
-  {
-    # Will return a result if nvm can be found and returns nothing otherwise
-    # which does not work with nvm
-    nvm_command_exists=$(command -v nvm)
-  } || {
-    echo "nvm_command_exists not found."
-  }
-
-  if [ -z "$nvm_path" ]
-  then
-    # Check for nvm in default location when installed with web dependencies
-    # script
-    if [ -d "$NVM_DIR" ]
-    then
-      [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
-    elif [ -d "$HOME/.nvm" ]
-    then
-      export NVM_DIR="$HOME/.nvm"
-      [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
-      {
-    	nvm_command_exists=$(command -v nvm)
-      } || {
-        echo "nvm_command_exists not found after sourcing."
-      }
-      if [ -z "$nvm_path" ]
-      then
-        echo "ERROR Unable to locate npm or nvm."
-        exit 1
-      fi
-    else
-      echo "ERROR Unable to locate npm or nvm."
-      exit 1
-    fi
-
-  fi
-  nvm use $DATAFED_NODE_VERSION
-fi
+cp "$PROJECT_ROOT/web/package.json" "${DATAFED_INSTALL_PATH}/web/"
+install_nvm
+install_node
+install_foxx_cli
 {
-  npm --allow-root --unsafe-perm --prefix ${DATAFED_INSTALL_PATH}/web install 
+  npm --allow-root --unsafe-perm --prefix "${DATAFED_INSTALL_PATH}/web" install 
 } || {
   echo "ERROR npm command failed!"
   exit 1
@@ -77,10 +32,10 @@ fi
 
 # Install javascript web server repo and core server were 
 # already installed by CMake
-cp "$PROJECT_ROOT/web/datafed-ws.js" ${DATAFED_INSTALL_PATH}/web
+cp "$PROJECT_ROOT/web/datafed-ws.js" "${DATAFED_INSTALL_PATH}/web"
 
 # Copy configuration files
-cp "$PROJECT_ROOT/config/datafed-ws.cfg" ${DATAFED_INSTALL_PATH}/web
+cp "$PROJECT_ROOT/config/datafed-ws.cfg" "${DATAFED_INSTALL_PATH}/web"
 
 # Make sure that the datafed-core-pub.key exists in the web/static folder
 if [[ -z  "${DATAFED_INSTALL_PATH}/keys/datafed-core-key.pub" ]]
@@ -89,4 +44,4 @@ then
   exit 1
 fi
 
-cp ${DATAFED_INSTALL_PATH}/keys/datafed-core-key.pub "$PROJECT_ROOT/web/static/"
+cp "${DATAFED_INSTALL_PATH}/keys/datafed-core-key.pub" "$DATAFED_INSTALL_PATH/web/static/"
diff --git a/scripts/install_ws_dependencies.sh b/scripts/install_ws_dependencies.sh
index 1d1d425a1..5f124a540 100755
--- a/scripts/install_ws_dependencies.sh
+++ b/scripts/install_ws_dependencies.sh
@@ -5,13 +5,14 @@ set -e
 
 SCRIPT=$(realpath "$0")
 SOURCE=$(dirname "$SCRIPT")
-PROJECT_ROOT=$(realpath ${SOURCE}/..)
+PROJECT_ROOT=$(realpath "${SOURCE}/..")
 
+source "${PROJECT_ROOT}/scripts/utils.sh"
 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()
 {
@@ -62,23 +63,14 @@ while [ : ]; do
   esac
 done
 
+sudo_command
+
 if [[ $local_UNIFY = false ]]; then
-  sudo apt-get update
-  sudo dpkg --configure -a
-  sudo apt-get install -y "${packages[@]}"
+  "$SUDO_CMD" apt-get update
+  "$SUDO_CMD" dpkg --configure -a
+  "$SUDO_CMD" apt-get install -y "${packages[@]}"
 
   for ext in "${externals[@]}"; do
     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/scripts/utils.sh b/scripts/utils.sh
new file mode 100755
index 000000000..dc1312d71
--- /dev/null
+++ b/scripts/utils.sh
@@ -0,0 +1,136 @@
+#!/bin/bash
+SCRIPT=$(realpath "$0")
+SOURCE=$(dirname "$SCRIPT")
+
+if [ -z "${PROJECT_ROOT}" ]
+then
+  PROJECT_ROOT=$(realpath "${SOURCE}/..")
+fi
+
+echo "PROJECT ROOT $PROJECT_ROOT"
+
+export_dependency_version_numbers() {
+    # Get the content of the function and remove comments
+    variables=$(cat "${PROJECT_ROOT}/scripts/dependency_versions.sh")
+
+    local content="$(echo "${variables}" | sed '/^$/d;/^#/d')"
+    
+    # Extract variable assignments from the content
+    local assignments=$(echo "$content" | grep -Eo '\b[a-zA-Z_][a-zA-Z_0-9]*="[^\"]*"')
+
+    echo "Variables are $variables"
+    echo "Content is $content"
+    echo "Assignments is $assignments"
+    # Loop through each assignment, export the variable
+    # Note: This may override existing variables
+    for assignment in $assignments; do
+        echo "export $assignment"
+        export "$assignment"
+    done
+}
+
+empty_command() {
+  "$@"
+}
+
+# The purpose of this function is to detect the sudo command
+# if it exists use it, if we are running as root set SUDO_CMD to empty_command
+# empty_command is needed so that I can do this where sudo doesn't exist
+#
+# "$SUDO_CMD" apt install curl
+#
+# If running as root this will expand to
+#
+# empty_command apt install curl
+#
+# which expands to
+#
+# apt install curl
+# 
+# If I left SUDO_CMD blank i.e. "" apt install curl bash would complain
+sudo_command() {
+  if [ "$(id -u)" -eq 0 ]; then
+      export SUDO_CMD="empty_command" # Ignore sudo running as root
+  else
+      # Check if sudo is available
+      if command -v sudo &> /dev/null; then
+          export SUDO_CMD=$(command -v sudo)
+          return 0
+      else
+          echo "Error: This script requires sudo but sudo is not installed." >&2
+          echo "You are not running as root!" >&2
+          exit 1
+      fi
+      exit $?  # Exit with the same status as the sudo command
+  fi
+}
+
+# Only recognized x.x.x format where all "x" are integers
+# Returns true if first version is greater or equal to second version
+#
+# semantic_version_compatible "1.2.3" "1.1.8"
+# echo $? 
+# Should print 1
+#
+# semantic_version_compatible "1.2.3" "1.2.8"
+# echo $? 
+# Should print 0
+#
+#semantic_version_compatible "1.1.1" "1.1.1"
+#echo "Should return true 1.1.1 >= 1.1.1"
+#
+#semantic_version_compatible "1.2.1" "1.1.1"
+#echo "Should return true 1.2.1 >= 1.1.1"
+#
+#semantic_version_compatible "1.2.1" "3.1.1"
+#echo "Should return false 1.2.1 >= 3.1.1"
+#
+#semantic_version_compatible "v1.2.1" "v3.1.1"
+#echo "Should return false v1.2.1 >= v3.1.1"
+#
+#semantic_version_compatible "v1.2.1" "1.1.1"
+#echo "Should return true v1.2.1 >= 1.1.1"
+
+
+semantic_version_compatible() {
+  local VER1="$1"
+  local VER2="$2"
+
+  # Remove any preceding v from version i.e. v1.1.2
+  VER1=$(echo "$VER1" | sed 's/v//g' )
+  VER2=$(echo "$VER2" | sed 's/v//g' )
+
+  maj_1=$(echo "$VER1" | sed 's/\./ /g' | awk '{print $1}')
+  min_1=$(echo "$VER1" | sed 's/\./ /g' | awk '{print $2}')
+  patch_1=$(echo "$VER1" | sed 's/\./ /g' | awk '{print $3}')
+  maj_2=$(echo "$VER2" | sed 's/\./ /g' | awk '{print $1}')
+  min_2=$(echo "$VER2" | sed 's/\./ /g' | awk '{print $2}')
+  patch_2=$(echo "$VER2" | sed 's/\./ /g' | awk '{print $3}')
+
+  if [ "$maj_1" -gt "$maj_2" ]
+  then
+    return 1
+  elif [ "$maj_1" -lt "$maj_2" ]
+  then
+    return 0
+  fi
+
+  if [ "$min_1" -gt "$min_2" ]
+  then
+    return 1
+  elif [ "$min_1" -lt "$min_2" ]
+  then
+    return 0
+  fi
+
+  if [ "$patch_1" -gt "$patch_2" ]
+  then
+    return 1
+  elif [ "$patch_1" -lt "$patch_2" ]
+  then
+    return 0
+  fi
+  return 1
+}
+
+
diff --git a/web/docker/Dockerfile b/web/docker/Dockerfile
index a8c3f1609..7e699672f 100644
--- a/web/docker/Dockerfile
+++ b/web/docker/Dockerfile
@@ -6,11 +6,9 @@ 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 ws-build
@@ -18,10 +16,9 @@ FROM ${DEPENDENCIES} AS ws-build
 ARG DATAFED_DIR
 ARG BUILD_DIR
 ARG DATAFED_INSTALL_PATH
-ARG NVM_DIR
-ARG NVM_INC
-ARG NVM_BIN
+ARG DATAFED_DEPENDENCIES_INSTALL_PATH
 
+ENV DATAFED_INSTALL_PATH="${DATAFED_INSTALL_PATH}"
 # This port is needed to communicate with the DataFed core server
 EXPOSE 7513
 # For communication with the public
@@ -32,12 +29,13 @@ COPY ./scripts/dependency_versions.sh ${BUILD_DIR}/scripts/
 COPY ./scripts/generate_datafed.sh    ${BUILD_DIR}/scripts/
 COPY ./scripts/generate_ws_config.sh  ${BUILD_DIR}/scripts/
 COPY ./scripts/install_ws.sh          ${BUILD_DIR}/scripts/
+COPY ./scripts/export_dependency_version.sh ${BUILD_DIR}/scripts/
 COPY ./cmake                          ${BUILD_DIR}/cmake
 COPY ./common/proto                   ${BUILD_DIR}/common/proto
 COPY ./web                            ${BUILD_DIR}/web
 
 RUN ${BUILD_DIR}/scripts/generate_datafed.sh && \
-	cmake -S. -B build						\
+	${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake -S. -B build						\
 		-DBUILD_REPO_SERVER=False		\
 		-DBUILD_AUTHZ=False					\
 		-DBUILD_CORE_SERVER=False		\
@@ -46,55 +44,46 @@ RUN ${BUILD_DIR}/scripts/generate_datafed.sh && \
 		-DBUILD_PYTHON_CLIENT=False	\
 		-DBUILD_FOXX=False					\
 		-DBUILD_COMMON=False
-RUN cmake --build build
-
-ENV NVM_DIR="$NVM_DIR"
-ENV NVM_INC="$NVM_INC"
-ENV NVM_BIN="$NVM_BIN"
-ENV PATH="$NVM_BIN:$PATH"
-
-RUN cmake --build build --target install
+RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build
+RUN ${DATAFED_DEPENDENCIES_INSTALL_PATH}/bin/cmake --build build --target install
+# Only part of the final install can be done here
 
 FROM ${RUNTIME} AS ws
 
-ARG DATAFED_NODE_VERSION=""
+SHELL ["/bin/bash", "-c"]
 ARG DATAFED_DIR
 ARG DATAFED_INSTALL_PATH
 ARG BUILD_DIR
-ARG NVM_DIR
-ARG NVM_INC
-ARG NVM_BIN
+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_DIR="$DATAFED_DIR"
 ENV            BUILD_DIR="$BUILD_DIR"
-ENV              NVM_DIR="$NVM_DIR"
-ENV              NVM_INC="$NVM_INC"
-ENV              NVM_BIN="$NVM_BIN"
-ENV                 PATH="$NVM_BIN:$PATH"
+ENV          DATAFED_DEFAULT_LOG_PATH="$DATAFED_INSTALL_PATH/logs"
 
 RUN apt install -y python3 make g++
 
 WORKDIR ${DATAFED_DIR}
 
-COPY --from=ws-build --chown=datafed:root "$NVM_DIR" "$NVM_DIR" 
-RUN ln -s ${DATAFED_INSTALL_PATH}/web ${DATAFED_DIR}/web
-
 USER datafed
 
-COPY --chown=datafed:root ./web/docker/entrypoint.sh       ${BUILD_DIR}/web/entrypoint.sh
-COPY --chown=datafed:root ./scripts/generate_datafed.sh    ${DATAFED_DIR}/scripts/generate_datafed.sh
-COPY --chown=datafed:root ./scripts/dependency_versions.sh ${DATAFED_DIR}/scripts/dependency_versions.sh
-COPY --chown=datafed:root ./scripts/generate_ws_config.sh  ${DATAFED_DIR}/scripts/generate_ws_config.sh
-COPY --chown=datafed:root ./scripts/install_ws.sh          ${DATAFED_DIR}/scripts/install_ws.sh
-COPY --chown=datafed:root ./cmake/Version.cmake            ${DATAFED_DIR}/cmake/Version.cmake
+COPY --chown=datafed:root ./scripts/generate_datafed.sh          ${BUILD_DIR}/scripts/generate_datafed.sh
+COPY --chown=datafed:root ./scripts/dependency_versions.sh       ${BUILD_DIR}/scripts/dependency_versions.sh
+COPY --chown=datafed:root ./scripts/dependency_install_functions.sh ${BUILD_DIR}/scripts/dependency_install_functions.sh
+COPY --chown=datafed:root ./scripts/generate_ws_config.sh        ${BUILD_DIR}/scripts/generate_ws_config.sh
+COPY --chown=datafed:root ./scripts/install_ws.sh                ${BUILD_DIR}/scripts/install_ws.sh
+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}/web ${DATAFED_INSTALL_PATH}/web
+COPY --from=ws-build --chown=datafed:root /usr/bin/curl /usr/bin/curl
 
-COPY --from=ws-build --chown=datafed:root ${BUILD_DIR}/web/package.json ${DATAFED_INSTALL_PATH}/web/package.json
-RUN . ${DATAFED_DIR}/scripts/dependency_versions.sh &&					\
-	. ${DATAFED_DIR}/.nvm/nvm.sh &&							\
-	npm --allow-root --unsafe-perm --prefix ${DATAFED_INSTALL_PATH}/web install
+WORKDIR ${BUILD_DIR}
 
-COPY --from=ws-build --chown=datafed:root ${BUILD_DIR}/web ${DATAFED_INSTALL_PATH}/web
+USER root
 
-WORKDIR ${DATAFED_INSTALL_PATH}/web
+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 b504c455f..c50ad329c 100644
--- a/web/docker/Dockerfile.web-base.ubuntu
+++ b/web/docker/Dockerfile.web-base.ubuntu
@@ -2,7 +2,14 @@ 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}"
+
+# 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
 
@@ -12,10 +19,20 @@ COPY ./scripts/generate_datafed.sh ${BUILD_DIR}/scripts/
 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}/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 ff4c5c0f5..f86bc172e 100644
--- a/web/docker/Dockerfile.web.ubuntu
+++ b/web/docker/Dockerfile.web.ubuntu
@@ -1,4 +1,6 @@
-FROM camden.ornl.gov/datafed/ws-base:latest as build
+ARG BASE_WEB_IMAGE="camden.ornl.gov/datafed/ws-base:latest"
+
+FROM "$BASE_WEB_IMAGE" as build
 
 ARG DATAFED_DIR="/datafed"
 ARG BUILD_DIR="/datafed/source"
@@ -8,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="${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
@@ -44,23 +48,26 @@ 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
-ENV PATH=$DATAFED_DIR/.nvm/versions/node/v13.14.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
-ENV NVM_BIN=$DATAFED_DIR/.nvm/versions/node/v13.14.0/bin
+#ENV NVM_DIR="$DATAFED_DIR/.nvm"
+#ENV NVM_INC=$DATAFED_DIR/.nvm/versions/node/v13.14.0/include/node
+#ENV PATH=$DATAFED_DIR/.nvm/versions/node/v13.14.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+#ENV NVM_BIN=$DATAFED_DIR/.nvm/versions/node/v13.14.0/bin
 
 # 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
 
+USER root
+
 ENTRYPOINT ["/datafed/source/web/docker/entrypoint.sh"]
 CMD ["/datafed/install/web/datafed-ws.js","/datafed/install/web/datafed-ws.cfg"]
diff --git a/web/docker/entrypoint.sh b/web/docker/entrypoint.sh
index e7a33bf6a..a4048bc3b 100755
--- a/web/docker/entrypoint.sh
+++ b/web/docker/entrypoint.sh
@@ -1,20 +1,48 @@
 #!/bin/bash
-
 # NVM_DIR must be defined
 
 set -euf -o pipefail
 
+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")
-PROJECT_ROOT=$(realpath ${SOURCE}/../..)
+PROJECT_ROOT=$(realpath "${SOURCE}/../..")
 
-source $NVM_DIR/nvm.sh
-${PROJECT_ROOT}/scripts/generate_datafed.sh
-${PROJECT_ROOT}/scripts/generate_ws_config.sh
-${PROJECT_ROOT}/scripts/install_ws.sh
+"${PROJECT_ROOT}/scripts/generate_datafed.sh"
+"${PROJECT_ROOT}/scripts/generate_ws_config.sh"
+. "${PROJECT_ROOT}/scripts/dependency_versions.sh"
 
+export NVM_DIR="${DATAFED_DEPENDENCIES_INSTALL_PATH}/nvm"
+export NODE_VERSION="$DATAFED_NODE_VERSION"
+[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
+
+echo "NODE VERSION $NODE_VERSION"
+echo "Which node"
+NODE_PATH=$(su datafed -c ". ${NVM_DIR}/nvm.sh; nvm which node")
+# Do this as root
+if [ ! -L "/usr/local/bin/node" ]; then
+  ln -s "$NODE_PATH" /usr/local/bin/node
+fi
 # Send output to file as well as print to terminal
-log_path=$(grep "log-path" /datafed/install/web/datafed-ws.cfg | cut -d "=" -f 2 | tr -d ' ')
+log_path=$(grep "log-path" "${BUILD_DIR}/config/datafed-ws.cfg" | cut -d "=" -f 2 | tr -d ' ')
+
+if [ ! -d "${log_path}" ]
+then
+  su -c "mkdir -p ${log_path}" datafed
+fi
+
+if [ ! -f "${DATAFED_INSTALL_PATH}/keys/datafed-core-key.pub" ]
+then
+  echo "datafed-core-key.pub not found"
+  exit 1
+fi
+cp "${DATAFED_INSTALL_PATH}/keys/datafed-core-key.pub" "${BUILD_DIR}/web/static"
 
 if [ "$#" -eq 0 ]; then
   echo "No arguments were passed, running bash"
@@ -22,13 +50,15 @@ if [ "$#" -eq 0 ]; then
   exit 0
 fi
 
+cd "$DATAFED_INSTALL_PATH/web"
 datafed_ws_exec=$(basename "$1")
 if [ "${datafed_ws_exec}" = "datafed-ws.js" ]
 then
   # Send output to log file
-  "$@"  2>&1 | tee "$log_path/datafed-ws.log"
+  su datafed -c '"$@"' -- argv0 "$@" 2>&1 | su datafed -c "tee $log_path/datafed-ws.log"
 else
   echo "Not sending output to datafed-ws.log"
   # If not do not by default send to log file
-  exec "$@"
+  su datafed -c '"$@"' -- argv0 "$@"
 fi
+


Test patch:
diff --git a/common/tests/security/tcp_secure/CMakeLists.txt b/common/tests/security/tcp_secure/CMakeLists.txt
index 8fffa9d64..bebb9fa40 100644
--- a/common/tests/security/tcp_secure/CMakeLists.txt
+++ b/common/tests/security/tcp_secure/CMakeLists.txt
@@ -1,12 +1,23 @@
 
 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 PkgConfig::PkgConfig_ZMQ ${Protobuf_LIBRARIES} 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 PkgConfig::PkgConfig_ZMQ ${Protobuf_LIBRARIES} 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 52fb4b748..5e318b6d9 100644
--- a/common/tests/unit/CMakeLists.txt
+++ b/common/tests/unit/CMakeLists.txt
@@ -18,8 +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} ${Boost_LIBRARIES} common PkgConfig::PkgConfig_ZMQ ${Protobuf_LIBRARIES} 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/scripts/container_run_test.sh b/scripts/container_run_test.sh
index 28702bfec..576b6fae8 100755
--- a/scripts/container_run_test.sh
+++ b/scripts/container_run_test.sh
@@ -73,20 +73,20 @@ while [ : ]; do
   esac
 done
 
-if [ "$local_TAG_FLAG_DETECTED" = "1" ] && [ "$local_NAME_FLAG_DETECTED" = "1" ]
+if [ "$local_TAG_FLAG_DETECTED" == "1" ] && [ "$local_NAME_FLAG_DETECTED" == "1" ]
 then
   echo "ERROR can only specify -t or -n cannot specify both"
   exit 1
 fi
 
-if [ "$local_TAG_FLAG_DETECTED" = "0" ] && [ "$local_NAME_FLAG_DETECTED" = "0" ]
+if [ "$local_TAG_FLAG_DETECTED" == "0" ] && [ "$local_NAME_FLAG_DETECTED" == "0" ]
 then
   echo "ERROR must specify an image tag with '-t' or a name '-n' cannot run without"
   exit 1
 fi
 
 
-if [ "$local_TAG_FLAG_DETECTED" = "1" ]
+if [ "$local_TAG_FLAG_DETECTED" == "1" ]
 then
   # Get all container ids with that tag and convert to bash array
   CONTAINER_IDS=($(docker container ls --format "{{.ID}}"))
@@ -97,16 +97,16 @@ then
   local_count=0
   for (( i=0; i<${#CONTAINER_IDS[@]}; i++)); do
     IMAGE="${CONTAINER_IMAGES[$i]}"
-    if [ "$IMAGE" = "$local_CONTAINER_TAG" ]
+    if [ "$IMAGE" == "$local_CONTAINER_TAG" ]
     then
-      if [ "${CONTAINER_STATES[$i]}" = "running" ]
+      if [ "${CONTAINER_STATES[$i]}" == "running" ]
       then
         local_count=$(( $local_count + 1 )) 
       fi
     fi
   done
 
-  if [ ! "$local_count" = "$local_CONTAINER_COUNT" ]
+  if [ ! "$local_count" == "$local_CONTAINER_COUNT" ]
   then
     echo "ERROR expected $local_CONTAINER_COUNT containers to be running with container image_tag:$local_CONTAINER_TAG but $local_count found instead."
     echo
@@ -114,7 +114,7 @@ then
       echo "${CONTAINER_IDS[$i]} ${CONTAINER_IMAGES[$i]} ${CONTAINER_NAMES[$i]} ${CONTAINER_STATES[$i]}"
     done
 
-    if [ "$local_ERROR_THROW" = "1" ]
+    if [ "$local_ERROR_THROW" == "1" ]
     then
       exit 1
     else
@@ -124,7 +124,7 @@ then
 
   echo "Success: $local_CONTAINER_COUNT instances of container image_tag:$local_CONTAINER_TAG found running"
 
-elif [ "$local_NAME_FLAG_DETECTED" = "1" ] 
+elif [ "$local_NAME_FLAG_DETECTED" == "1" ] 
 then
 
   # Get all container ids with that tag and convert to bash array
@@ -136,16 +136,16 @@ then
   local_count=0
   for (( i=0; i<${#CONTAINER_IDS[@]}; i++)); do
     NAME="${CONTAINER_NAMES[$i]}"
-    if [ "$NAME" = "$local_CONTAINER_NAME" ]
+    if [ "$NAME" == "$local_CONTAINER_NAME" ]
     then
-      if [ "${CONTAINER_STATES[$i]}" = "running" ]
+      if [ "${CONTAINER_STATES[$i]}" == "running" ]
       then
         local_count=$(( $local_count + 1 )) 
       fi
     fi
   done
 
-  if [ ! "$local_count" = "$local_CONTAINER_COUNT" ]
+  if [ ! "$local_count" == "$local_CONTAINER_COUNT" ]
   then
     echo "ERROR expected $local_CONTAINER_COUNT containers to be running with container name:$local_CONTAINER_NAME but $local_count found instead."
     echo
@@ -153,7 +153,7 @@ then
       echo "${CONTAINER_IDS[$i]} ${CONTAINER_IMAGES[$i]} ${CONTAINER_NAMES[$i]} ${CONTAINER_STATES[$i]}"
     done
 
-    if [ "$local_ERROR_THROW" = "1" ]
+    if [ "$local_ERROR_THROW" == "1" ]
     then
       exit 1
     else
diff --git a/tests/end-to-end/README.md b/tests/end-to-end/README.md
index c9ef53eeb..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 set to 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
 
diff --git a/tests/end-to-end/setup.sh b/tests/end-to-end/setup.sh
index 2e35dbef1..8f748a7ec 100755
--- a/tests/end-to-end/setup.sh
+++ b/tests/end-to-end/setup.sh
@@ -33,6 +33,17 @@ set -ef -o pipefail
 # 9. DATAFED_REPO_FORM_PATH - path to the repo form, this is the repo form that is available as a bash script and can be generated using the /scripts/globus/generate_repo_form.sh script with the --generate-script flag. The form will contain the environmental variables needed to create teh repository in datafed
 
 # Check that required env variables have been set
+
+local_DATABASE_NAME="sdms"
+local_DATABASE_USER="root"
+
+if [ -z "${DATAFED_DATABASE_PASSWORD}" ]
+then
+  local_DATAFED_DATABASE_PASSWORD=""
+else
+  local_DATAFED_DATABASE_PASSWORD=$(printenv DATAFED_DATABASE_PASSWORD)
+fi
+
 local_DATAFED_USER89_PASSWORD=""
 if [ -z "${DATAFED_USER89_PASSWORD}" ]
 then
@@ -120,11 +131,13 @@ then
 fi
 
 # First step is to clear the database
+echo "Clearing old database"
 ${PROJECT_ROOT}/scripts/clear_db.sh
 
 # Second install foxx
+echo "Installing foxx services and API"
 ${PROJECT_ROOT}/scripts/install_foxx.sh
-
+echo "Completed"
 
 if [ -z "${DATAFED_DATABASE_HOST}" ]
 then
@@ -133,30 +146,41 @@ else
   local_DATAFED_DATABASE_HOST=$(printenv DATAFED_DATABASE_HOST)
 fi
 
-#curl -X GET http://127.0.0.1:8529/_db/sdms/
+if [ -z "${DATAFED_DATABASE_PORT}" ]
+then
+  local_DATAFED_DATABASE_PORT="8529"
+else
+  local_DATAFED_DATABASE_PORT=$(printenv DATAFED_DATABASE_PORT)
+fi
+
+
+# If the database was set up correctly auth will be turned on
+basic_auth="$local_DATABASE_USER:$local_DATAFED_DATABASE_PASSWORD"
 
 echo "IP is ${local_DATAFED_DATABASE_HOST}"
 echo "USER89 GLobud ID $DATAFED_USER89_GLOBUS_UUID"
-echo "Refresh is ${DATAFED_USER89_REFRESH_TOKEN}"
+echo "Refresh is ${DATAFED_USER89_GLOBUS_REFRESH_TOKEN}"
 # Chreate user datafed89 who is admin
-HTTP_CODE=$( curl -w "%{http_code}" -o /dev/null -X GET "http://${local_DATAFED_DATABASE_HOST}:8529/_db/sdms/api/${local_FOXX_MAJOR_API_VERSION}/usr/create?name=Data%20Fed&uid=datafed89&uuids=%5B\"${DATAFED_USER89_GLOBUS_UUID}\"%5D&password=${local_DATAFED_USER89_PASSWORD}&email=datafed89%40gmail.com&is_admin=true&secret=${DATAFED_ZEROMQ_SYSTEM_SECRET}" )
+HTTP_CODE=$( curl --user "${basic_auth}" -w "%{http_code}" -o /dev/null -X GET
+"http://${local_DATAFED_DATABASE_HOST}:${local_DATABASE_PORT}/_db/${local_DATABASE_NAME}/api/${local_FOXX_MAJOR_API_VERSION}/usr/create?name=Data%20Fed&uid=datafed89&uuids=%5B\"${DATAFED_USER89_GLOBUS_UUID}\"%5D&password=${local_DATAFED_USER89_PASSWORD}&email=datafed89%40gmail.com&is_admin=true&secret=${DATAFED_ZEROMQ_SYSTEM_SECRET}" )
 echo "HTTP_CODE: ${HTTP_CODE}"
 FIRST_INT=${HTTP_CODE:0:1}
 if [ "${FIRST_INT}" -ne "2" ]
 then
-  response=$( curl -X GET "http://${local_DATAFED_DATABASE_HOST}:8529/_db/sdms/api/${local_FOXX_MAJOR_API_VERSION}/usr/create?name=Data%20Fed&uid=datafed89&uuids=%5B\"${DATAFED_USER89_GLOBUS_UUID}\"%5D&password=${local_DATAFED_USER89_PASSWORD}&email=datafed89%40gmail.com&is_admin=true&secret=${DATAFED_ZEROMQ_SYSTEM_SECRET}" )
+  response=$( curl --user "${basic_auth}" -X GET
+  "http://${local_DATAFED_DATABASE_HOST}:${local_DATABASE_PORT}/_db/${local_DATABASE_NAME}/api/${local_FOXX_MAJOR_API_VERSION}/usr/create?name=Data%20Fed&uid=datafed89&uuids=%5B\"${DATAFED_USER89_GLOBUS_UUID}\"%5D&password=${local_DATAFED_USER89_PASSWORD}&email=datafed89%40gmail.com&is_admin=true&secret=${DATAFED_ZEROMQ_SYSTEM_SECRET}" )
   CODE=$(echo $response | jq .code )
   ERROR_MSG=$(echo $response | jq .errorMessage )
   echo "$ERROR_MSG"
   exit 1
 fi
 # Set globus tokens
-HTTP_CODE=$(curl  -w "%{http_code}" -o /dev/null  -X GET "http://${local_DATAFED_DATABASE_HOST}:8529/_db/sdms/api/${local_FOXX_MAJOR_API_VERSION}/usr/token/set?client=u%2Fdatafed89&access=${DATAFED_USER89_GLOBUS_ACCESS_TOKEN}&refresh=${DATAFED_USER89_GLOBUS_REFRESH_TOKEN}&expires_in=1")
+HTTP_CODE=$(curl --user "${basic_auth}" -w "%{http_code}" -o /dev/null  -X GET "http://${local_DATAFED_DATABASE_HOST}:${local_DATABASE_PORT}/_db/${local_DATABASE_NAME}/api/${local_FOXX_MAJOR_API_VERSION}/usr/token/set?client=u%2Fdatafed89&access=${DATAFED_USER89_GLOBUS_ACCESS_TOKEN}&refresh=${DATAFED_USER89_GLOBUS_REFRESH_TOKEN}&expires_in=1")
 echo "HTTP_CODE: ${HTTP_CODE}"
 FIRST_INT=${HTTP_CODE:0:1}
 if [ "${FIRST_INT}" -ne "2" ]
 then
-  response=$(curl --fail-early -X GET "http://${local_DATAFED_DATABASE_HOST}:8529/_db/sdms/api/${local_FOXX_MAJOR_API_VERSION}/usr/token/set?client=u%2Fdatafed89&access=${DATAFED_USER89_GLOBUS_ACCESS_TOKEN}&refresh=${DATAFED_USER89_GLOBUS_REFRESH_TOKEN}&expires_in=1")
+  response=$(curl --user "${basic_auth}" --fail-early -X GET "http://${local_DATAFED_DATABASE_HOST}:${local_DATABASE_PORT}/_db/${local_DATABASE_NAME}/api/${local_FOXX_MAJOR_API_VERSION}/usr/token/set?client=u%2Fdatafed89&access=${DATAFED_USER89_GLOBUS_ACCESS_TOKEN}&refresh=${DATAFED_USER89_GLOBUS_REFRESH_TOKEN}&expires_in=1")
   CODE=$(echo $response | jq .code )
   ERROR_MSG=$(echo $response | jq .errorMessage )
   echo "$ERROR_MSG"
@@ -164,24 +188,24 @@ then
 fi
 
 # Create user datafed99 who is not admin
-HTTP_CODE=$(curl  -w "%{http_code}" -o /dev/null  -X GET "http://${local_DATAFED_DATABASE_HOST}:8529/_db/sdms/api/${local_FOXX_MAJOR_API_VERSION}/usr/create?name=Data%20Fed&uid=datafed99&uuids=%5B\"${DATAFED_USER99_GLOBUS_UUID}\"%5D&password=${local_DATAFED_USER99_PASSWORD}&email=datafed99%40gmail.com&is_admin=false&secret=${DATAFED_ZEROMQ_SYSTEM_SECRET}")
+HTTP_CODE=$(curl  --user "${basic_auth}"  -w "%{http_code}" -o /dev/null  -X GET "http://${local_DATAFED_DATABASE_HOST}:${local_DATABASE_PORT}/_db/${local_DATABASE_NAME}/api/${local_FOXX_MAJOR_API_VERSION}/usr/create?name=Data%20Fed&uid=datafed99&uuids=%5B\"${DATAFED_USER99_GLOBUS_UUID}\"%5D&password=${local_DATAFED_USER99_PASSWORD}&email=datafed99%40gmail.com&is_admin=false&secret=${DATAFED_ZEROMQ_SYSTEM_SECRET}")
 echo "HTTP_CODE: ${HTTP_CODE}"
 FIRST_INT=${HTTP_CODE:0:1}
 if [ "${FIRST_INT}" -ne "2" ]
 then
-  response=$(curl --fail-early -X GET "http://${local_DATAFED_DATABASE_HOST}:8529/_db/sdms/api/${local_FOXX_MAJOR_API_VERSION}/usr/create?name=Data%20Fed&uid=datafed99&uuids=%5B\"${DATAFED_USER99_GLOBUS_UUID}\"%5D&password=${local_DATAFED_USER99_PASSWORD}&email=datafed99%40gmail.com&is_admin=false&secret=${DATAFED_ZEROMQ_SYSTEM_SECRET}")
+  response=$(curl  --user "${basic_auth}" --fail-early -X GET "http://${local_DATAFED_DATABASE_HOST}:${local_DATABASE_PORT}/_db/${local_DATABASE_NAME}/api/${local_FOXX_MAJOR_API_VERSION}/usr/create?name=Data%20Fed&uid=datafed99&uuids=%5B\"${DATAFED_USER99_GLOBUS_UUID}\"%5D&password=${local_DATAFED_USER99_PASSWORD}&email=datafed99%40gmail.com&is_admin=false&secret=${DATAFED_ZEROMQ_SYSTEM_SECRET}")
   CODE=$(echo $response | jq .code )
   ERROR_MSG=$(echo $response | jq .errorMessage )
   echo "$ERROR_MSG"
   exit 1
 fi
 # Set globus tokens
-HTTP_CODE=$(curl  -w "%{http_code}" -o /dev/null  -X GET "http://${local_DATAFED_DATABASE_HOST}:8529/_db/sdms/api/${local_FOXX_MAJOR_API_VERSION}/usr/token/set?client=u%2Fdatafed99&access=${DATAFED_USER99_GLOBUS_ACCESS_TOKEN}&refresh=${DATAFED_USER99_GLOBUS_REFRESH_TOKEN}&expires_in=1")
+HTTP_CODE=$(curl --user "${basic_auth}"   -w "%{http_code}" -o /dev/null  -X GET "http://${local_DATAFED_DATABASE_HOST}:${local_DATABASE_PORT}/_db/${local_DATABASE_NAME}/api/${local_FOXX_MAJOR_API_VERSION}/usr/token/set?client=u%2Fdatafed99&access=${DATAFED_USER99_GLOBUS_ACCESS_TOKEN}&refresh=${DATAFED_USER99_GLOBUS_REFRESH_TOKEN}&expires_in=1")
 echo "HTTP_CODE: ${HTTP_CODE}"
 FIRST_INT=${HTTP_CODE:0:1}
 if [ "${FIRST_INT}" -ne "2" ]
 then
-  response=$(curl --fail-early -X GET "http://${local_DATAFED_DATABASE_HOST}:8529/_db/sdms/api/${local_FOXX_MAJOR_API_VERSION}/usr/token/set?client=u%2Fdatafed99&access=${DATAFED_USER99_GLOBUS_ACCESS_TOKEN}&refresh=${DATAFED_USER99_GLOBUS_REFRESH_TOKEN}&expires_in=1")
+  response=$(curl --user "${basic_auth}"  --fail-early -X GET "http://${local_DATAFED_DATABASE_HOST}:${local_DATABASE_PORT}/_db/${local_DATABASE_NAME}/api/${local_FOXX_MAJOR_API_VERSION}/usr/token/set?client=u%2Fdatafed99&access=${DATAFED_USER99_GLOBUS_ACCESS_TOKEN}&refresh=${DATAFED_USER99_GLOBUS_REFRESH_TOKEN}&expires_in=1")
   CODE=$(echo $response | jq .code )
   ERROR_MSG=$(echo $response | jq .errorMessage )
   echo "$ERROR_MSG"