Repository: NeonGeckoCom/neon-diana-utils (language: python)

Issue:
Update GH Automation
# Description
Update release flow to new shared workflows
Add license tests
Update unit test Python version coverage

# Issues
Replaces #20

# Other Notes
<!-- Note any breaking changes, WIP changes, requests for input, etc. here -->


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/propose_release.yml b/.github/workflows/propose_release.yml
new file mode 100644
index 00000000..eeb6de66
--- /dev/null
+++ b/.github/workflows/propose_release.yml
@@ -0,0 +1,29 @@
+name: Propose Stable Release
+on:
+  workflow_dispatch:
+    inputs:
+      release_type:
+        type: choice
+        description: Release Type
+        options:
+          - patch
+          - minor
+          - major
+jobs:
+  update_version:
+    uses: neongeckocom/.github/.github/workflows/propose_semver_release.yml@master
+    with:
+      branch: dev
+      release_type: ${{ inputs.release_type }}
+      update_changelog: True
+      version_file: "neon_diana_utils/version.py"
+      on_version_change: "scripts/sync_chart_app_version.py"
+  pull_changes:
+    uses: neongeckocom/.github/.github/workflows/pull_master.yml@master
+    needs: update_version
+    with:
+      pr_reviewer: neonreviewers
+      pr_assignee: ${{ github.actor }}
+      pr_draft: false
+      pr_title: ${{ needs.update_version.outputs.version }}
+      pr_body: ${{ needs.update_version.outputs.changelog }}
\ No newline at end of file
diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml
index 65ee102b..96a03579 100644
--- a/.github/workflows/publish_release.yml
+++ b/.github/workflows/publish_release.yml
@@ -7,34 +7,6 @@ on:
       - master
 
 jobs:
-  tag_release:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - name: Get Version
-        run: |
-          VERSION=$(python setup.py --version)
-          echo "VERSION=${VERSION}" >> $GITHUB_ENV
-      - uses: ncipollo/release-action@v1
-        with:
-          token: ${{secrets.GITHUB_TOKEN}}
-          tag: ${{env.VERSION}}
-  build_and_publish:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - name: Setup Python
-        uses: actions/setup-python@v1
-        with:
-          python-version: 3.8
-      - name: Install Build Tools
-        run: |
-          python -m pip install build wheel
-
-      - name: Build Distribution Packages
-        run: |
-          python setup.py bdist_wheel
-      - name: Publish to Test PyPI
-        uses: pypa/gh-action-pypi-publish@master
-        with:
-          password: ${{secrets.PYPI_TOKEN}}
\ No newline at end of file
+  build_and_publish_pypi_and_release:
+    uses: neongeckocom/.github/.github/workflows/publish_stable_release.yml@master
+    secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/pull_master.yml b/.github/workflows/pull_master.yml
deleted file mode 100644
index 8e9c5a87..00000000
--- a/.github/workflows/pull_master.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-# This workflow will generate a PR for changes in cert into master
-
-name: Pull to Master
-on:
-  push:
-    branches:
-      - dev
-  workflow_dispatch:
-
-jobs:
-  pull_changes:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - name: pull-request-action
-        uses: repo-sync/pull-request@v2
-        with:
-          github_token: ${{ secrets.GITHUB_TOKEN }}
-          pr_reviewer: 'neonreviewers'
-          pr_assignee: 'neondaniel'
-          pr_draft: true
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..21086c40
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,131 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+*.lock
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..d5f2dd71
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,125 @@
+# Changelog
+
+## [0.0.6a17](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a17) (2023-07-04)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a16...0.0.6a17)
+
+**Merged pull requests:**
+
+- Fix typo in release action [\#38](https://github.com/NeonGeckoCom/neon-diana-utils/pull/38) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.6a16](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a16) (2023-07-04)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a15...0.0.6a16)
+
+**Implemented enhancements:**
+
+- RabbitMQ Authentication \(LDAP\) [\#3](https://github.com/NeonGeckoCom/neon-diana-utils/issues/3)
+
+**Merged pull requests:**
+
+- Remove redundant release tag action in automation [\#37](https://github.com/NeonGeckoCom/neon-diana-utils/pull/37) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.6a15](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a15) (2023-07-01)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a14...0.0.6a15)
+
+**Merged pull requests:**
+
+- Fix metrics pvc mount [\#35](https://github.com/NeonGeckoCom/neon-diana-utils/pull/35) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.6a14](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a14) (2023-06-30)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a13...0.0.6a14)
+
+**Closed issues:**
+
+- TTS and Translate Docker Support [\#14](https://github.com/NeonGeckoCom/neon-diana-utils/issues/14)
+
+**Merged pull requests:**
+
+- Re-implement Docker Compose support [\#34](https://github.com/NeonGeckoCom/neon-diana-utils/pull/34) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.6a13](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a13) (2023-06-30)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a12...0.0.6a13)
+
+**Merged pull requests:**
+
+- Update Diana README with new documentation for 0.0.6a10 [\#31](https://github.com/NeonGeckoCom/neon-diana-utils/pull/31) ([erikabarcott](https://github.com/erikabarcott))
+
+## [0.0.6a12](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a12) (2023-06-30)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a11...0.0.6a12)
+
+**Implemented enhancements:**
+
+- Kubernetes Support [\#2](https://github.com/NeonGeckoCom/neon-diana-utils/issues/2)
+
+**Merged pull requests:**
+
+- Refactor module to remove deprecated code and simplify structure [\#33](https://github.com/NeonGeckoCom/neon-diana-utils/pull/33) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.6a11](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a11) (2023-06-30)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a10...0.0.6a11)
+
+**Merged pull requests:**
+
+- Implement FastChat Deployment [\#32](https://github.com/NeonGeckoCom/neon-diana-utils/pull/32) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.6a10](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a10) (2023-06-06)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a9...0.0.6a10)
+
+**Merged pull requests:**
+
+- Add ChatGPT deployment [\#28](https://github.com/NeonGeckoCom/neon-diana-utils/pull/28) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.6a9](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a9) (2023-06-06)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a8...0.0.6a9)
+
+**Merged pull requests:**
+
+- Automate Chart versioning [\#30](https://github.com/NeonGeckoCom/neon-diana-utils/pull/30) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.6a8](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a8) (2023-06-01)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a7...0.0.6a8)
+
+**Merged pull requests:**
+
+- Fix typo in Helm Charts [\#29](https://github.com/NeonGeckoCom/neon-diana-utils/pull/29) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.6a7](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a7) (2023-05-03)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.6a6...0.0.6a7)
+
+**Merged pull requests:**
+
+- Helm charts [\#25](https://github.com/NeonGeckoCom/neon-diana-utils/pull/25) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.6a6](https://github.com/NeonGeckoCom/neon-diana-utils/tree/0.0.6a6) (2023-05-01)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-diana-utils/compare/0.0.5...0.0.6a6)
+
+**Merged pull requests:**
+
+- Fix path error in setup.py [\#27](https://github.com/NeonGeckoCom/neon-diana-utils/pull/27) ([NeonDaniel](https://github.com/NeonDaniel))
+- Update GH Automation [\#26](https://github.com/NeonGeckoCom/neon-diana-utils/pull/26) ([NeonDaniel](https://github.com/NeonDaniel))
+- Add method to generate TCP service config [\#24](https://github.com/NeonGeckoCom/neon-diana-utils/pull/24) ([NeonDaniel](https://github.com/NeonDaniel))
+- Add util to add Github auth to Kubernetes [\#23](https://github.com/NeonGeckoCom/neon-diana-utils/pull/23) ([NeonDaniel](https://github.com/NeonDaniel))
+- Added mimic2 containers to service\_mappings.yml [\#21](https://github.com/NeonGeckoCom/neon-diana-utils/pull/21) ([NeonDaniel](https://github.com/NeonDaniel))
+- Refactor and add HTTP Backend Services [\#19](https://github.com/NeonGeckoCom/neon-diana-utils/pull/19) ([NeonDaniel](https://github.com/NeonDaniel))
+- Increment version to 0.0.5 [\#17](https://github.com/NeonGeckoCom/neon-diana-utils/pull/17) ([NeonDaniel](https://github.com/NeonDaniel))
+- Kubernetes Support [\#16](https://github.com/NeonGeckoCom/neon-diana-utils/pull/16) ([NeonDaniel](https://github.com/NeonDaniel))
+- Increment version to 0.0.3 [\#10](https://github.com/NeonGeckoCom/neon-diana-utils/pull/10) ([NeonDaniel](https://github.com/NeonDaniel))
+- Fix version.py path spec in workflow [\#9](https://github.com/NeonGeckoCom/neon-diana-utils/pull/9) ([NeonDaniel](https://github.com/NeonDaniel))
+- Updated Docker files [\#8](https://github.com/NeonGeckoCom/neon-diana-utils/pull/8) ([NeonDaniel](https://github.com/NeonDaniel))
+- Increment version to 0.0.2 [\#7](https://github.com/NeonGeckoCom/neon-diana-utils/pull/7) ([NeonDaniel](https://github.com/NeonDaniel))
+- Add installation instructions to README [\#5](https://github.com/NeonGeckoCom/neon-diana-utils/pull/5) ([NeonDaniel](https://github.com/NeonDaniel))
+
+
+
+\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
diff --git a/Docker_Deployment.md b/Docker_Deployment.md
new file mode 100644
index 00000000..eda5dd50
--- /dev/null
+++ b/Docker_Deployment.md
@@ -0,0 +1,19 @@
+# Deployment via Docker Compose
+[Docker Compose](https://docs.docker.com/compose/) is a standard tool used for
+deploying multiple containers.
+
+### Diana Backend
+To generate configuration files for deployment in ~/diana, run: 
+```
+diana configure-backend -o docker-compose /home/$USER/diana
+```
+Follow the interactive prompts to configure keys for services you want to run.
+To run the output configured containers:
+```
+cd ~/diana
+docker-compose up -d
+```
+
+> Note that default backend services will be included in the output 
+> `docker-compose.yml`. You may wish to remove some services that you didn't
+> configure or add others to run with the backend.
\ No newline at end of file
diff --git a/Helm_Deployment.md b/Helm_Deployment.md
new file mode 100644
index 00000000..3dfb7c51
--- /dev/null
+++ b/Helm_Deployment.md
@@ -0,0 +1,46 @@
+# Deployment via Helm Charts
+[Helm](https://helm.sh/) is a standard framework for deploying Kubernetes applications.
+The Helm charts for Neon Diana are the recommended method for production deployment.
+
+## Preparing for Backend Deployment
+Before deploying a Diana backend, ensure the following has been completed:
+- Kubernetes Cluster is deployed and `helm` CLI is properly configured
+- Desired domain is ready to forward all relevant subdomains to the cluster
+
+## Backend Configuration
+Configure the backend deployment with 
+`diana configure-mq-backend <output_path>`. Follow the shell prompts to 
+provide any necessary configuration parameters; when complete, the specified
+`output_path` will be populated with Helm charts to deploy.
+
+### Ingress
+An NGINX ingress chart is provided at `<output_path>/ingress-nginx`. `values.yaml`
+may be updated to include any additional TCP ports to be used (i.e. for RabbitMQ)
+and then the chart is generally deployed to the `ingress-nginx` namespace. Only
+one ingress is necessary for a cluster. Deploy the configured ingress with:
+
+```
+helm dependency update <output_path>/ingress-common
+helm install ingress-nginx <output_path>/ingress-common --namespace ingress-nginx --create-namespace
+```
+> At this point, check to make sure all expected subdomains resolve to the ingress
+> IP address (ping each domain and check the resolved address)
+
+### Diana Backend
+Validate the configuration in `<output_path>/diana-backend/values.yaml`; at minimum,
+the following parameters MUST be updated:
+- `domain`
+- `letsencrypt.email`
+- `letsencrypt.server` (default is test endpoint)
+
+After updating or overriding these values, deploy the backend via:
+
+```
+helm dependency update <output_path>/diana-backend
+helm install diana-backend <output_path>/diana-backend --namespace backend --create-namespace
+```
+
+## Other Notes
+- The namespace used for Backend deployment is configurable; it may be desirable
+  to use namespaces for test vs production deployments, to separate the Diana
+  backend from other deployments, or both.
\ No newline at end of file
diff --git a/README.md b/README.md
index 8c02116f..a538e7d8 100644
--- a/README.md
+++ b/README.md
@@ -1,58 +1,239 @@
-# Neon Diana
-Device Independent API for Neon Applications (Diana) is a collection of microservices that provide various functionality 
-to NeonAI systems. All services are implemented as standalone Docker containers that are connected to a RabbitMQ server.
-This repository contains the files required to launch a Neon API server.
+# Install and Deploy NeonAI Diana 
 
-Install the Diana utilities Python package with: `pip install neon-diana-utils`
+Device Independent API for Neon Applications (Diana) is a collection of microservices that add functionality to NeonAI systems. Diana microservices are deployed in a Kubernetes cluster.
 
-## Automated Configuration
-The `diana` entrypoint is available to handle automated setup and some common administration tasks. You can see get detailed
-help via `diana --help`. A standard configuration is described here:
+After you have installed Diana, use `diana --help` to get detailed help.
 
-### Configuring Default Backend Services
-A Diana backend can be configured automatically with `diana configure-backend`. A standard example is included here, but 
-a description of config options is available via: `diana configure-backend --help`.
+For more information about using NeonAI, see [the NeonAI documentation site](https://neongeckocom.github.io/neon-docs/).
+
+## Prerequisites
+
+To install Diana:
+
+* [Ubuntu](https://ubuntu.com/) 20.04 or later. 
+  * Diana will most likely work on other flavors of Linux, but we have not yet verified this.
+* [Python](https://www.python.org/getit/) 3.8 or later.
+* [Pip](https://pypi.org/project/pip/) Python package installer.
+
+To deploy Diana: 
+
+* [Kubectl](https://kubernetes.io/docs/reference/kubectl/) Kubernetes command-line tool.
+* [Helm](https://helm.sh/) package manager for Kubernetes.
+* A [Kubernetes](https://kubernetes.io/) installation.
+  * The following instructions assume a local installation using [Microk8s](https://microk8s.io/) version 1.26/stable or later.
+  * You can likely deploy Diana on a Kubernetes cluster in the cloud, but we have not yet verified this.
+
+## Use a Python virtual environment
+
+We recommend you use a [Python virtual environment](https://docs.python.org/3/library/venv.html) for installing and testing Diana. In addition to the usual benefits, using a virtual environment makes it easier to ensure you are using the correct versions of Python and Diana.
+
+1. Create a Python virtual environment:
 
-```shell
-diana configure-backend -d -u administrator -p password ~/neon_diana
 ```
-* `-d` specifies that default backend services will be configured
-* `-u` specifies the MQ user `administrator` for admin portal access
-* `-p` specifies the password `password` for the `administrator` user
-* `~/neon_diana` specifies the output path for configuration files
+python3.10 -m venv venv
+```
 
-### Providing Backend Service Credentials
-Many backend services rely on configured credentials for authentication. Before running a configured backend, `ngi_auth_vars.yml`
-must be defined with the appropriate credentials. A more complete example can be seen in the [Configuration section](#configuration).
+2. Open a new terminal window. Activate the Python virtual environment in this new window:
 
-#### ~/neon_diana/ngi_auth_vars.yml
-```yaml
-emails:
-  mail: neon@neon.ai
-  pass: 
-  host: smtp.gmail.com
-  port: '465'
-api_services:
-  wolfram_alpha:
-    api_key: ""
-  alpha_vantage:
-    api_key: ""
-  open_weather_map:
-    api_key: ""
-```
-* The `emails` config should reference a smtp email account used in `neon_email_proxy`
-* The `api_services` config should reference services used in `neon_api_proxy`
-
-### Running Configured Backend Services
+```
+. venv/bin/activate
+```
+
+Using this new window, proceed with the instructions.
+
+## Install Diana
+
+3. Use Pip to install Diana:
+
+```
+pip install --pre neon-diana-utils
+```
+
+This command installs the newest pre-release version, which is described in this tutorial. 
+
+**Warning:** You can use `pip install neon-diana-utils` to install the current stable version. Version 1.0.0 includes Helm chart support. For information on installing and running older versions, see [the archived documentation in the README file](https://github.com/NeonGeckoCom/neon-diana-utils/blob/dev/README.md).
+
+For more information on the available versions of Diana, see [the Python Package Index repo for Neon Diana](https://pypi.org/project/neon-diana-utils/).
+
+4. Verify Diana is installed:
+
+```
+diana --version
+```
+
+**Tip:** If your computer does not recognize this command, you may need to add `~/.local/bin` to your $PATH with a command like `export PATH=$PATH:/home/${USER}/.local/bin`.
+
+Use `diana --help` for detailed information about Diana and its commands.
+
+The output of `diana --help` looks like:
+
+```
+Usage: diana [OPTIONS] COMMAND [ARGS]...
+
+  Diana: Device Independent API for Neon Applications.
+
+  See also: diana COMMAND --help
+
+Options:
+  -v, --version  Print the current version
+  --help         Show this message and exit.
+
+Commands:
+  configure-backend     Configure a Diana Backend
+  configure-mq-backend  Configure RabbitMQ and export user credentials
+  make-github-secret    Generate Kubernetes secret for Github images
+  make-keys-config      Generate a configuration file with access keys
+  make-rmq-config       Generate RabbitMQ definitions
+  start-backend         Start a Diana Backend
+  stop-backend          Stop a Diana Backend
+
+```
+
+## Deploy NeonAI Diana
+
+This example deploys Diana in a local [Microk8s Kubernetes](https://microk8s.io/) cluster. 
+
+### Install and Run Microk8s
+
+If you don't have Microk8s installed, you can install it and create the necessary user with:
+
+```
+sudo snap install microk8s --classic
+sudo usermod -aG microk8s $USER
+newgrp microk8s
+```
+
+5. Start Microk8s:
+
+```
+microk8s start
+```
+
+6. Enable the services for persistent storage, DNS, and the Kubernetes dashboard:
+
+```
+microk8s enable hostpath-storage
+microk8s enable dns
+microk8s enable dashboard
+```
+
+7. Enable the MetalLB service:
+
+```
+microk8s enable metallb
+```
+
+At the prompt, enter a subnet  which is not being used by your router. For example: 
+
+```
+10.10.10.10-10.10.10.10
+```
+
+Note: Unless you plan on adding multiple nodes, this range only needs one address.
+
+8. After Microk8s is running, use `microk8s kubectl create token default` to create a Microk8s token.
+
+9. In the new terminal window, use `microk8s kubectl port-forward -n kube-system service/kubernetes-dashboard 1443:443` to forward the dashboard port. 
+
+You can now access your Kubernetes dashboard in a browser at https://localhost:1443/ using the token you created in step 2. 
+
+10. The process in this terminal needs to keep running. Either background the process, or leave this terminal window open and open a new terminal window to continue working.
+
+### Set Up DNS
+
+The ingress controller needs URLs to be mapped to services. There are a number of different ways you can accomplish this, depending on your networking setup. 
+
+For this guide, we will use the simple case of editing the `/etc/hosts` file.
+
+11. Edit the `/etc/hosts` file. Add one entry for the domain name of each service you intend to run. Add the canonical domain and point it to the IP address you gave MetalLB in step 3.
+
+For example, if you plan to run a service named `test-service` on the `diana.k8s` domain, add the following line:
+
+```
+10.10.10.10 test-service.diana.k8s
+```
+
+This tells your computer that `test-service.diana.k8s` is at IP address `10.10.10.10`. Your computer will route all requests for `test-service.diana.k8s` to the Kubernetes cluster you set up at `10.10.10.10`, instead of looking for it on the public internet.
+
+Add one line for each service. Point each service to the same IP address.
+
+
+### Prepare for Deployment
+
+12. After you set up your Kubernetes cluster and configure DNS, configure the Diana backend with:
+
+```
+diana configure-mq-backend OUTPUT_PATH
+```
+
+Replace `OUTPUT_PATH` with the directory where you want Diana to store its Helm charts and configurations. For example:
+
+```
+diana configure-mq-backend ~/neon_diana
+```
+
+Follow the prompts to provide any necessary configuration parameters. 
+
+13. **Optional:** To add extra TCP ports (i.e. for RabbitMQ), update the `OUTPUT_PATH/ingress-nginx/values.yaml` file accordingly.
+
+14. Deploy the NGINX ingress:
+
+```
+helm install ingress-nginx OUTPUT_PATH/ingress-common --namespace ingress-nginx --create-namespace
+```
+
+15. Edit `OUTPUT_PATH/diana-backend-values.yaml` and update any necessary configuration. At minimum, you need to update the following parameters:
+
+* `domain` Change this to the domain you added to the `/etc/hosts` file in step 11.
+* `letsencrypt.email` If you are using a "real" domain, change this to the email address you want to use for the [Let's Encrypt](https://letsencrypt.org/) SSL certificate. For local testing, leave this as is.
+* `letsencrypt.server` If you are using a "real" domain, change this to a valid Let's Encrypt server address, such as `https://acme-v02.api.letsencrypt.org/directory`. For local testing, leave this as is.
+
+## Deploy the Diana Backend
+
+16. Update the Helm dependency:
+
+```
+helm dependency update OUTPUT_PATH/diana-backend
+```
+
+17. Use Helm to launch Diana:
+
+```
+helm install diana-backend OUTPUT_PATH/test/diana-backend --namespace backend --create-namespace
+```
+
+This creates the `backend` namespace and launches Diana into that namespace. You can change this to any namespace name you prefer. You may want to use separate namespaces for test versus production deployments, to separate the Diana backend from other deployments, or both.
+
+
+# Legacy Documentation
+The below documentation is mostly applicable to Docker deployment of the Diana
+backend and is no longer fully-supported. Documentation is retained here for
+reference.
+
+### Orchestrator Configuration
+The following sections describe how to initialize a standard backend with 
+[Docker](#docker-compose) or [Kubernetes](#kubernetes).
+
+#### Docker Compose
+For testing or running on a dedicated host, `Docker Compose` offers a simple method for starting a set of services.
+This deployment assumes all services will run a single instance on a shared host with any configuration or other files 
+saved to the host filesystem or a configured NFS share. The configuration files 
+[generated earlier](#generate-config-with-diana) are all that is necessary to start the backend with Docker Compose
+
+##### Running Configured Backend Services
 After a backend is configured, it can be started with `diana start-backend`. A standard example is included here, but 
 a description of config options is available via: `diana start-backend --help`.
 
+> *Note*: If running private containers, you will need to authenticate with Docker.
+> Documentation is available [from docker](https://docs.docker.com/engine/reference/commandline/login/).
+> Containers from NeonGecko are published to the `ghcr.io` server
+
 ```shell
 diana start-backend ~/neon_diana
 ```
 * `~/neon_diana` specifies the path to backend configuration
 
-### Stopping a Running Backend
+##### Stopping a Running Backend
 After a backend is started, it can be stopped with `diana stop-backend`. A standard example is included here, but 
 a description of config options is available via: `diana stop-backend --help`.
 ```shell
@@ -60,112 +241,148 @@ diana stop-backend ~/neon_diana
 ```
 * `~/neon_diana` specifies the path to backend configuration
 
-## Manual Configuration
-### Running Services
-[Docker Compose](https://docs.docker.com/compose/) is used to facilitate starting up separate services that comprise Diana.
-The example below assumes that configuration is populated in `~/docker_config` and that metrics should be saved to 
-`~/docker_metrics`. These paths are arbitrary and can be changed to any location that is accessible by the user running 
-docker. Details of what should be present in `NEON_CONFIG_PATH` can be found in the [Configuration](#configuration) 
-section below.
+#### Kubernetes
+For deployment, Kubernetes provides a managed solution that can handle scaling, rolling updates, and other benefits.
+This deployment assumes you have an existing cluster; it is assumed that the Cluster or system administrator will manage
+creation of `PersistentVolume` and `LoadBalancer` objects as necessary.
+
+##### Cluster Preparation
+The config generation in this project assumes your cluster has the `NGINX Ingress Controller` deployed.
+Installation instructions are available [from Kubernetes](https://kubernetes.github.io/ingress-nginx/deploy/).
+
+The `ingress-nginx-controller` service should have External Endpoints exposed. If you are deploying locally, you may use
+[MetalLB](https://metallb.universe.tf/) to configure a `LoadBalancer`.
+
+The [Kubernets Cluser References section below](#kubernetes-cluster-references)
+contains references to documentation and guides used to configure a cluster.
+
+##### Providing GitHub Image Pull Secrets
+For private container images, you will need to specify GitHub credentials. Documentation for creating GitHub Personal 
+Access Tokens can be found [in the GitHub docs](https://docs.github.com/en/enterprise-cloud@latest/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).
+The configured token must have the permission: `read:packages`.
+
+```shell
+diana make-api-secrets -u <github_username> -t <github_pat> ~/neon_diana
+```
+
+##### Providing Backend Service Credentials
+Kubernetes uses [secrets](https://kubernetes.io/docs/concepts/configuration/secret/) to store sensitive data in a cluster.
+Generate a Kubernetes secret spec with `ngi_auth_vars.yml` (`k8s_secret_ngi-auth.yml`).
 
 ```shell
-export NEON_CONFIG_PATH="/home/${USER}/diana_config/"
-export NEON_METRIC_PATH="/home/${USER}/diana_metrics/"
-docker login ghcr.io
-docker-compose up -d
+diana make-api-secrets -p ~/.config/neon ~/neon_diana
 ```
+* `-p` specifies the path to the directory containing `ngi_auth_vars.yml`
+* `~/neon_diana` specifies the output path for configuration files
+
+##### Defining Ingress
+
+###### TCP Services
+`diana` includes cli utilities for generating ingress definitions for non-http services.
+In general, ingress definitions will be created or updated when relevant backend services are configured, but
+the `diana add-tcp-service` entrypoint is also available to define these manually. Note that adding configuration
+will modify existing spec files in the configured path.
+
+###### HTTP Services
+`diana` includes cli utilities for generating ingress rules for http services when using `ingress-nginx`.
+It is assumed that the `ingress-nginx` and `cert-manager` namespaced services are deployed as described 
+[below](#kubernetes-cluster-references) and that A Records are defined for all configured subdomains.
+
+HTTP `Ingress` is namespaced, so the configurations generated here must be applied to the same namespace
+as the HTTP `Service`s they forward to. The commands in this guide will assume everything is in the "default"
+namespace unless otherwise specified.
 
-If you prefer not to run all services, you may specify which services to run with `docker-compose up`.
 ```shell
-docker-compose up -d neon_rabbitmq neon_api_proxy neon_metrics_service
+# Create a certificate issuer (must be deployed to each namespace)
+diana make-cert-issuer -e <email_address> ~/neon_diana
+
+# Update an Ingress configuration for every HTTP service
+diana add-ingress -s <service_name> -p <service_http_port> -h <url_for_service> ~/neon_diana
 ```
 
-### Initial Configuration
-`neon_diana_utils` contains convenience utilities, including for automated initial configuration of RabbitMQ. If you 
-have a clean RabbitMQ container, you can use `create_default_mq_server` to configure an admin account and all parameters
-required for running Neon Diana. Make sure the `neon_rabbitmq` container is running before running this utility. After 
-RabbitMQ Configuration is complete, you can start the remaining containers 
+##### Applying Configuration to a Cluster
+`kubectl` should be configured to reference the Kubernetes cluster you are deploying to.
+If you are accessing private repositories, you will also need to configure the secret `github-auth`. Documentation 
+can be found [in the Kubernetes docs](https://kubernetes.io/dkocs/tasks/configure-pod-container/pull-image-private-registry/).
 
-ex:
 ```shell
-export NEON_CONFIG_PATH="/home/${USER}/diana_config/"
-# Modify neon-diana-backend/setup_default_server.py with desired username and password
-python neon-diana-backend/setup_default_server.py
-```
-
-### Configuration
-All containers containing an MQ module will expect `mq_config.json` to be mounted to `NEON_CONFIG_PATH` 
-(usually `/config` in the containers).
-
-- `neon_api_proxy`, `neon_brands_service`, and `neon_email_proxy` will expect `/config/ngi_auth_vars.yml` to specify any
-    account credentials
-- `neon_metrics_service` will output metrics files to `/metrics`.
-- `neon_rabbitmq` will expect `/config/rabbitmq.conf` to specify a path to the configuration `.json` file to load.
-
-Example configuration file structure:
-```
-$NEON_CONFIG_DIR
-├── mq_config.json
-├── ngi_auth_vars.yml
-├── rabbitmq.conf
-└── rabbit_mq_config.json
-```
-
-`mq_config.json` (passwords redacted)
-```json
-{
-  "server": "neon_rabbitmq",
-  "users": {
-    "test": {
-      "user": "test_user",
-      "password": ""
-    },
-    "neon_api_connector": {
-      "user": "neon_api",
-      "password": ""
-    },
-    "neon_coupon_connector": {
-      "user": "neon_coupons",
-      "password": ""
-    },
-    "neon_email_proxy": {
-      "user": "neon_email",
-      "password": ""
-    },
-    "neon_metrics_connector": {
-      "user": "neon_metrics",
-      "password": ""
-    },
-    "neon_script_parser_service": {
-      "user": "neon_script_parser",
-      "password": ""
-    }
-  }
-}
-```
-
-`ngi_auth_vars.yml` (passwords redacted)
+# Apply configuration and secrets
+kubectl apply -f ~/neon_diana/config/k8s_secret_mq-config.yml -f ~/neon_diana/config/k8s_config_rabbitmq.yml -f ~/neon_diana/k8s_secret_ngi-auth.yml
+
+# If using ingress-nginx, apply those configurations
+kubectl apply -f ~/neon_diana/ingress/k8s_config_tcp_services.yml
+kubectl patch -n ingress-nginx service ingress-nginx-controller --patch-file ~/neon_diana/ingress/k8s_patch_nginx_service.yml
+
+# If using HTTP services, apply ingress rules
+kubectl apply -f ~/neon_diana/ingress/k8s_config_cert_issuer.yml -f ~/neon_diana/ingress/k8s_config_ingress.yml
+
+# If using private images
+kubectl apply -f ~/neon_diana/k8s_secret_github.yml
+
+# Start backend services
+kubectl apply -f ~/neon_diana/services/k8s_diana_backend.yml
+```
+
+# Kubernetes Cluster References
+The following configurations were used at the time of writing this document:
+
+- [ingress-nginx](https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/cloud/deploy.yaml)
+- [cert-manager](https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.yaml)
+
+These guides also may be useful for configuration that isn't handled in this repository:
+
+- [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/deploy/)
+- [cert-manager](https://cert-manager.io/docs/tutorials/acme/ingress/)
+- [MetalLB](https://metallb.universe.tf/installation/)
+- [GitHub Token Auth](https://dev.to/asizikov/using-github-container-registry-with-kubernetes-38fb)
+
+### Certbot SSL
+The definition below can be used to configure LetsEncrypt
+```yaml
+apiVersion: cert-manager.io/v1
+kind: Issuer
+metadata:
+  name: letsencrypt-prod
+spec:
+  acme:
+    # The ACME server URL
+    server: https://acme-v02.api.letsencrypt.org/directory
+    # Email address used for ACME registration
+    email: ${CERT_EMAIL}
+    # Name of a secret used to store the ACME account private key
+    privateKeySecretRef:
+      name: letsencrypt-prod
+    # Enable the HTTP-01 challenge provider
+    solvers:
+    - http01:
+        ingress:
+          class: nginx
+```
+
+### Ingress Definitions
+The definition below can be used to configure ingress
 ```yaml
-track_my_brands:
-  user: admintr1_drup1
-  password: 
-  host: trackmybrands.com
-  database: admintr1_drup1
-emails:
-  mail: neon@neon.ai
-  pass: 
-  host: smtp.gmail.com
-  port: '465'
-api_services:
-  wolfram_alpha:
-    api_key: ""
-  alpha_vantage:
-    api_key: ""
-  open_weather_map:
-    api_key: ""
-```
-
-`rabbitmq.conf`
-```
-load_definitions = /config/rabbit_mq_config.json
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: ingress-backend
+  annotations:
+    cert-manager.io/issuer: "letsencrypt-prod"
+spec:
+  tls:
+    - hosts:
+        - mqadmin.${domain}
+      secretName: ${domain}-tls
+  ingressClassName: nginx
+  rules:
+    - host: "mqadmin.${domain}"
+      http:
+        paths:
+          - path: /
+            pathType: Prefix
+            backend:
+              service:
+                name: neon-rabbitmq
+                port:
+                  number: 15672
 ```
diff --git a/Testing.md b/Testing.md
new file mode 100644
index 00000000..61128165
--- /dev/null
+++ b/Testing.md
@@ -0,0 +1,57 @@
+## Getting Started
+To begin testing, we start with a clean environment and install a Kubernetes
+runtime. For this example, we will use Ubuntu 22.04 and MicroK8s.
+1. Start with Ubuntu 22.04 (with Snap).
+2. Install Kubernetes runtime
+```shell
+sudo snap install microk8s --classic
+sudo usermod -aG microk8s $USER
+newgrp microk8s
+```
+3. Enable services for persistent storage, DNS, Dashboard, and MetalLB
+```shell
+microk8s enable hostpath-storage
+microk8s enable dns
+microk8s enable dashboard
+microk8s enable metallb
+# When prompted, choose a subnet that is different than anything your router uses,
+# i.e. 10.10.10.10-10.10.10.10
+# Unless you plan on adding multiple nodes, this range only needs one address.
+```
+3. Forward the Kubernetes Dashboard port. You will need to run this in a separate terminal, or
+   fork the process to the background to continue after starting the dashboard.
+```shell
+microk8s kubectl create token default  # Copy this token
+microk8s kubectl port-forward -n kube-system service/kubernetes-dashboard 1443:443
+# Dashboard is now available at: https://localhost:1443 using the token generated earlier
+```
+> Note: Add `--address="0.0.0.0` to expose the dashboard to your local network
+
+## DNS Resolution
+Ingress configuration will expect URLs to map to services, for local testing you may
+either configure subdomains for a domain you own to forward to your ingress controller
+(in our example this is `10.10.10.10`), or you can modify the hosts file 
+(`/etc/hosts` for Ubuntu) on a computer you use for testing to forward to a fake
+domain, or a real domain that you own. You could also configure dnsmasq or any
+number of other services, but this guide will assume the simple case of overriding
+the hosts file.
+
+### Modify `/etc/hosts`
+Every HTTP service you enable will expect a unique subdomain to bind to. For
+example, a service that binds to `service.diana.k8s` would need the following
+line added to `/etc/hosts`:
+```
+10.10.10.10    service.diana.k8s
+```
+A line like this is required for each HTTP service enabled for testing. This tells
+the operating system that `service.diana.k8s` exists at IP address `10.10.10.10`,
+so the request is routed to the Kubernetes cluster instead of out to a DNS resolver.
+
+## Next Steps
+From here on, the testing environment should closely match a deployment 
+environment (Digital Ocean, Google Cloud, Azure, etc).
+
+### References
+[ingress-nginx Installation Guide](https://kubernetes.github.io/ingress-nginx/deploy/)
+[microk8s Addon: dashboard](https://microk8s.io/docs/addon-dashboard)
+[microk8s Addon: MetalLB](https://microk8s.io/docs/addon-metallb)
\ No newline at end of file
diff --git a/neon_diana_utils/cli.py b/neon_diana_utils/cli.py
index 4c9d98c4..75838e65 100644
--- a/neon_diana_utils/cli.py
+++ b/neon_diana_utils/cli.py
@@ -24,28 +24,10 @@
 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 # SOFTWARE,  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-import subprocess
 import click
 
-from os import makedirs, getenv
-from os.path import expanduser, isdir, isfile, join
 from click_default_group import DefaultGroup
-
-from .version import __version__
-
-# TODO: Valid services can be read from service_mappings.yml directly
-VALID_SERVICES = ("neon_rabbitmq",
-                  "neon_api_proxy",
-                  "neon_brands_service",
-                  "neon_email_proxy",
-                  "neon_script_parser",
-                  "neon_metrics_service")
-
-# TODO: Consider tagging these in service_mappings.yml or some other method for easier extensibility DM
-DEFAULT_SERVICES = ("neon_rabbitmq",
-                    "neon_api_proxy",
-                    "neon_email_proxy",
-                    "neon_metrics_service")
+from neon_diana_utils.version import __version__
 
 
 @click.group("diana", cls=DefaultGroup,
@@ -59,108 +41,46 @@ def neon_diana_cli(version: bool = False):
         click.echo(f"Diana version {__version__}")
 
 
-@neon_diana_cli.command(help="Configure a Diana Backend")
-@click.option('--service', '-s', multiple=True,
-              help="Optional service to configure")
-@click.option('--default', '-d', is_flag=True, default=False,
-              help="Configure the Default Backend Services")
-@click.option('--complete', '-c', is_flag=True, default=False,
-              help="Configure All Backend Services. (NOTE: This includes packages that require authentication)")
-@click.option('--user', '-u', default="admin",
-              help="Username to configure for administration")
-@click.option('--password', '-p',
-              help="Password associated with admin user to login to console")
-@click.argument('config_path', default=getenv("NEON_CONFIG_DIR", "~/.config/neon/"))
-def configure_backend(config_path, service, default, complete, user, password):
-    # Determine Configuration Path
-    config_path = expanduser(config_path)
-    if not config_path:
-        return ValueError("Null config_path")
-    if not isdir(config_path):
-        try:
-            makedirs(config_path)
-        except Exception:
-            return ValueError(f"Unable to create config directory: {config_path}")
-
-    # Determine admin credentials
-    if not password:
-        click.echo(f"No password specified, please specify a password for RabbitMQ admin access")
-        return ValueError("Null password")
-
-    click.echo(f"Configuring RabbitMQ Administrator: {user}")
-
-    # Determine Services to Configure
-    if complete:
-        services_to_configure = VALID_SERVICES
-    elif default:
-        services_to_configure = DEFAULT_SERVICES
-    else:
-        services_to_configure = None
-    if service:
-        if services_to_configure:
-            click.echo("Service set and individual services specified\n"
-                       "Configuring specified services only.")
-        services_to_configure = list(service)
-        for service in services_to_configure:
-            if service not in VALID_SERVICES:
-                click.echo(f"Invalid service requested will be ignored: {service}")
-                services_to_configure.remove(service)
-        services_to_configure = set(services_to_configure)
-    if not services_to_configure:
-        click.echo("No services specified. Call `diana configure-backend --help` for more info")
-        return ValueError("No services provided")
-    click.echo(f"Configuring API Services: {services_to_configure}")
-    click.echo(f"Configuration will be written to: {config_path}")
-
-    # Call setup
-    import sys
-    std_out = sys.stdout
-    sys.stdout = open("/dev/null", 'a+')
-    from .utils import create_diana_configurations
-    create_diana_configurations(user, password, services_to_configure, config_path)
-    sys.stdout = std_out
-    click.echo(f"Configuration Complete")
-    click.echo(f"Remember to place `ngi_auth_vars.yml` in {config_path}")
-
-
-@neon_diana_cli.command(help="Start a Diana Backend")
-@click.option('--service', '-s', multiple=True,
-              help="Optional service to start")
-@click.option('--attach', '-a', is_flag=True, default=False,
-              help="Attach terminal to the started containers")
-@click.argument('config_path', default=getenv("NEON_CONFIG_DIR", "~/.config/neon/"))
-def start_backend(config_path, service, attach):
-    # Validate path to docker-compose.yml
-    config_path = expanduser(config_path)
-    if not config_path:
-        return ValueError("Null config_path")
-    if not isfile(join(config_path, "docker-compose.yml")):
-        click.echo(f"docker-compose.yml not found in {config_path}")
-        return ValueError(f"docker-compose.yml not found")
-
-    docker_compose_command = "docker-compose up"
-    if service:
-        # TODO: Validate services are in docker compose file
-        docker_compose_command = " ".join((docker_compose_command, " ".join(service)))
-    if not attach:
-        docker_compose_command += " --detach"
-        subprocess.Popen(["/bin/bash", "-c", f"cd {config_path} && {docker_compose_command}"]).communicate()
-        click.echo("Diana Backend Started")
-    else:
-        subprocess.Popen(["/bin/bash", "-c", f"cd {config_path} && {docker_compose_command}"]).communicate()
-
-
-@neon_diana_cli.command(help="Stop a Diana Backend")
-@click.argument('config_path', default=getenv("NEON_CONFIG_DIR", "~/.config/neon/"))
-def stop_backend(config_path):
-    # Validate path to docker-compose.yml
-    config_path = expanduser(config_path)
-    if not config_path:
-        return ValueError("Null config_path")
-    if not isfile(join(config_path, "docker-compose.yml")):
-        click.echo(f"docker-compose.yml not found in {config_path}")
-        return ValueError(f"docker-compose.yml not found")
-
-    docker_compose_command = "docker-compose down"
-    subprocess.Popen(["/bin/bash", "-c", f"cd {config_path} && {docker_compose_command}"]).communicate()
-    click.echo("Diana Backend Stopped")
+# Backend
+@neon_diana_cli.command(help="Configure RabbitMQ and export user credentials")
+@click.option("--username", "-u", help="RabbitMQ username")
+@click.option("--password", "-p", help="RabbitMQ password")
+@click.option("--orchestrator", "-o", default="kubernetes",
+              help="Container orchestrator (`kubernetes` or `docker-compose`")
+@click.argument("output_path", default=None, required=False)
+def configure_backend(username, password, orchestrator, output_path):
+    from neon_diana_utils.configuration import configure_backend, Orchestrator
+    try:
+        orchestrator = Orchestrator(orchestrator)
+    except ValueError:
+        click.echo(f"{orchestrator} is not a valid orchestrator")
+        return
+    configure_backend(username, password, output_path, orchestrator)
+
+
+@neon_diana_cli.command(help="Configure backend services for deployment")
+@click.option("--username", "-u", help="RabbitMQ username")
+@click.option("--password", "-p", help="RabbitMQ password")
+@click.argument("output_path", default=None, required=False)
+def configure_mq_backend(username, password, output_path):
+    # TODO: Deprecate in favor of `configure_backend`
+    from neon_diana_utils.configuration import configure_backend
+    configure_backend(username, password, output_path)
+
+
+@neon_diana_cli.command(help="Generate RabbitMQ definitions")
+@click.option("--username", "-u", help="RabbitMQ username")
+@click.option("--password", "-p", help="RabbitMQ password")
+@click.argument("output_file", default=None, required=False)
+def make_rmq_config(username, password, output_file):
+    from neon_diana_utils.configuration import generate_rmq_config
+    generate_rmq_config(username, password, output_file)
+
+
+@neon_diana_cli.command(help="Generate a configuration file with access keys")
+@click.option("--skip-write", "-s", help="Skip writing config to file",
+              is_flag=True)
+@click.argument("output_file", default=None, required=False)
+def make_keys_config(skip_write, output_file):
+    from neon_diana_utils.configuration import make_keys_config
+    make_keys_config(not skip_write, output_file)
diff --git a/neon_diana_utils/configuration.py b/neon_diana_utils/configuration.py
new file mode 100644
index 00000000..03e90d7f
--- /dev/null
+++ b/neon_diana_utils/configuration.py
@@ -0,0 +1,322 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+#    this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from this
+#    software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE,  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import click
+import yaml
+import json
+import secrets
+import shutil
+
+from enum import Enum
+from pprint import pformat
+from typing import Optional
+from os import makedirs, listdir, walk, remove
+from os.path import expanduser, join, abspath, isfile, isdir, dirname
+from ovos_utils.xdg_utils import xdg_config_home
+from ovos_utils.log import LOG
+
+
+class Orchestrator(Enum):
+    """
+    Enum represending container orchestrators that may be configured
+    """
+    KUBERNETES = "kubernetes"
+    COMPOSE = "docker-compose"
+
+
+def validate_output_path(output_path: str) -> bool:
+    """
+    Ensure the requested output path is available to be written
+    @returns: True if path is valid, else False
+    """
+    if isfile(output_path):
+        LOG.warning(f"File already exists: {output_path}")
+        return False
+    elif isdir(output_path) and listdir(output_path):
+        LOG.warning(f"Directory is not empty: {output_path}")
+        return False
+    elif not isdir(dirname(output_path)):
+        makedirs(dirname(output_path))
+    return True
+
+
+def make_keys_config(write_config: bool,
+                     output_file: str = None) -> Optional[dict]:
+    """
+    Interactive configuration tool to prompt user for expected API keys and
+    service accounts to be included in Configuration.
+    @param write_config: If true, write config to `output_file`
+    @param output_file: Configuration file to write keys to
+    @returns: dict configuration
+    """
+    if write_config:
+        output_file = expanduser(abspath((output_file or join(xdg_config_home(),
+                                                              "diana",
+                                                              "diana.yaml"))))
+        if not validate_output_path(output_file):
+            click.echo(f"File already exists: {output_file}")
+            return
+
+    api_services = dict()
+    if click.confirm("Configure API Proxy Services?"):
+        keys_confirmed = False
+        while not keys_confirmed:
+            wolfram_key = click.prompt("Wolfram|Alpha API Key", type=str)
+            alphavantage_key = click.prompt("AlphaVantage API Key",
+                                            type=str)
+            owm_key = click.prompt("OpenWeatherMap API Key", type=str)
+            api_services = {
+                "wolfram_alpha": {"api_key": wolfram_key},
+                "alpha_vantage": {"api_key": alphavantage_key},
+                "open_weather_map": {"api_key": owm_key}
+            }
+            click.echo(pformat(api_services))
+            keys_confirmed = click.confirm("Are these keys correct?")
+
+    email_config = dict()
+    if click.confirm("Configure Email Service?"):
+        config_confirmed = False
+        while not config_confirmed:
+            email_addr = click.prompt("Email Address", type=str)
+            email_password = click.prompt("Password", type=str)
+            smtp_host = click.prompt("SMTP URL", type=str,
+                                     default="smtp.gmail.com")
+            smtp_port = click.prompt("SMTP Port", type=str,
+                                     default="465")
+            email_config = {"mail": email_addr,
+                            "pass": email_password,
+                            "host": smtp_host,
+                            "port": smtp_port}
+            click.echo(pformat(email_config))
+            config_confirmed = \
+                click.confirm("Is this configuration correct?")
+
+    brands_config = dict()
+    if click.confirm("Configure Brands/Coupons Service?"):
+        config_confirmed = False
+        while not config_confirmed:
+            server_host = click.prompt("SQL Host Address", type=str,
+                                       default="trackmybrands.com")
+            sql_database = click.prompt("SQL Database", type=str,
+                                        default="admintr1_drup1")
+            sql_username = click.prompt("SQL Username", type=str)
+            sql_password = click.prompt("SQL Password", type=str)
+            brands_config = {"host": server_host,
+                             "database": sql_database,
+                             "user": sql_username,
+                             "password": sql_password}
+            click.echo(pformat(brands_config))
+            config_confirmed = \
+                click.confirm("Is this configuration correct?")
+
+    chatgpt_config = dict()
+    if click.confirm("Configure ChatGPT Service?"):
+        config_confirmed = False
+        while not config_confirmed:
+            gpt_key = click.prompt("ChatGPT API Key", type=str)
+            gpt_model = click.prompt("ChatGPT Model", type=str,
+                                     default="gpt-3.5-turbo")
+            gpt_role = click.prompt("ChatGPT Role", type=str,
+                                    default="You are trying to give a short "
+                                            "answer in less than 40 words.")
+            gpt_context = click.prompt("ChatGPT Context depth", type=int,
+                                       default=3)
+            max_tokens = click.prompt("Maximum tokens in responses", type=int,
+                                      default=100)
+            chatgpt_config = {
+                "key": gpt_key,
+                "model": gpt_model,
+                "role": gpt_role,
+                "context_depth": gpt_context,
+                "max_tokens": max_tokens
+            }
+            click.echo(pformat(chatgpt_config))
+            config_confirmed = \
+                click.confirm("Is this configuration correct?")
+
+    fastchat_config = dict()
+    if click.confirm("Configure FastChat Service?"):
+        config_confirmed = False
+        while not config_confirmed:
+            model = click.prompt("FastChat Model", type=str,
+                                 default="fastchat")
+            context = click.prompt("FastChat context depth", type=int,
+                                   default=3)
+            max_tokens = click.prompt("Max number of tokens in responses",
+                                      type=int, default=128)
+            num_processes = click.prompt(
+                "Number of queries to handle in parallel",
+                type=int, default=1)
+            num_threads = click.prompt("Number of threads to use per query",
+                                       type=int, default=2)
+            fastchat_config = {
+                "model": model,
+                "context_depth": context,
+                "max_tokens": max_tokens,
+                "num_parallel_processes": num_processes,
+                "num_threads_per_process": num_threads
+            }
+            click.echo(pformat(fastchat_config))
+            config_confirmed = click.confirm("Is this configuration correct?")
+
+    config = {"keys": {"api_services": api_services,
+                       "emails": email_config,
+                       "track_my_brands": brands_config},
+              "ChatGPT": chatgpt_config,
+              "FastChat": fastchat_config
+              }
+    if write_config:
+        click.echo(f"Writing configuration to {output_file}")
+        with open(output_file, 'w+') as f:
+            yaml.dump(config, f)
+    return config
+
+
+def generate_rmq_config(admin_username: str, admin_password: str,
+                        output_file: str = None) -> dict:
+    """
+    Generate a default configuration for RabbitMQ. This defines all default
+    users, vhosts, and permissions that may be used with a deployment.
+    @param admin_username: Username for admin account
+    @param admin_password: Password for admin account
+    @param output_file: Optional path to write configuration to
+    @returns: dict RabbitMQ Configuration
+    """
+    base_config_file = join((dirname(__file__)), "templates",
+                            "rmq_backend_config.yml")
+    with open(base_config_file) as f:
+        base_config = yaml.safe_load(f)
+    for user in base_config['users']:
+        if user["password"]:
+            # Skip users with defined passwords
+            continue
+        user['password'] = secrets.token_urlsafe(32)
+
+    base_config['users'].append({'name': admin_username,
+                                 'password': admin_password,
+                                 'tags': ['administrator']})
+
+    if output_file and validate_output_path(output_file):
+        with open(output_file, 'w+') as f:
+            json.dump(base_config, f, indent=2)
+    return base_config
+
+
+def generate_mq_auth_config(rmq_config: dict) -> dict:
+    """
+    Generate an MQ auth config from RabbitMQ config
+    :param rmq_config: RabbitMQ definitions, i.d. from `generate_rmq_config
+    :returns: Configuration for Neon MQ-Connector
+    """
+    mq_user_mapping_file = join(dirname(__file__), "templates",
+                                "mq_user_mapping.yml")
+    with open(mq_user_mapping_file) as f:
+        mq_user_mapping = yaml.safe_load(f)
+
+    mq_config = dict()
+    LOG.debug(rmq_config.keys())
+    for user in rmq_config['users']:
+        username = user['name']
+        for service in mq_user_mapping.get(username, []):
+            mq_config[service] = {"user": username,
+                                  "password": user['password']}
+    return mq_config
+
+
+def configure_backend(username: str = None,
+                      password: str = None,
+                      output_path: str = None,
+                      orchestrator: Orchestrator = Orchestrator.KUBERNETES):
+    """
+    Generate DIANA backend definitions
+    @param username: RabbitMQ Admin username to configure
+    @param password: RabbitMQ Admin password to configure
+    @param output_path: directory to write output definitions to
+    @param orchestrator: Container orchestrator to generate configuration for
+    """
+
+    # Validate output paths
+    output_path = expanduser(output_path or join(xdg_config_home(), "diana"))
+    if not validate_output_path(output_path):
+        click.echo(f"Path exists: {output_path}")
+        return
+
+    # Get Helm charts in output directory for deployment
+    chart_path = join(output_path, "diana-backend")
+    if orchestrator == Orchestrator.KUBERNETES:
+        shutil.copytree(join(dirname(__file__), "helm_charts"), output_path)
+        # Cleanup any leftover build files
+        for root, _, files in walk(output_path):
+            for file in files:
+                if any((file.endswith(x) for x in (".lock", ".tgz"))):
+                    remove(join(root, file))
+        rmq_file = join(chart_path, "rabbitmq.json")
+        diana_config = join(chart_path, "diana.yaml")
+
+    elif orchestrator == Orchestrator.COMPOSE:
+        shutil.copytree(join(dirname(__file__), "docker", "backend"),
+                        output_path)
+        rmq_file = join(output_path, "xdg", "config", "rabbitmq",
+                        "rabbitmq.json")
+        diana_config = join(output_path, "xdg", "config", "neon", "diana.yaml")
+    else:
+        raise RuntimeError(f"{orchestrator} is not yet supported")
+    try:
+        # Generate RabbitMQ config
+        username = username or click.prompt("RabbitMQ Admin Username", type=str)
+        password = password or click.prompt("RabbitMQ Admin Password", type=str,
+                                            hide_input=True)
+        rmq_config = generate_rmq_config(username, password, rmq_file)
+        click.echo(f"Generated RabbitMQ config at {rmq_file}")
+
+        # Generate MQ Auth config
+        mq_auth_config = generate_mq_auth_config(rmq_config)
+        click.echo(f"Generated auth for services: {set(mq_auth_config.keys())}")
+
+        # Generate GH Auth config secret
+        if orchestrator == Orchestrator.KUBERNETES:
+            from neon_diana_utils.kubernetes_utils import create_github_secret
+            if click.confirm("Configure GitHub token for private services?"):
+                gh_username = click.prompt("GitHub username", type=str)
+                gh_token = click.prompt("GitHub Token with `read:packages` "
+                                        "permission", type=str)
+                gh_secret_path = join(chart_path, "templates",
+                                      "secret_gh_token.yaml")
+                create_github_secret(gh_username, gh_token, gh_secret_path)
+                click.echo(f"Generated GH secret at {gh_secret_path}")
+
+        # Generate `diana.yaml` output
+        keys_config = make_keys_config(False)
+        config = {**{"MQ": {"users": mq_auth_config,
+                            "server": "neon-rabbitmq",
+                            "port": 5672}},
+                  **keys_config}
+        click.echo(f"Writing configuration to {diana_config}")
+        with open(diana_config, 'w+') as f:
+            yaml.dump(config, f)
+        click.echo(f"Outputs generated in {output_path}")
+    except Exception as e:
+        click.echo(e)
diff --git a/neon_diana_utils/docker/backend/.env b/neon_diana_utils/docker/backend/.env
new file mode 100644
index 00000000..f9a345ff
--- /dev/null
+++ b/neon_diana_utils/docker/backend/.env
@@ -0,0 +1,3 @@
+DIANA_XDG_PATH=./xdg
+MQ_IMAGE_TAG=dev
+HTTP_IMAGE_TAG=dev
\ No newline at end of file
diff --git a/neon_diana_utils/docker/backend/docker-compose.yml b/neon_diana_utils/docker/backend/docker-compose.yml
new file mode 100644
index 00000000..2fc6c7ee
--- /dev/null
+++ b/neon_diana_utils/docker/backend/docker-compose.yml
@@ -0,0 +1,155 @@
+version: '3.1'
+networks:
+  diana-backend:
+volumes:
+  xdg:
+    driver_opts:
+      type: xdg
+      o: bind
+      device: ${DIANA_XDG_PATH}
+services:
+  neon-rabbitmq:
+    container_name: neon-rabbitmq
+    image: rabbitmq:3-management
+    ports:
+      - "5672:5672"
+      - "15672:15672"
+    networks:
+      diana-backend:
+        aliases:
+          - neon-rabbitmq
+    volumes:
+      - xdg:/xdg:rw
+      - ./rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro
+  # MQ Services
+  neon-api-proxy:
+    container_name: neon-api-proxy
+    image: ghcr.io/neongeckocom/neon_api_proxy:${MQ_IMAGE_TAG}
+    depends_on:
+      - neon-rabbitmq
+    networks:
+      - diana-backend
+    volumes:
+      - xdg:/xdg:rw
+    environment:
+      - XDG_CONFIG_HOME=/xdg/config
+      - XDG_DATA_HOME=/xdg/data
+      - XDG_CACHE_HOME=/xdg/cache
+      - XDG_STATE_HOME=/xdg/state
+  neon-email-proxy:
+    container_name: neon-email-proxy
+    image: ghcr.io/neongeckocom/neon_email_proxy:${MQ_IMAGE_TAG}
+    depends_on:
+      - neon-rabbitmq
+    networks:
+      - diana-backend
+    volumes:
+      - xdg:/xdg:rw
+    environment:
+      - XDG_CONFIG_HOME=/xdg/config
+      - XDG_DATA_HOME=/xdg/data
+      - XDG_CACHE_HOME=/xdg/cache
+      - XDG_STATE_HOME=/xdg/state
+  neon-llm-chatgpt:
+    container_name: neon-llm-chatgpt
+    image: ghcr.io/neongeckocom/neon-llm-chatgpt:${MQ_IMAGE_TAG}
+    depends_on:
+      - neon-rabbitmq
+    networks:
+      - diana-backend
+    volumes:
+      - xdg:/xdg:rw
+    environment:
+      - XDG_CONFIG_HOME=/xdg/config
+      - XDG_DATA_HOME=/xdg/data
+      - XDG_CACHE_HOME=/xdg/cache
+      - XDG_STATE_HOME=/xdg/state
+  neon-llm-fastchat:
+    container_name: neon-llm-fastchat
+    image: ghcr.io/neongeckocom/neon-llm-fastchat:${MQ_IMAGE_TAG}
+    depends_on:
+      - neon-rabbitmq
+    networks:
+      - diana-backend
+    volumes:
+      - xdg:/xdg:rw
+    environment:
+      - XDG_CONFIG_HOME=/xdg/config
+      - XDG_DATA_HOME=/xdg/data
+      - XDG_CACHE_HOME=/xdg/cache
+      - XDG_STATE_HOME=/xdg/state
+  neon-metrics-service:
+    container_name: neon-metrics-service
+    image: ghcr.io/neongeckocom/neon_metrics_service:${MQ_IMAGE_TAG}
+    depends_on:
+      - neon-rabbitmq
+    networks:
+      - diana-backend
+    volumes:
+      - xdg:/xdg:rw
+    environment:
+      - XDG_CONFIG_HOME=/xdg/config
+      - XDG_DATA_HOME=/xdg/data
+      - XDG_CACHE_HOME=/xdg/cache
+      - XDG_STATE_HOME=/xdg/state
+
+# These Containers require authentication to pull and are disabled by default
+#  neon-script-parser:
+#    container_name: neon-script-parser
+#    image: ghcr.io/neongeckocom/neon-script-parser:${MQ_IMAGE_TAG}
+#    depends_on:
+#      - neon-rabbitmq
+#    networks:
+#      - diana-backend
+#    volumes:
+#      - xdg:/xdg:rw
+#    environment:
+#      - XDG_CONFIG_HOME=/xdg/config
+#      - XDG_DATA_HOME=/xdg/data
+#      - XDG_CACHE_HOME=/xdg/cache
+#      - XDG_STATE_HOME=/xdg/state
+#  neon-brands-service:
+#    container_name: neon-brands-service
+#    image: ghcr.io/neongeckocom/neon_brands_service:${MQ_IMAGE_TAG}
+#    depends_on:
+#      - neon-rabbitmq
+#    networks:
+#      - diana-backend
+#    volumes:
+#      - xdg:/xdg:rw
+#    environment:
+#      - XDG_CONFIG_HOME=/xdg/config
+#      - XDG_DATA_HOME=/xdg/data
+#      - XDG_CACHE_HOME=/xdg/cache
+#      - XDG_STATE_HOME=/xdg/state
+
+  # HTTP Services
+  neon-libretranslate:
+    container_name: neon-libretranslate
+    image: ghcr.io/neongeckocom/libretranslate:latest
+    networks:
+      - diana-backend
+    ports:
+      - "5000:5000"
+    volumes:
+      - xdg:/xdg:rw
+    environment:
+      - XDG_CONFIG_HOME=/xdg/config
+      - XDG_DATA_HOME=/xdg/data
+      - XDG_CACHE_HOME=/xdg/cache
+      - XDG_STATE_HOME=/xdg/state
+  neon-tts-coqui:
+    container_name: neon-tts-coqui
+    image: ghcr.io/neongeckocom/neon-tts-plugin-coqui:${HTTP_IMAGE_TAG}
+    networks:
+      - diana-backend
+    ports:
+      - "9666:9666"
+    volumes:
+      - xdg:/xdg:rw
+    environment:
+      - XDG_CONFIG_HOME=/xdg/config
+      - XDG_DATA_HOME=/xdg/data
+      - XDG_CACHE_HOME=/xdg/cache
+      - XDG_STATE_HOME=/xdg/state
+  # TODO: stt-nemo
\ No newline at end of file
diff --git a/neon_diana_utils/docker/backend/rabbitmq.conf b/neon_diana_utils/docker/backend/rabbitmq.conf
new file mode 100644
index 00000000..96c25586
--- /dev/null
+++ b/neon_diana_utils/docker/backend/rabbitmq.conf
@@ -0,0 +1,1 @@
+load_definitions = /xdg/config/rabbitmq/rabbitmq.json
\ No newline at end of file
diff --git a/neon_diana_utils/docker/backend/xdg/config/neon/.keep b/neon_diana_utils/docker/backend/xdg/config/neon/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/neon_diana_utils/helm_charts/diana-backend/.helmignore b/neon_diana_utils/helm_charts/diana-backend/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/diana-backend/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/diana-backend/Chart.yaml b/neon_diana_utils/helm_charts/diana-backend/Chart.yaml
new file mode 100644
index 00000000..828b3aa4
--- /dev/null
+++ b/neon_diana_utils/helm_charts/diana-backend/Chart.yaml
@@ -0,0 +1,39 @@
+apiVersion: v2
+name: diana
+description: A Helm chart to deploy the Neon Diana backend
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "1.0.0"
+
+dependencies:
+# nginx needs to deploy to its own namespace
+#  - name: ingress-nginx
+#    version: 4.6.0
+#    repository: https://kubernetes.github.io/ingress-nginx
+  - name: rabbitmq
+    version: 11.13.0
+    repository: https://charts.bitnami.com/bitnami
+  - name: diana-http
+    version: 0.0.1
+    repository: file://../http-services
+  - name: diana-mq
+    version: 0.0.4
+    repository: file://../mq-services
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/diana-backend/templates/_helpers.tpl b/neon_diana_utils/helm_charts/diana-backend/templates/_helpers.tpl
new file mode 100644
index 00000000..a6d9ad0c
--- /dev/null
+++ b/neon_diana_utils/helm_charts/diana-backend/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "diana.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "diana.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "diana.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "diana.labels" -}}
+helm.sh/chart: {{ include "diana.chart" . }}
+{{ include "diana.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "diana.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "diana.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "diana.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "diana.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/diana-backend/templates/config-secret.yaml b/neon_diana_utils/helm_charts/diana-backend/templates/config-secret.yaml
new file mode 100644
index 00000000..7bf1cb0f
--- /dev/null
+++ b/neon_diana_utils/helm_charts/diana-backend/templates/config-secret.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ .Values.configSecret }}
+type: Opaque
+data:
+  "diana.yaml": |-
+    {{ .Files.Get "diana.yaml" | b64enc }}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/diana-backend/templates/letsencrypt-issuer.yaml b/neon_diana_utils/helm_charts/diana-backend/templates/letsencrypt-issuer.yaml
new file mode 100644
index 00000000..44a38475
--- /dev/null
+++ b/neon_diana_utils/helm_charts/diana-backend/templates/letsencrypt-issuer.yaml
@@ -0,0 +1,14 @@
+apiVersion: cert-manager.io/v1
+kind: Issuer
+metadata:
+  name: {{ .Values.letsencrypt.sslSecret }}
+spec:
+  acme:
+    server: {{ .Values.letsencrypt.server }}
+    email: {{ .Values.letsencrypt.email }}
+    privateKeySecretRef:
+      name: {{ .Values.letsencrypt.sslSecret }}
+    solvers:
+      - http01:
+          ingress:
+            class: nginx
diff --git a/neon_diana_utils/helm_charts/diana-backend/templates/rabbitmq-secret.yaml b/neon_diana_utils/helm_charts/diana-backend/templates/rabbitmq-secret.yaml
new file mode 100644
index 00000000..267a35af
--- /dev/null
+++ b/neon_diana_utils/helm_charts/diana-backend/templates/rabbitmq-secret.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ .Values.rabbitmq.loadDefinition.existingSecret }}
+type: Opaque
+data:
+  "load_definition.json": |-
+    {{ .Files.Get "rabbitmq.json" | b64enc }}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/diana-backend/values.yaml b/neon_diana_utils/helm_charts/diana-backend/values.yaml
new file mode 100644
index 00000000..37cb92d4
--- /dev/null
+++ b/neon_diana_utils/helm_charts/diana-backend/values.yaml
@@ -0,0 +1,65 @@
+# This Chart will deploy Diana backend services, including RabbitMQ,
+# HTTP Services with associated ingress and certificates, MQ Services,
+# and configuration including secrets with keys and passwords.
+
+# TODO: Update `domain`
+domain: &domain diana.k8s
+# TODO: Update tag to `dev` or `latest`
+mqServiceTag: &mq_tag dev
+
+configSecret: diana-config
+# Note: Changes here need to be reflected in all MQ containers
+
+letsencrypt:
+  sslSecret: letsencrypt-ssl
+  # Note: This should match `diana-http.ingress.tlsSecretName
+  server: https://acme-staging-v02.api.letsencrypt.org/directory
+  email: developers@neon.ai
+  # TODO: Update server and email to valid options
+  # https://acme-v02.api.letsencrypt.org/directory
+
+# Deploys RabbitMQ with specified auth credentials
+rabbitmq:
+  nameOverride: &name_rmq neon-rabbitmq
+  fullnameOverride: *name_rmq
+  persistence:
+    enabled: false
+  commonLabels:
+    neon.project.name: diana
+    neon.diana.service: *name_rmq
+    neon.service.class: communication
+  loadDefinition:
+    enabled: true
+    existingSecret: rmq-config
+  extraConfiguration: |
+    load_definitions = /app/load_definition.json
+
+# Deploys HTTP Services
+diana-http:
+  domain: *domain
+  ingress:
+    tlsSecretName: letsencrypt-ssl
+    extraRules:
+      - host: mqadmin
+        serviceName: *name_rmq
+        servicePort: 15672
+
+# Deploys MQ Services
+diana-mq:
+  neon-api-proxy:
+    image:
+      tag: *mq_tag
+  neon-brands-service:
+    image:
+      tag: *mq_tag
+      pullSecret: github-auth
+  neon-email-proxy:
+    image:
+      tag: *mq_tag
+  neon-metrics-service:
+    image:
+      tag: *mq_tag
+  neon-script-parser:
+    image:
+      tag: *mq_tag
+      pullSecret: github-auth
diff --git a/neon_diana_utils/helm_charts/http-services/Chart.yaml b/neon_diana_utils/helm_charts/http-services/Chart.yaml
new file mode 100644
index 00000000..782f05fd
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/Chart.yaml
@@ -0,0 +1,40 @@
+apiVersion: v2
+name: diana-http
+description: Deploy DIANA HTTP Services
+
+type: application
+version: &chart_version 0.0.1
+appVersion: "1.0.0"
+dependencies:
+  - name: libretranslate
+    alias: libretranslate
+    version: *chart_version
+    repository: file://charts/libretranslate
+  - name: tts-coqui
+    alias: tts-coqui
+    version: *chart_version
+    repository: file://charts/tts-coqui
+  - name: tts-glados
+    alias: tts-glados
+    version: *chart_version
+    repository: file://charts/tts-glados
+  - name: tts-larynx
+    alias: tts-larynx
+    version: *chart_version
+    repository: file://charts/tts-larynx
+  - name: tts-ljspeech
+    alias: tts-ljspeech
+    version: *chart_version
+    repository: file://charts/tts-ljspeech
+  - name: tts-mozilla
+    alias: tts-mozilla
+    version: *chart_version
+    repository: file://charts/tts-mozilla
+  - name: tts-nancy
+    alias: tts-nancy
+    version: *chart_version
+    repository: file://charts/tts-nancy
+  - name: ww-snowboy
+    alias: ww-snowboy
+    version: *chart_version
+    repository: file://charts/ww-snowboy
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/base-http/.helmignore b/neon_diana_utils/helm_charts/http-services/charts/base-http/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/base-http/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/http-services/charts/base-http/Chart.yaml b/neon_diana_utils/helm_charts/http-services/charts/base-http/Chart.yaml
new file mode 100644
index 00000000..52ac8e22
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/base-http/Chart.yaml
@@ -0,0 +1,7 @@
+apiVersion: v2
+name: base-http
+description: Library chart for basic HTTP Services
+type: library
+
+version: 0.0.2
+appVersion: "1.0.0"
diff --git a/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_deployment.tpl b/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_deployment.tpl
new file mode 100644
index 00000000..2c579fd9
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_deployment.tpl
@@ -0,0 +1,43 @@
+{{- define "base-http.deployment" -}}
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ .Chart.Name }}
+spec:
+  replicas: {{ .Values.replicaCount }}
+  selector:
+    matchLabels:
+      neon.diana.service: {{ .Chart.Name }}
+  strategy:
+    type: Recreate
+  template:
+    metadata:
+      annotations:
+        releaseTime: {{ dateInZone "2006-01-02 15:04:05Z" (now) "UTC"| quote }}
+      labels:
+        neon.diana.service: {{ .Chart.Name }}
+        neon.project.name: diana
+        neon.service.class: http-backend
+    spec:
+      containers:
+        - image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          name: {{ .Chart.Name }}
+          ports:
+            - name: {{ .Chart.Name }}
+              containerPort: {{ .Values.servicePort }}
+              protocol: TCP
+          {{- if .Values.configSecret }}
+          volumeMounts:
+            - mountPath: /config/neon
+              name: config
+      volumes:
+        - name: config
+          projected:
+            sources:
+              - secret:
+                  name: {{ .Values.configSecret }}
+      {{- end }}
+      restartPolicy: Always
+{{- end -}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_domain.tpl b/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_domain.tpl
new file mode 100644
index 00000000..cd6f15cb
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_domain.tpl
@@ -0,0 +1,3 @@
+{{- define "service.domain" -}}
+{{- printf "%s.%s" .Values.subdomain .Values.domain -}}
+{{- end }}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_ingress.tpl b/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_ingress.tpl
new file mode 100644
index 00000000..502c8158
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_ingress.tpl
@@ -0,0 +1,58 @@
+{{- define "base-http.ingress" -}}
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := .Chart.Name -}}
+{{- $svcPort := .Values.servicePort -}}
+{{- $hostname := include "service.domain" . -}}
+{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
+  {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
+  {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
+  {{- end }}
+{{- end }}
+{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1
+{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+  name: {{ $fullName }}
+  {{- with .Values.ingress.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+spec:
+  {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
+  ingressClassName: {{ .Values.ingress.className }}
+  {{- end }}
+  {{- if .Values.ingress.tls }}
+  tls:
+    - hosts:
+        - {{ $hostname }}
+      secretName: {{ .Values.ingress.tls.secretName }}
+  {{- end }}
+  rules:
+    {{- range .Values.ingress.hosts }}
+    - host: {{ $hostname }}
+      http:
+        paths:
+          {{- range .paths }}
+          - path: {{ .path }}
+            {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
+            pathType: {{ .pathType }}
+            {{- end }}
+            backend:
+              {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
+              service:
+                name: {{ $fullName }}
+                port:
+                  number: {{ $svcPort }}
+              {{- else }}
+              serviceName: {{ $fullName }}
+              servicePort: {{ $svcPort }}
+              {{- end }}
+          {{- end }}
+    {{- end }}
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_service.tpl b/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_service.tpl
new file mode 100644
index 00000000..cdf38216
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/base-http/templates/_service.tpl
@@ -0,0 +1,19 @@
+{{- define "base-http.service" -}}
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    neon.project.name: diana
+    neon.diana.service: {{ tpl .Chart.Name . }}
+    neon.service.class: http-backend
+  name: {{ .Chart.Name }}
+spec:
+  type: ClusterIP
+  ports:
+    - name: {{ .Chart.Name }}
+      port: {{ .Values.servicePort }}
+      targetPort: {{ .Values.service.targetPort }}
+      protocol: TCP
+  selector:
+    neon.diana.service: {{ .Chart.Name }}
+{{- end -}}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/libretranslate/.helmignore b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/http-services/charts/libretranslate/Chart.yaml b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/Chart.yaml
new file mode 100644
index 00000000..54ac9afa
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: libretranslate
+description: Deploy a Libretranslate Server
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-http
+    version: 0.0.2
+    repository: file://../base-http
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/NOTES.txt b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/NOTES.txt
new file mode 100644
index 00000000..af5379f4
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/NOTES.txt
@@ -0,0 +1,23 @@
+You can access the application at:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+  {{- range .paths }}
+  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.subdomain }}.{{ $.Values.domain }}{{ .path }}
+  {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "lang-libretranslate.fullname" . }})
+  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+  echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "lang-libretranslate.fullname" . }}'
+  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "lang-libretranslate.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+  echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "lang-libretranslate.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+  echo "Visit http://127.0.0.1:8080 to use your application"
+  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
+> This container includes an MQ integration that is not yet implemented in Helm deployments.
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/_helpers.tpl b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/_helpers.tpl
new file mode 100644
index 00000000..983cd4e3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "lang-libretranslate.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "lang-libretranslate.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "lang-libretranslate.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "lang-libretranslate.labels" -}}
+helm.sh/chart: {{ include "lang-libretranslate.chart" . }}
+{{ include "lang-libretranslate.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "lang-libretranslate.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "lang-libretranslate.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "lang-libretranslate.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "lang-libretranslate.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/deployment.yaml b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/deployment.yaml
new file mode 100644
index 00000000..a2446c4b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/ingress.yaml b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/ingress.yaml
new file mode 100644
index 00000000..fb084512
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/ingress.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.ingress" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/service.yaml b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/service.yaml
new file mode 100644
index 00000000..a3493c81
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/libretranslate/values.yaml b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/values.yaml
new file mode 100644
index 00000000..0cc4a8f5
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/values.yaml
@@ -0,0 +1,34 @@
+# Default values for lang-libretranslate.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+subdomain: libretranslate
+domain: diana.local
+servicePort: "5000"
+configSecret: diana-config
+
+image:
+  repository: ghcr.io/neongeckocom/libretranslate
+  pullPolicy: Always
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: latest
+
+service:
+  type: ClusterIP
+  targetPort: "5000"
+
+ingress:
+  enabled: true
+  className: nginx
+  annotations: {}
+    # kubernetes.io/ingress.class: nginx
+    # kubernetes.io/tls-acme: "true"
+  hosts:
+    - paths:
+        - path: /
+          pathType: Prefix
+          backend:
+            service:
+  tls:
+    secretName: tls-letsencrypt-prod
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/.helmignore b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/Chart.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/Chart.yaml
new file mode 100644
index 00000000..37e9fa7b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: tts-coqui
+description: Deploy a Coqui TTS Server
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-http
+    version: 0.0.2
+    repository: file://../base-http
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/NOTES.txt b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/NOTES.txt
new file mode 100644
index 00000000..3cbb4485
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/NOTES.txt
@@ -0,0 +1,22 @@
+You can access the application at:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+  {{- range .paths }}
+  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.subdomain }}.{{ $.Values.domain }}{{ .path }}
+  {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tts-coqui.fullname" . }})
+  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+  echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tts-coqui.fullname" . }}'
+  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tts-coqui.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+  echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tts-coqui.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+  echo "Visit http://127.0.0.1:8080 to use your application"
+  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/_helpers.tpl b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/_helpers.tpl
new file mode 100644
index 00000000..53d80050
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "tts-coqui.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "tts-coqui.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "tts-coqui.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "tts-coqui.labels" -}}
+helm.sh/chart: {{ include "tts-coqui.chart" . }}
+{{ include "tts-coqui.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "tts-coqui.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "tts-coqui.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "tts-coqui.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "tts-coqui.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/deployment.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/deployment.yaml
new file mode 100644
index 00000000..a2446c4b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/ingress.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/ingress.yaml
new file mode 100644
index 00000000..b7af14d9
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/ingress.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.ingress" .}}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/service.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/service.yaml
new file mode 100644
index 00000000..a3493c81
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/values.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/values.yaml
new file mode 100644
index 00000000..d44e9ee7
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/values.yaml
@@ -0,0 +1,33 @@
+# Default values for tts-coqui.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+subdomain: coqui
+domain: diana.local
+servicePort: "9666"
+
+image:
+  repository: ghcr.io/neongeckocom/neon-tts-plugin-coqui
+  pullPolicy: Always
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: dev
+
+service:
+  type: ClusterIP
+  targetPort: "9666"
+
+ingress:
+  enabled: true
+  className: nginx
+  annotations: {}
+    # kubernetes.io/ingress.class: nginx
+    # kubernetes.io/tls-acme: "true"
+  hosts:
+    - paths:
+        - path: /
+          pathType: Prefix
+          backend:
+            service:
+  tls:
+    secretName: tls-letsencrypt-prod
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-glados/.helmignore b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-glados/Chart.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/Chart.yaml
new file mode 100644
index 00000000..c4ec8e7a
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: tts-glados
+description: Deploy a Glados TTS Server
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-http
+    version: 0.0.2
+    repository: file://../base-http
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/NOTES.txt b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/NOTES.txt
new file mode 100644
index 00000000..b07b4075
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/NOTES.txt
@@ -0,0 +1,23 @@
+You can access the application at:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+  {{- range .paths }}
+  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.subdomain }}.{{ $.Values.domain }}{{ .path }}
+  {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tts-glados.fullname" . }})
+  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+  echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tts-glados.fullname" . }}'
+  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tts-glados.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+  echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tts-glados.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+  echo "Visit http://127.0.0.1:8080 to use your application"
+  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
+> NOTE: This container is still under development
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/_helpers.tpl b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/_helpers.tpl
new file mode 100644
index 00000000..64bc7bf5
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "tts-glados.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "tts-glados.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "tts-glados.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "tts-glados.labels" -}}
+helm.sh/chart: {{ include "tts-glados.chart" . }}
+{{ include "tts-glados.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "tts-glados.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "tts-glados.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "tts-glados.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "tts-glados.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/deployment.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/deployment.yaml
new file mode 100644
index 00000000..a2446c4b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/ingress.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/ingress.yaml
new file mode 100644
index 00000000..b7af14d9
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/ingress.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.ingress" .}}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/service.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/service.yaml
new file mode 100644
index 00000000..a3493c81
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-glados/values.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/values.yaml
new file mode 100644
index 00000000..449b357a
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/values.yaml
@@ -0,0 +1,33 @@
+# Default values for tts-glados.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+subdomain: glados
+domain: diana.local
+servicePort: "9666"
+
+image:
+  repository: ghcr.io/neongeckocom/neon-tts-plugin-glados
+  pullPolicy: Always
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: dev
+
+service:
+  type: ClusterIP
+  targetPort: "9666"
+
+ingress:
+  enabled: true
+  className: nginx
+  annotations: {}
+    # kubernetes.io/ingress.class: nginx
+    # kubernetes.io/tls-acme: "true"
+  hosts:
+    - paths:
+        - path: /
+          pathType: Prefix
+          backend:
+            service:
+  tls:
+    secretName: tls-letsencrypt-prod
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/.helmignore b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/Chart.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/Chart.yaml
new file mode 100644
index 00000000..c1613d7f
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: tts-larynx
+description: Deploy a Larynx TTS Server
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-http
+    version: 0.0.2
+    repository: file://../base-http
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/NOTES.txt b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/NOTES.txt
new file mode 100644
index 00000000..834ac038
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/NOTES.txt
@@ -0,0 +1,23 @@
+You can access the application at:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+  {{- range .paths }}
+  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.subdomain }}.{{ $.Values.domain }}{{ .path }}
+  {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tts-larynx.fullname" . }})
+  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+  echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tts-larynx.fullname" . }}'
+  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tts-larynx.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+  echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tts-larynx.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+  echo "Visit http://127.0.0.1:8080 to use your application"
+  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
+> NOTE: This container is still under development
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/_helpers.tpl b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/_helpers.tpl
new file mode 100644
index 00000000..c07c7583
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "tts-larynx.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "tts-larynx.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "tts-larynx.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "tts-larynx.labels" -}}
+helm.sh/chart: {{ include "tts-larynx.chart" . }}
+{{ include "tts-larynx.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "tts-larynx.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "tts-larynx.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "tts-larynx.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "tts-larynx.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/deployment.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/deployment.yaml
new file mode 100644
index 00000000..a2446c4b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/ingress.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/ingress.yaml
new file mode 100644
index 00000000..b7af14d9
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/ingress.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.ingress" .}}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/service.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/service.yaml
new file mode 100644
index 00000000..a3493c81
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/values.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/values.yaml
new file mode 100644
index 00000000..028cdce9
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/values.yaml
@@ -0,0 +1,33 @@
+# Default values for tts-larynx.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+subdomain: larynx
+domain: diana.local
+servicePort: "5002"
+
+image:
+  repository: rhasspy/larynx
+  pullPolicy: Always
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: latest
+
+service:
+  type: ClusterIP
+  targetPort: "5002"
+
+ingress:
+  enabled: true
+  className: nginx
+  annotations: {}
+    # kubernetes.io/ingress.class: nginx
+    # kubernetes.io/tls-acme: "true"
+  hosts:
+    - paths:
+        - path: /
+          pathType: Prefix
+          backend:
+            service:
+  tls:
+    secretName: tls-letsencrypt-prod
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/.helmignore b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/Chart.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/Chart.yaml
new file mode 100644
index 00000000..a0f5b734
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: tts-ljspeech
+description: Deploy a LJSpeech TTS Server
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-http
+    version: 0.0.2
+    repository: file://../base-http
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/NOTES.txt b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/NOTES.txt
new file mode 100644
index 00000000..90b65f37
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/NOTES.txt
@@ -0,0 +1,23 @@
+You can access the application at:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+  {{- range .paths }}
+  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.subdomain }}.{{ $.Values.domain }}{{ .path }}
+  {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tts-ljspeech.fullname" . }})
+  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+  echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tts-ljspeech.fullname" . }}'
+  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tts-ljspeech.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+  echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tts-ljspeech.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+  echo "Visit http://127.0.0.1:8080 to use your application"
+  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
+> NOTE: This container is still under development
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/_helpers.tpl b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/_helpers.tpl
new file mode 100644
index 00000000..3678c067
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "tts-ljspeech.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "tts-ljspeech.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "tts-ljspeech.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "tts-ljspeech.labels" -}}
+helm.sh/chart: {{ include "tts-ljspeech.chart" . }}
+{{ include "tts-ljspeech.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "tts-ljspeech.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "tts-ljspeech.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "tts-ljspeech.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "tts-ljspeech.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/deployment.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/deployment.yaml
new file mode 100644
index 00000000..a2446c4b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/ingress.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/ingress.yaml
new file mode 100644
index 00000000..b7af14d9
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/ingress.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.ingress" .}}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/service.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/service.yaml
new file mode 100644
index 00000000..a3493c81
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/values.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/values.yaml
new file mode 100644
index 00000000..034d1c4d
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/values.yaml
@@ -0,0 +1,33 @@
+# Default values for tts-ljspeech.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+subdomain: ljspeech
+domain: diana.local
+servicePort: "9000"
+
+image:
+  repository: ghcr.io/openvoiceos/mimic2-ljspeech
+  pullPolicy: Always
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: dev
+
+service:
+  type: ClusterIP
+  targetPort: "9000"
+
+ingress:
+  enabled: true
+  className: nginx
+  annotations: {}
+    # kubernetes.io/ingress.class: nginx
+    # kubernetes.io/tls-acme: "true"
+  hosts:
+    - paths:
+        - path: /
+          pathType: Prefix
+          backend:
+            service:
+  tls:
+    secretName: tls-letsencrypt-prod
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/.helmignore b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/Chart.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/Chart.yaml
new file mode 100644
index 00000000..02a5bcdf
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: tts-mozilla
+description: Deploy a Mozilla TTS Server
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-http
+    version: 0.0.2
+    repository: file://../base-http
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/NOTES.txt b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/NOTES.txt
new file mode 100644
index 00000000..8231be3d
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/NOTES.txt
@@ -0,0 +1,23 @@
+You can access the application at:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+  {{- range .paths }}
+  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.subdomain }}.{{ $.Values.domain }}{{ .path }}
+  {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tts-mozilla.fullname" . }})
+  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+  echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tts-mozilla.fullname" . }}'
+  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tts-mozilla.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+  echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tts-mozilla.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+  echo "Visit http://127.0.0.1:8080 to use your application"
+  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
+> NOTE: This container is still under development
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/_helpers.tpl b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/_helpers.tpl
new file mode 100644
index 00000000..785ac78d
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "tts-mozilla.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "tts-mozilla.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "tts-mozilla.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "tts-mozilla.labels" -}}
+helm.sh/chart: {{ include "tts-mozilla.chart" . }}
+{{ include "tts-mozilla.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "tts-mozilla.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "tts-mozilla.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "tts-mozilla.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "tts-mozilla.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/deployment.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/deployment.yaml
new file mode 100644
index 00000000..a2446c4b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/ingress.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/ingress.yaml
new file mode 100644
index 00000000..b7af14d9
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/ingress.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.ingress" .}}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/service.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/service.yaml
new file mode 100644
index 00000000..a3493c81
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/values.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/values.yaml
new file mode 100644
index 00000000..30a0b79d
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/values.yaml
@@ -0,0 +1,33 @@
+# Default values for tts-mozilla.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+subdomain: mtts
+domain: diana.local
+servicePort: "5002"
+
+image:
+  repository: synesthesiam/mozilla-tts
+  pullPolicy: Always
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: latest
+
+service:
+  type: ClusterIP
+  targetPort: "5002"
+
+ingress:
+  enabled: true
+  className: nginx
+  annotations: {}
+    # kubernetes.io/ingress.class: nginx
+    # kubernetes.io/tls-acme: "true"
+  hosts:
+    - paths:
+        - path: /
+          pathType: Prefix
+          backend:
+            service:
+  tls:
+    secretName: tls-letsencrypt-prod
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/.helmignore b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/Chart.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/Chart.yaml
new file mode 100644
index 00000000..9569f0ee
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: tts-nancy
+description: Deploy a Nancy TTS Server
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-http
+    version: 0.0.2
+    repository: file://../base-http
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/NOTES.txt b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/NOTES.txt
new file mode 100644
index 00000000..2dcd251a
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/NOTES.txt
@@ -0,0 +1,22 @@
+You can access the application at:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+  {{- range .paths }}
+  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.subdomain }}.{{ $.Values.domain }}{{ .path }}
+  {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tts-nancy.fullname" . }})
+  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+  echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tts-nancy.fullname" . }}'
+  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tts-nancy.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+  echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tts-nancy.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+  echo "Visit http://127.0.0.1:8080 to use your application"
+  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/_helpers.tpl b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/_helpers.tpl
new file mode 100644
index 00000000..4f27f350
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "tts-nancy.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "tts-nancy.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "tts-nancy.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "tts-nancy.labels" -}}
+helm.sh/chart: {{ include "tts-nancy.chart" . }}
+{{ include "tts-nancy.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "tts-nancy.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "tts-nancy.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "tts-nancy.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "tts-nancy.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/deployment.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/deployment.yaml
new file mode 100644
index 00000000..a2446c4b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/ingress.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/ingress.yaml
new file mode 100644
index 00000000..b7af14d9
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/ingress.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.ingress" .}}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/service.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/service.yaml
new file mode 100644
index 00000000..a3493c81
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/values.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/values.yaml
new file mode 100644
index 00000000..0eacc0ab
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/values.yaml
@@ -0,0 +1,33 @@
+# Default values for tts-nancy.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+subdomain: nancy
+domain: diana.local
+servicePort: "9000"
+
+image:
+  repository: ghcr.io/openvoiceos/mimic2-nancy
+  pullPolicy: Always
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: dev
+
+service:
+  type: ClusterIP
+  targetPort: "9000"
+
+ingress:
+  enabled: true
+  className: nginx
+  annotations: {}
+    # kubernetes.io/ingress.class: nginx
+    # kubernetes.io/tls-acme: "true"
+  hosts:
+    - paths:
+        - path: /
+          pathType: Prefix
+          backend:
+            service:
+  tls:
+    secretName: tls-letsencrypt-prod
diff --git a/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/.helmignore b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/Chart.yaml b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/Chart.yaml
new file mode 100644
index 00000000..a1f01781
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: ww-snowboy
+description: Deploy Snowboy Wake Word Trainer
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-http
+    version: 0.0.2
+    repository: file://../base-http
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/NOTES.txt b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/NOTES.txt
new file mode 100644
index 00000000..2eba2282
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/NOTES.txt
@@ -0,0 +1,23 @@
+You can access the application at:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+  {{- range .paths }}
+  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.subdomain }}.{{ $.Values.domain }}{{ .path }}
+  {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "ww-snowboy.fullname" . }})
+  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+  echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "ww-snowboy.fullname" . }}'
+  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "ww-snowboy.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+  echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "ww-snowboy.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+  echo "Visit http://127.0.0.1:8080 to use your application"
+  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
+> NOTE: This container is not properly tagged by the author
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/_helpers.tpl b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/_helpers.tpl
new file mode 100644
index 00000000..daf66b11
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "ww-snowboy.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "ww-snowboy.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "ww-snowboy.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "ww-snowboy.labels" -}}
+helm.sh/chart: {{ include "ww-snowboy.chart" . }}
+{{ include "ww-snowboy.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "ww-snowboy.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "ww-snowboy.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "ww-snowboy.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "ww-snowboy.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/deployment.yaml b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/deployment.yaml
new file mode 100644
index 00000000..a2446c4b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/ingress.yaml b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/ingress.yaml
new file mode 100644
index 00000000..fb084512
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/ingress.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.ingress" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/service.yaml b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/service.yaml
new file mode 100644
index 00000000..a3493c81
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-http.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/values.yaml b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/values.yaml
new file mode 100644
index 00000000..4bb90a71
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/values.yaml
@@ -0,0 +1,32 @@
+# Default values for ww-snowboy.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+subdomain: snowboy
+domain: diana.local
+servicePort: "8000"
+image:
+  repository: rhasspy/snowboy-seasalt
+  pullPolicy: Always
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: latest
+
+service:
+  type: ClusterIP
+  targetPort: "8000"
+
+ingress:
+  enabled: true
+  className: nginx
+  annotations: {}
+    # kubernetes.io/ingress.class: nginx
+    # kubernetes.io/tls-acme: "true"
+  hosts:
+    - paths:
+        - path: /
+          pathType: Prefix
+          backend:
+            service:
+  tls:
+    secretName: tls-letsencrypt-prod
diff --git a/neon_diana_utils/helm_charts/http-services/templates/ingress.yaml b/neon_diana_utils/helm_charts/http-services/templates/ingress.yaml
new file mode 100644
index 00000000..1f5010ff
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/templates/ingress.yaml
@@ -0,0 +1,39 @@
+{{ if .Values.ingress.enabled }}
+{{- $domain := .Values.domain -}}
+{{- $rules := concat .Values.ingress.rules .Values.ingress.extraRules }}
+{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1
+{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+  name: ingress-diana-http
+  annotations:
+    kubernetes.io/ingress.class: {{ .Values.ingress.ingressClassName }}
+    cert-manager.io/issuer: {{ .Values.ingress.tlsSecretName }}
+spec:
+  tls:
+    - secretName: {{ .Values.ingress.tlsSecretName }}
+      hosts:
+        {{- range $rules }}
+        - {{ printf "%s.%s " .host $domain }}
+        {{- end }}
+
+  rules:
+    {{- range $rules }}
+    - host: {{ printf "%s.%s " .host $domain }}
+      http:
+        paths:
+          - path: /
+            pathType: Prefix
+            backend:
+              service:
+                name: {{ .serviceName }}
+                port:
+                  number: {{ .servicePort}}
+    {{- end }}
+
+  {{- end }}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/http-services/values.yaml b/neon_diana_utils/helm_charts/http-services/values.yaml
new file mode 100644
index 00000000..c6feb9d0
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/values.yaml
@@ -0,0 +1,81 @@
+containerPort: &default_port "4430"
+domain: diana.k8s
+libretranslate:
+  subdomain: &host_libretranslate libretranslate
+  servicePort: *default_port
+  ingress:
+    enabled: False
+#  image.tag:
+tts-coqui:
+  subdomain: &host_coqui coqui
+  servicePort: *default_port
+  ingress:
+    enabled: False
+#  image.tag:
+tts-glados:
+  subdomain: &host_glados glados
+  servicePort: *default_port
+  ingress:
+    enabled: False
+#  image.tag:
+tts-larynx:
+  subdomain: &host_larynx larynx
+  servicePort: *default_port
+  ingress:
+    enabled: False
+#  image.tag:
+tts-ljspeech:
+  subdomain: &host_ljspeech ljspeech
+  servicePort: *default_port
+  ingress:
+    enabled: False
+#  image.tag:
+tts-mozilla:
+  subdomain: &host_mtts mtts
+  servicePort: *default_port
+  ingress:
+    enabled: False
+#  image.tag:
+tts-nancy:
+  subdomain: &host_nancy nancy
+  servicePort: *default_port
+  ingress:
+    enabled: False
+#  image.tag:
+ww-snowboy:
+  subdomain: &host_snowboy snowboy
+  servicePort: *default_port
+  ingress:
+    enabled: False
+#  image.tag:
+
+ingress:
+  enabled: True
+  ingressClassName: nginx
+  tlsSecretName: tls-letsencrypt-prod
+  rules:
+    - host: *host_libretranslate
+      serviceName: libretranslate
+      servicePort: *default_port
+    - host: *host_coqui
+      serviceName: tts-coqui
+      servicePort: *default_port
+    - host: *host_glados
+      serviceName: tts-glados
+      servicePort: *default_port
+    - host: *host_larynx
+      serviceName: tts-larynx
+      servicePort: *default_port
+    - host: *host_ljspeech
+      serviceName: tts-ljspeech
+      servicePort: *default_port
+    - host: *host_mtts
+      serviceName: tts-mozilla
+      servicePort: *default_port
+    - host: *host_nancy
+      serviceName: tts-nancy
+      servicePort: *default_port
+    - host: *host_snowboy
+      serviceName: ww-snowboy
+      servicePort: *default_port
+  extraRules: []
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/ingress-common/.helmignore b/neon_diana_utils/helm_charts/ingress-common/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/ingress-common/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/ingress-common/Chart.yaml b/neon_diana_utils/helm_charts/ingress-common/Chart.yaml
new file mode 100644
index 00000000..07cae0bb
--- /dev/null
+++ b/neon_diana_utils/helm_charts/ingress-common/Chart.yaml
@@ -0,0 +1,15 @@
+apiVersion: v2
+name: ingress-common
+description: Installs common ingress and certificate components
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: ingress-nginx
+    version: 4.6.0
+    repository: https://kubernetes.github.io/ingress-nginx
+  - name: cert-manager
+    version: 1.11.1
+    repository: https://charts.jetstack.io
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/ingress-common/templates/namespace.yaml b/neon_diana_utils/helm_charts/ingress-common/templates/namespace.yaml
new file mode 100644
index 00000000..661039bb
--- /dev/null
+++ b/neon_diana_utils/helm_charts/ingress-common/templates/namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: cert-manager
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/ingress-common/values.yaml b/neon_diana_utils/helm_charts/ingress-common/values.yaml
new file mode 100644
index 00000000..341075aa
--- /dev/null
+++ b/neon_diana_utils/helm_charts/ingress-common/values.yaml
@@ -0,0 +1,13 @@
+ingress-nginx:
+  nameOverride: &name ingress-nginx
+  fullnameOverride: *name
+
+  serviceAccount:
+    create: true
+    name: ingress-nginx
+  tcp:
+    5672: "default/neon-rabbitmq:5672"
+# Note that all RabbitMQ services will use AMQP port 5672 internally
+cert-manager:
+  namespace: cert-manager
+  installCRDs: True
diff --git a/neon_diana_utils/helm_charts/mq-services/Chart.yaml b/neon_diana_utils/helm_charts/mq-services/Chart.yaml
new file mode 100644
index 00000000..41997724
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/Chart.yaml
@@ -0,0 +1,32 @@
+apiVersion: v2
+name: diana-mq
+description: Deploy DIANA MQ Services
+
+type: application
+version: 0.0.4
+appVersion: "1.0.0"
+dependencies:
+  - name: neon-api-proxy
+    alias: neon-api-proxy
+    version: 0.0.1
+    repository: file://charts/neon-api-proxy
+  - name: neon-brands-service
+    alias: neon-brands-service
+    version: 0.0.1
+    repository: file://charts/neon-brands-service
+  - name: neon-email-proxy
+    alias: neon-email-proxy
+    version: 0.0.1
+    repository: file://charts/neon-email-proxy
+  - name: neon-metrics-service
+    alias: neon-metrics-service
+    version: 0.0.2
+    repository: file://charts/neon-metrics-service
+  - name: neon-script-parser
+    alias: neon-script-parser
+    version: 0.0.1
+    repository: file://charts/neon-script-parser
+  - name: neon-llm-chatgpt
+    alias: neon-llm-chatgpt
+    version: 0.0.1
+    repository: file://charts/neon-llm-chatgpt
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/base-mq/.helmignore b/neon_diana_utils/helm_charts/mq-services/charts/base-mq/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/base-mq/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/base-mq/Chart.yaml b/neon_diana_utils/helm_charts/mq-services/charts/base-mq/Chart.yaml
new file mode 100644
index 00000000..f4c27b3d
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/base-mq/Chart.yaml
@@ -0,0 +1,7 @@
+apiVersion: v2
+name: base-mq
+description: Library chart for basic MQ Services
+
+type: library
+version: 0.0.3
+appVersion: "1.0.0"
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/base-mq/templates/_deployment.tpl b/neon_diana_utils/helm_charts/mq-services/charts/base-mq/templates/_deployment.tpl
new file mode 100644
index 00000000..edf10dfe
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/base-mq/templates/_deployment.tpl
@@ -0,0 +1,54 @@
+{{- define "base-mq.deployment" -}}
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ .Chart.Name }}
+spec:
+  replicas:  {{ .Values.replicaCount }}
+  selector:
+    matchLabels:
+      neon.diana.service: {{ .Chart.Name }}
+  strategy:
+    type: Recreate
+  template:
+    metadata:
+      annotations:
+        releaseTime: {{ dateInZone "2006-01-02 15:04:05Z" (now) "UTC"| quote }}
+      labels:
+        neon.diana.service: {{ .Chart.Name }}
+        neon.project.name: diana
+        neon.service.class: mq-backend
+    spec:
+      restartPolicy: Always
+      containers:
+        - image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          name: {{ .Chart.Name }}
+          volumeMounts:
+            - mountPath: /config/neon
+              name: config
+            {{- if .Values.persistentVolumeClaim }}
+            - mountPath: {{ .Values.persistentVolumeClaim.containerPath }}
+              name: {{ .Values.persistentVolumeClaim.name }}
+            {{- end -}}
+          {{- if .Values.resources }}
+          resources:
+          {{- toYaml $.Values.resources | nindent 12 -}}
+          {{ end }}
+      volumes:
+        - name: config
+          projected:
+            sources:
+              - secret:
+                  name: {{ .Values.configSecret }}
+        {{- if .Values.persistentVolumeClaim }}
+        - name: {{ .Values.persistentVolumeClaim.name }}
+          persistentVolumeClaim:
+            claimName: {{ .Values.persistentVolumeClaim.claimName }}
+        {{- end -}}
+      {{- if .Values.image.pullSecret }}
+      imagePullSecrets:
+        - name: {{ .Values.image.pullSecret }}
+      {{- end -}}
+{{- end -}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/base-mq/templates/_service.tpl b/neon_diana_utils/helm_charts/mq-services/charts/base-mq/templates/_service.tpl
new file mode 100644
index 00000000..abaeced7
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/base-mq/templates/_service.tpl
@@ -0,0 +1,18 @@
+{{- define "base-mq.service" -}}
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    neon.project.name: diana
+    neon.diana.service: {{ .Chart.Name }}
+    neon.service.class: mq-backend
+  name: {{ .Chart.Name }}
+spec:
+  clusterIP: None
+  ports:
+    - name: headless
+      port: 55555
+      targetPort: 0
+  selector:
+    neon.diana.service: {{ .Chart.Name }}
+{{- end -}}
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/.helmignore b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/Chart.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/Chart.yaml
new file mode 100644
index 00000000..e0371266
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: neon-api-proxy
+description: Deploy an MQ API Proxy
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-mq
+    version: 0.0.3
+    repository: file://../base-mq
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/templates/_helpers.tpl b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/templates/_helpers.tpl
new file mode 100644
index 00000000..27e7fa6a
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "neon-api-proxy.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "neon-api-proxy.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "neon-api-proxy.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "neon-api-proxy.labels" -}}
+helm.sh/chart: {{ include "neon-api-proxy.chart" . }}
+{{ include "neon-api-proxy.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "neon-api-proxy.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "neon-api-proxy.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "neon-api-proxy.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "neon-api-proxy.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/templates/deployment.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/templates/deployment.yaml
new file mode 100644
index 00000000..41547ae7
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/templates/service.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/templates/service.yaml
new file mode 100644
index 00000000..80730915
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/values.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/values.yaml
new file mode 100644
index 00000000..a0115c44
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-api-proxy/values.yaml
@@ -0,0 +1,6 @@
+replicaCount: 1
+configSecret: diana-config
+image:
+  repository: ghcr.io/neongeckocom/neon_api_proxy
+  pullPolicy: Always
+  tag: dev
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/.helmignore b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/Chart.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/Chart.yaml
new file mode 100644
index 00000000..9776ae14
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: neon-brands-service
+description: Deploy an MQ Brands Service
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-mq
+    version: 0.0.3
+    repository: file://../base-mq
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/templates/_helpers.tpl b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/templates/_helpers.tpl
new file mode 100644
index 00000000..3cef2cc9
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "neon-brands-service.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "neon-brands-service.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "neon-brands-service.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "neon-brands-service.labels" -}}
+helm.sh/chart: {{ include "neon-brands-service.chart" . }}
+{{ include "neon-brands-service.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "neon-brands-service.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "neon-brands-service.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "neon-brands-service.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "neon-brands-service.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/templates/deployment.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/templates/deployment.yaml
new file mode 100644
index 00000000..41547ae7
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/templates/service.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/templates/service.yaml
new file mode 100644
index 00000000..80730915
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/values.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/values.yaml
new file mode 100644
index 00000000..6459108e
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-brands-service/values.yaml
@@ -0,0 +1,7 @@
+replicaCount: 1
+configSecret: diana-config
+image:
+  pullSecret: github-auth
+  repository: ghcr.io/neongeckocom/neon_brands_service
+  pullPolicy: Always
+  tag: dev
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/.helmignore b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/Chart.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/Chart.yaml
new file mode 100644
index 00000000..41b8d174
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: neon-email-proxy
+description: Deploy an Email Proxy Service
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-mq
+    version: 0.0.3
+    repository: file://../base-mq
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/templates/_helpers.tpl b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/templates/_helpers.tpl
new file mode 100644
index 00000000..12c23d48
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "neon-email-proxy.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "neon-email-proxy.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "neon-email-proxy.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "neon-email-proxy.labels" -}}
+helm.sh/chart: {{ include "neon-email-proxy.chart" . }}
+{{ include "neon-email-proxy.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "neon-email-proxy.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "neon-email-proxy.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "neon-email-proxy.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "neon-email-proxy.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/templates/deployment.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/templates/deployment.yaml
new file mode 100644
index 00000000..41547ae7
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/templates/service.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/templates/service.yaml
new file mode 100644
index 00000000..80730915
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/values.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/values.yaml
new file mode 100644
index 00000000..9c3bb59e
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-email-proxy/values.yaml
@@ -0,0 +1,6 @@
+replicaCount: 1
+configSecret: diana-config
+image:
+  repository: ghcr.io/neongeckocom/neon_email_proxy
+  pullPolicy: Always
+  tag: dev
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/.helmignore b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/Chart.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/Chart.yaml
new file mode 100644
index 00000000..f52b7bc9
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: neon-llm-chatgpt
+description: Deploy an LLM proxy for Chat GPT
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-mq
+    version: 0.0.3
+    repository: file://../base-mq
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/templates/_helpers.tpl b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/templates/_helpers.tpl
new file mode 100644
index 00000000..8afd6ed2
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "neon-llm-service.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "neon-llm-service.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "neon-llm-service.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "neon-llm-service.labels" -}}
+helm.sh/chart: {{ include "neon-llm-service.chart" . }}
+{{ include "neon-llm-service.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "neon-llm-service.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "neon-llm-service.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "neon-llm-service.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "neon-llm-service.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/templates/deployment.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/templates/deployment.yaml
new file mode 100644
index 00000000..41547ae7
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/templates/service.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/templates/service.yaml
new file mode 100644
index 00000000..80730915
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/values.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/values.yaml
new file mode 100644
index 00000000..a525745b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-chatgpt/values.yaml
@@ -0,0 +1,6 @@
+replicaCount: 1
+configSecret: diana-config
+image:
+  repository: ghcr.io/neongeckocom/neon-llm-chatgpt
+  pullPolicy: Always
+  tag: dev
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/.helmignore b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/Chart.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/Chart.yaml
new file mode 100644
index 00000000..58060a4d
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: neon-llm-fastchat
+description: Deploy an LLM proxy for FastChat
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-mq
+    version: 0.0.3
+    repository: file://../base-mq
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/templates/_helpers.tpl b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/templates/_helpers.tpl
new file mode 100644
index 00000000..8afd6ed2
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "neon-llm-service.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "neon-llm-service.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "neon-llm-service.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "neon-llm-service.labels" -}}
+helm.sh/chart: {{ include "neon-llm-service.chart" . }}
+{{ include "neon-llm-service.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "neon-llm-service.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "neon-llm-service.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "neon-llm-service.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "neon-llm-service.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/templates/deployment.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/templates/deployment.yaml
new file mode 100644
index 00000000..41547ae7
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/templates/service.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/templates/service.yaml
new file mode 100644
index 00000000..80730915
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/values.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/values.yaml
new file mode 100644
index 00000000..19878512
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-llm-fastchat/values.yaml
@@ -0,0 +1,10 @@
+replicaCount: 1
+configSecret: diana-config
+image:
+  repository: ghcr.io/neongeckocom/neon-llm-fastchat
+  pullPolicy: Always
+  tag: dev
+resources:
+  requests:
+    memory: "4Gi"
+    cpu: "2.0"
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/.helmignore b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/Chart.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/Chart.yaml
new file mode 100644
index 00000000..82813bfd
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: neon-metrics-service
+description: Deploy a Metrics Collector Service
+
+type: application
+version: 0.0.2
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-mq
+    version: 0.0.3
+    repository: file://../base-mq
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/_helpers.tpl b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/_helpers.tpl
new file mode 100644
index 00000000..12c23d48
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "neon-email-proxy.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "neon-email-proxy.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "neon-email-proxy.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "neon-email-proxy.labels" -}}
+helm.sh/chart: {{ include "neon-email-proxy.chart" . }}
+{{ include "neon-email-proxy.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "neon-email-proxy.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "neon-email-proxy.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "neon-email-proxy.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "neon-email-proxy.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/deployment.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/deployment.yaml
new file mode 100644
index 00000000..41547ae7
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/persistent-volume-claim.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/persistent-volume-claim.yaml
new file mode 100644
index 00000000..51a837f6
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/persistent-volume-claim.yaml
@@ -0,0 +1,10 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  name: {{ .Values.persistentVolumeClaim.claimName }}
+spec:
+  accessModes:
+    - ReadWriteOnce
+  resources:
+    requests:
+      storage: 1Gi
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/service.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/service.yaml
new file mode 100644
index 00000000..80730915
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/values.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/values.yaml
new file mode 100644
index 00000000..c30a8152
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-metrics-service/values.yaml
@@ -0,0 +1,10 @@
+replicaCount: 1
+configSecret: diana-config
+image:
+  repository: ghcr.io/neongeckocom/neon_metrics_service
+  pullPolicy: Always
+  tag: dev
+persistentVolumeClaim:
+  name: metrics
+  claimName: metrics
+  containerPath: /data
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/.helmignore b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/Chart.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/Chart.yaml
new file mode 100644
index 00000000..5d9e4908
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: neon-script-parser
+description: Deploy an MQ CCL Script Parser Service
+
+type: application
+version: 0.0.1
+appVersion: "1.0.0"
+
+dependencies:
+  - name: base-mq
+    version: 0.0.3
+    repository: file://../base-mq
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/templates/_helpers.tpl b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/templates/_helpers.tpl
new file mode 100644
index 00000000..49254424
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "neon-script-parser.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "neon-script-parser.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "neon-script-parser.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "neon-script-parser.labels" -}}
+helm.sh/chart: {{ include "neon-script-parser.chart" . }}
+{{ include "neon-script-parser.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "neon-script-parser.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "neon-script-parser.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "neon-script-parser.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "neon-script-parser.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/templates/deployment.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/templates/deployment.yaml
new file mode 100644
index 00000000..41547ae7
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/templates/deployment.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.deployment" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/templates/service.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/templates/service.yaml
new file mode 100644
index 00000000..80730915
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/templates/service.yaml
@@ -0,0 +1,1 @@
+{{- include "base-mq.service" .}}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/values.yaml b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/values.yaml
new file mode 100644
index 00000000..864462bf
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/charts/neon-script-parser/values.yaml
@@ -0,0 +1,7 @@
+replicaCount: 1
+configSecret: diana-config
+image:
+  pullSecret: github-auth
+  repository: ghcr.io/neongeckocom/neon-script-parser
+  pullPolicy: Always
+  tag: dev
diff --git a/neon_diana_utils/helm_charts/mq-services/values.yaml b/neon_diana_utils/helm_charts/mq-services/values.yaml
new file mode 100644
index 00000000..ab2d1afe
--- /dev/null
+++ b/neon_diana_utils/helm_charts/mq-services/values.yaml
@@ -0,0 +1,17 @@
+imageTag: &tag dev
+
+neon-api-proxy:
+  image:
+    tag: *tag
+neon-brands-service:
+  image:
+    tag: *tag
+neon-email-proxy:
+  image:
+    tag: *tag
+neon-metrics-service:
+  image:
+    tag: *tag
+neon-script-parser:
+  image:
+    tag: *tag
diff --git a/neon_diana_utils/helm_charts/neon-rabbitmq/.helmignore b/neon_diana_utils/helm_charts/neon-rabbitmq/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/neon_diana_utils/helm_charts/neon-rabbitmq/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/neon_diana_utils/helm_charts/neon-rabbitmq/Chart.yaml b/neon_diana_utils/helm_charts/neon-rabbitmq/Chart.yaml
new file mode 100644
index 00000000..0403b1a6
--- /dev/null
+++ b/neon_diana_utils/helm_charts/neon-rabbitmq/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v2
+name: neon-rabbitmq
+description: A Helm chart for deploying RabbitMQ for the Diana Backend
+
+type: application
+version: 0.1.0
+appVersion: "1.0.0"
+
+dependencies:
+  - name: rabbitmq
+    version: 11.13.0
+    repository: https://charts.bitnami.com/bitnami
diff --git a/neon_diana_utils/helm_charts/neon-rabbitmq/templates/_helpers.tpl b/neon_diana_utils/helm_charts/neon-rabbitmq/templates/_helpers.tpl
new file mode 100644
index 00000000..0e218d7f
--- /dev/null
+++ b/neon_diana_utils/helm_charts/neon-rabbitmq/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "rabbitmq.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "rabbitmq.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "rabbitmq.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "rabbitmq.labels" -}}
+helm.sh/chart: {{ include "rabbitmq.chart" . }}
+{{ include "rabbitmq.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "rabbitmq.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "rabbitmq.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "rabbitmq.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "rabbitmq.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/neon_diana_utils/helm_charts/neon-rabbitmq/templates/ingress.yaml b/neon_diana_utils/helm_charts/neon-rabbitmq/templates/ingress.yaml
new file mode 100644
index 00000000..0b3faf33
--- /dev/null
+++ b/neon_diana_utils/helm_charts/neon-rabbitmq/templates/ingress.yaml
@@ -0,0 +1,29 @@
+{{- if .Values.ingress.enabled -}}
+{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1
+{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+  name: ingress-diana-rabbitmq
+spec:
+  ingressClassName: nginx
+  tls:
+    - secretName: {{ .Values.ingress.tlsSecretName }}
+      hosts:
+        - {{ .Values.ingress.hostname }}
+  rules:
+    - host: {{ .Values.ingress.hostname }}
+      http:
+        paths:
+          - path: /
+            pathType: Prefix
+            backend:
+              service:
+                name: neon-rabbitmq
+                port:
+                  number: 15672
+{{- end }}
\ No newline at end of file
diff --git a/neon_diana_utils/helm_charts/neon-rabbitmq/values.yaml b/neon_diana_utils/helm_charts/neon-rabbitmq/values.yaml
new file mode 100644
index 00000000..6d473e6d
--- /dev/null
+++ b/neon_diana_utils/helm_charts/neon-rabbitmq/values.yaml
@@ -0,0 +1,24 @@
+# Default values for rabbitmq.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+replicaCount: 1
+
+rabbitmq:
+  nameOverride: neon-rabbitmq
+  fullnameOverride: neon-rabbitmq
+  commonLabels:
+    neon.project.name: diana
+    neon.diana.service: neon-rabbitmq
+    neon.service.class: communication
+  auth:
+    username: neon
+    password: neon
+
+  image:
+    debug: true
+
+ingress:
+  enabled: True
+  hostname: mqadmin.diana.k8s
+  ingressClassName: nginx
diff --git a/neon_diana_utils/kubernetes_utils.py b/neon_diana_utils/kubernetes_utils.py
new file mode 100644
index 00000000..9ac3572b
--- /dev/null
+++ b/neon_diana_utils/kubernetes_utils.py
@@ -0,0 +1,70 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+#    this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from this
+#    software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE,  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import yaml
+
+from typing import Optional
+from os import getenv
+from os.path import join, expanduser
+
+from neon_diana_utils.configuration import validate_output_path
+
+
+def create_github_secret(username: str, token: str,
+                         output_file: Optional[str] = None) -> str:
+    """
+    Generate a Kubernetes Secret to authenticate to GitHub for image pulls
+    :param username: GitHub username
+    :param token: GitHub token with read_packages permission
+    :param output_file: output file to write
+    :returns: path to written Kubernetes config file
+    """
+    import json
+    from base64 import b64encode
+    output_file = output_file or join(getenv("NEON_CONFIG_PATH",
+                                             "~/.config/neon"),
+                                      f"k8s_secret_github.yml")
+    output_file = expanduser(output_file)
+    if not validate_output_path(output_file):
+        raise FileExistsError(output_file)
+    encoded_auth = b64encode(f"{username}:{token}".encode())
+    auth_dict = {"auths": {"ghcr.io": {"auth": encoded_auth.decode()}}}
+    auth_str = json.dumps(auth_dict)
+    encoded_config = b64encode(auth_str.encode())
+    secret_spec = {
+        "kind": "Secret",
+        "type": "kubernetes.io/dockerconfigjson",
+        "apiVersion": "v1",
+        "metadata": {
+            "name": "github-auth"
+        },
+        "data": {
+            ".dockerconfigjson": encoded_config.decode()
+        }
+    }
+    with open(output_file, 'w+') as f:
+        yaml.dump(secret_spec, f)
+    return output_file
diff --git a/neon_diana_utils/rabbitmq_api.py b/neon_diana_utils/rabbitmq_api.py
index 70059053..03cbf6c6 100644
--- a/neon_diana_utils/rabbitmq_api.py
+++ b/neon_diana_utils/rabbitmq_api.py
@@ -32,11 +32,12 @@
 
 
 class RabbitMQAPI:
-    def __init__(self, url: str):
+    def __init__(self, url: str, verify_ssl: bool = False):
         """
         Creates an object used to interface with a RabbitMQ server
         :param url: Management URL (usually IP:port)
         """
+        self._verify_ssl = verify_ssl
         self.console_url = url
         self._username = None
         self._password = None
@@ -64,7 +65,9 @@ def add_vhost(self, vhost: str) -> bool:
         :param vhost: vhost to add
         :return: True if request was successful
         """
-        status = requests.put(f"{self.console_url}/api/vhosts/{quote_plus(vhost)}", auth=self.auth)
+        status = requests.put(
+            f"{self.console_url}/api/vhosts/{quote_plus(vhost)}",
+            auth=self.auth, verify=self._verify_ssl)
         return status.ok
 
     def add_user(self, user: str, password: str, tags: str = "") -> bool:
@@ -77,7 +80,9 @@ def add_user(self, user: str, password: str, tags: str = "") -> bool:
         """
         tags = tags or ""
         body = {"password": password, "tags": tags}
-        status = requests.put(f"{self.console_url}/api/users/{quote_plus(user)}", data=json.dumps(body), auth=self.auth)
+        status = requests.put(
+            f"{self.console_url}/api/users/{quote_plus(user)}",
+            data=json.dumps(body), auth=self.auth, verify=self._verify_ssl)
         return status.ok
 
     def delete_user(self, user: str) -> bool:
@@ -85,13 +90,18 @@ def delete_user(self, user: str) -> bool:
         Delete a user from the server
         :param user: username to remove
         """
-        status = requests.delete(f"{self.console_url}/api/users/{quote_plus(user)}", auth=self.auth)
+        status = requests.delete(
+            f"{self.console_url}/api/users/{quote_plus(user)}",
+            auth=self.auth, verify=self._verify_ssl)
         return status.ok
 
-    def configure_vhost_user_permissions(self, vhost: str, user: str, configure: str = ".*", write: str = ".*",
+    def configure_vhost_user_permissions(self, vhost: str, user: str,
+                                         configure: str = ".*",
+                                         write: str = ".*",
                                          read: str = ".*") -> bool:
         """
-        Configure user's access to vhost. See RabbitMQ docs: https://www.rabbitmq.com/access-control.html#authorisation
+        Configure user's access to vhost. See RabbitMQ docs:
+        https://www.rabbitmq.com/access-control.html#authorisation
         :param vhost: vhost to set/modify permissions for
         :param user: user to set/modify permissions of
         :param configure: regex configure permissions
@@ -99,24 +109,29 @@ def configure_vhost_user_permissions(self, vhost: str, user: str, configure: str
         :param read: regex read permissions
         :return: True if request was successful
         """
-        url = f"{self.console_url}/api/permissions/{quote_plus(vhost)}/{quote_plus(user)}"
+        url = f"{self.console_url}/api/permissions/{quote_plus(vhost)}/" \
+              f"{quote_plus(user)}"
         body = {"configure": configure,
                 "write": write,
                 "read": read}
-        status = requests.put(url, data=json.dumps(body), auth=self.auth)
+        status = requests.put(url, data=json.dumps(body), auth=self.auth,
+                              verify=self._verify_ssl)
         return status.ok
 
     def get_definitions(self):
         """
-        Get the server definitions for RabbitMQ; these are used to persist configuration between container restarts
+        Get the server definitions for RabbitMQ; these are used to persist
+        configuration between container restarts
         """
-        resp = requests.get(f"{self.console_url}/api/definitions", auth=self.auth)
+        resp = requests.get(f"{self.console_url}/api/definitions",
+                            auth=self.auth, verify=self._verify_ssl)
         data = json.loads(resp.content)
         return data
 
     def create_default_users(self, users: list) -> dict:
         """
-        Creates the passed list of users with random passwords and returns a dict of users to passwords
+        Creates the passed list of users with random passwords and returns a
+        dict of users to passwords
         :param users: list of usernames to create
         :return: Dict of created usernames and associated passwords
         """
@@ -130,7 +145,8 @@ def create_default_users(self, users: list) -> dict:
 
     def configure_admin_account(self, username: str, password: str) -> bool:
         """
-        Configures an administrator with the passed credentials and removes the default account
+        Configures an administrator with the passed credentials and removes
+        the default account
         :param username: New administrator's username
         :param password: New administrator's password
         :return: True if action was successful
diff --git a/neon_diana_utils/templates/docker-compose.yml b/neon_diana_utils/templates/docker-compose.yml
deleted file mode 100644
index 1ca57bd3..00000000
--- a/neon_diana_utils/templates/docker-compose.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-version: "3.1"
-networks:
-  mq_backend:
\ No newline at end of file
diff --git a/neon_diana_utils/templates/mq_user_mapping.yml b/neon_diana_utils/templates/mq_user_mapping.yml
new file mode 100644
index 00000000..0b0cbbd4
--- /dev/null
+++ b/neon_diana_utils/templates/mq_user_mapping.yml
@@ -0,0 +1,17 @@
+# Mapping of MQ users to MQ service names for service authentication
+neon_api:
+  - neon_api_connector
+neon_coupons:
+  - neon_coupon_connector
+neon_email:
+  - neon_email_proxy
+neon_metrics:
+  - neon_metrics_connector
+neon_script_parser:
+  - neon_script_parser_service
+neon_libretranslate:
+  - mq-libre-translate
+neon_llm_chatgpt:
+  - neon_llm_chatgpt
+neon_llm_fastchat:
+  - neon_llm_fastchat
\ No newline at end of file
diff --git a/neon_diana_utils/templates/rmq_backend_config.yml b/neon_diana_utils/templates/rmq_backend_config.yml
new file mode 100644
index 00000000..799c0153
--- /dev/null
+++ b/neon_diana_utils/templates/rmq_backend_config.yml
@@ -0,0 +1,131 @@
+users:
+  - name: "neon_api_utils"
+    password: "Klatchat2021"
+    tags:
+      - backend
+      - user
+  - name: "neon_metrics"
+    password:
+    tags:
+      - backend
+      - service
+  - name: "neon_coupons"
+    password:
+    tags:
+      - backend
+      - service
+  - name: "neon_email"
+    password:
+    tags:
+      - backend
+      - service
+  - name: "neon_script_parser"
+    password:
+    tags:
+      - backend
+      - service
+  - name: "neon_api"
+    password:
+    tags:
+      - backend
+      - service
+  - name: "neon_libretranslate"
+    password:
+    tags:
+      - backend
+      - service
+  - name: "neon_llm_chatgpt"
+    password:
+    tags:
+      - backend
+      - llm
+  - name: "neon_llm_fastchat"
+    password:
+    tags:
+      - backend
+      - llm
+vhosts:
+  - name: "/neon_emails"
+  - name: "/neon_api"
+  - name: "/neon_script_parser"
+  - name: "/neon_metrics"
+  - name: "/neon_coupons"
+  - name: "/neon_testing"
+  - name: "/translation"
+  - name: "/llm"
+permissions:
+  - user: "neon_llm_chatgpt"
+    vhost: "/llm"
+    configure: ".*"
+    write: ".*"
+    read: "chat_gpt_input"
+  - user: "neon_llm_fastchat"
+    vhost: "/llm"
+    configure: ".*"
+    write: ".*"
+    read: "fastchat_input"
+  - user: "neon_api_utils"
+    vhost: "/llm"
+    configure: ".*"
+    write: ".*"
+    read: ".*(?!_input).*"
+  - user: "neon_libretranslate"
+    vhost: "/translation"
+    configure: ".*"
+    write: ".*"
+    read: ".*"
+  - user: "neon_api_utils"
+    vhost: "/neon_coupons"
+    configure: ".*"
+    write: ".*"
+    read: ".*"
+  - user: "neon_email"
+    vhost: "/neon_emails"
+    configure: ".*"
+    write: ".*"
+    read: ".*"
+  - user: "neon_api_utils"
+    vhost: "/neon_emails"
+    configure: ".*"
+    write: ".*"
+    read: "^(?!neon_emails_input).*"
+  - user: "neon_api"
+    vhost: "/neon_api"
+    configure: ".*"
+    write: ".*"
+    read: ".*"
+  - user: "neon_api_utils"
+    vhost: "/neon_metrics"
+    configure: ".*"
+    write: ".*"
+    read: ""
+  - user: "neon_api_utils"
+    vhost: "/neon_api"
+    configure: "./*"
+    write: "./*"
+    read: "./*"
+  - user: "neon_api_utils"
+    vhost: "/neon_script_parser"
+    configure: ".*"
+    write: ".*"
+    read: ".*"
+  - user: "neon_script_parser"
+    vhost: "/neon_script_parser"
+    configure: ".*"
+    write: ".*"
+    read: ".*"
+  - user: "neon_metrics"
+    vhost: "/neon_metrics"
+    configure: ".*"
+    write: ".*"
+    read: ".*"
+  - user: "neon_api_utils"
+    vhost: "/neon_testing"
+    configure: "./*"
+    write: "./*"
+    read: "./*"
+  - user: "neon_coupons"
+    vhost: "/neon_coupons"
+    configure: ".*"
+    write: ".*"
+    read: ".*"
\ No newline at end of file
diff --git a/neon_diana_utils/templates/service_mappings.yml b/neon_diana_utils/templates/service_mappings.yml
deleted file mode 100644
index 465aa687..00000000
--- a/neon_diana_utils/templates/service_mappings.yml
+++ /dev/null
@@ -1,134 +0,0 @@
-neon_rabbitmq:
-  docker_compose:
-    container_name: neon-rabbitmq
-    image: rabbitmq:3-management
-    restart: unless-stopped
-    networks:
-      - mq_backend
-    ports:
-      - 15672:15672
-      - 5672:5672
-    volumes:
-      - ${NEON_CONFIG_PATH}/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
-      - ${NEON_CONFIG_PATH}:/config
-neon_api_proxy:
-  mq_service_name: neon_api_connector
-  mq_username: neon_api
-  mq_user_permissions:
-    neon_api:
-      /neon_api:
-        read: .*
-        write: .*
-        configure: .*
-    neon_api_utils:
-      /neon_api:
-        read: ./*
-        write: ./*
-        configure: ./*
-  mq_vhosts:
-    - /neon_api
-  docker_compose:
-    container_name: neon-api-proxy
-    image: ghcr.io/neongeckocom/neon_api_proxy:dev
-    restart: unless-stopped
-    networks:
-      - mq_backend
-    volumes:
-      - ${NEON_CONFIG_PATH}:/config
-neon_brands_service:
-  mq_service_name: neon_coupon_connector
-  mq_username: neon_coupons
-  mq_user_permissions:
-    neon_coupons:
-      /neon_coupons:
-        read: .*
-        write: .*
-        configure: .*
-    neon_api_utils:
-      /neon_coupons:
-        read: .*
-        write: .*
-        configure: .*
-  mq_vhosts:
-    - /neon_coupons
-  docker_compose:
-    container_name: neon-brands-service
-    image: ghcr.io/neongeckocom/neon_brands_service:dev
-    restart: unless-stopped
-    networks:
-      - mq_backend
-    volumes:
-      - ${NEON_CONFIG_PATH}:/config
-neon_email_proxy:
-  mq_username: neon_email
-  mq_user_permissions:
-    neon_email:
-      /neon_emails:
-        read: .*
-        write: .*
-        configure: .*
-    neon_api_utils:
-      /neon_emails:
-        read: "^(?!neon_emails_input).*"
-        write: .*
-        configure: .*
-  mq_vhosts:
-    - /neon_emails
-  docker_compose:
-    container_name: neon-email-proxy
-    image: ghcr.io/neongeckocom/neon_email_proxy:dev
-    restart: unless-stopped
-    networks:
-      - mq_backend
-    volumes:
-      - ${NEON_CONFIG_PATH}:/config
-neon_script_parser:
-  mq_service_name: neon_script_parser_service
-  mq_username: neon_script_parser
-  mq_user_permissions:
-    neon_script_parser:
-      /neon_script_parser:
-        read: .*
-        write: .*
-        configure: .*
-    neon_api_utils:
-      /neon_script_parser:
-        read: .*
-        write: .*
-        configure: .*
-  mq_vhosts:
-    - /neon_script_parser
-
-  docker_compose:
-    container_name: neon-script-parser
-    image: ghcr.io/neongeckocom/neon-script-parser:dev
-    restart: unless-stopped
-    networks:
-      - mq_backend
-    volumes:
-      - ${NEON_CONFIG_PATH}:/config
-neon_metrics_service:
-  mq_service_name: neon_metrics_connector
-  mq_username: neon_metrics
-  mq_user_permissions:
-    neon_metrics:
-      /neon_metrics:
-        read: .*
-        write: .*
-        configure: .*
-    neon_api_utils:
-      /neon_metrics:
-        read: ""
-        write: .*
-        configure: .*
-  mq_vhosts:
-    - /neon_metrics
-  docker_compose:
-    container_name: neon-metrics-service
-    image: ghcr.io/neongeckocom/neon_metrics_service:dev
-    restart: unless-stopped
-    networks:
-      - mq_backend
-    volumes:
-      - ${NEON_CONFIG_PATH}:/config
-      - ${NEON_METRIC_PATH}:/metrics
diff --git a/neon_diana_utils/utils.py b/neon_diana_utils/utils.py
deleted file mode 100644
index 213129c6..00000000
--- a/neon_diana_utils/utils.py
+++ /dev/null
@@ -1,240 +0,0 @@
-# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
-# All trademark and other rights reserved by their respective owners
-# Copyright 2008-2021 Neongecko.com Inc.
-# BSD-3
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright notice,
-#    this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from this
-#    software without specific prior written permission.
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-# CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
-# OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE,  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import itertools
-import json
-import os
-import docker
-
-from os import getenv
-from os.path import join, dirname, expanduser, basename, exists
-from typing import Optional
-
-from docker.errors import APIError
-from docker.models.containers import Container
-from ruamel.yaml import YAML
-from neon_utils import LOG
-from neon_utils.configuration_utils import dict_merge
-
-from neon_diana_utils.rabbitmq_api import RabbitMQAPI
-
-
-def _run_clean_rabbit_mq(bind_existing: bool = False) -> Container:
-    """
-    Start a clean RabbitMQ Docker instance to generate configuration files
-    :param bind_existing: If true, allows for binding to a running container
-        WARNING: setting `bind_existing` can result in overwriting valid MQ configuration
-    """
-    docker_client = docker.from_env()
-    try:
-        container = docker_client.containers.run("rabbitmq:3-management",
-                                                 ports={"15672": "15672",
-                                                        "5672": "5672"},
-                                                 detach=True)
-    except APIError as e:
-        if e.status_code == 500:
-            LOG.warning("Is an instance of RabbitMQ already running?")
-            if bind_existing:
-                for c in docker_client.containers.list():
-                    if "rabbitmq:3-management" in c.image.tags:
-                        LOG.info(f"Found a running RabbitMQ instance to configure")
-                        return c
-        else:
-            LOG.error(e)
-        raise e
-    return container
-
-
-def cleanup_docker_container(container_to_remove: Container):
-    """
-    Stop and remove the specified Docker Container
-    :param container_to_remove: Docker.Container object to stop and remove
-    """
-    container_to_remove.stop()
-    container_to_remove.remove()
-
-
-def create_diana_configurations(admin_user: str, admin_pass: str,
-                                services: set, config_path: str = None,
-                                allow_bind_existing: bool = False):
-    """
-    Create configuration files for Neon Diana.
-    :param admin_user: username to configure for RabbitMQ configuration
-    :param admin_pass: password associated with admin_user
-    :param services: list of services to configure on this backend
-    :param config_path: path to write configuration files (default=NEON_CONFIG_PATH)
-    :param allow_bind_existing: bool to allow overwriting configuration for a running RabbitMQ instance
-    """
-    container = _run_clean_rabbit_mq(allow_bind_existing)
-    container_logs = container.logs(stream=True)
-    for log in container_logs:
-        if b"Server startup complete" in log:
-            break
-    configure_diana_backend("http://0.0.0.0:15672", admin_user, admin_pass, services, config_path)
-
-    cleanup_docker_container(container)
-
-
-def configure_diana_backend(url: str, admin_user: str, admin_pass: str,
-                            services: set, config_path: str = None):
-    """
-    Configure a new Diana backend
-    :param url: URL of admin portal (i.e. http://0.0.0.0:15672)
-    :param admin_user: username to configure for RabbitMQ configuration
-    :param admin_pass: password associated with admin_user
-    :param services: list of services to configure on this backend
-    :param config_path: path to write configuration files (default=NEON_CONFIG_PATH)
-    """
-    api = RabbitMQAPI(url)
-
-    # Configure Administrator
-    api.login("guest", "guest")
-    api.configure_admin_account(admin_user, admin_pass)
-
-    # Read configuration from templates
-    template_file = join(dirname(__file__), "templates",
-                         "service_mappings.yml")
-    with open(template_file) as f:
-        template_data = YAML().load(f)
-    services_to_configure = {name: dict(template_data[name])
-                             for name in services if name in template_data}
-
-    # Warn for unknown requested services
-    if set(services_to_configure.keys()) != set(services):
-        unhandled_services = [s for s in services if s not in services_to_configure.keys()]
-        LOG.warning(f"Some requested services not handled: {unhandled_services}")
-
-    # Parse Configured Service Mapping
-    vhosts_to_configure = set(itertools.chain.from_iterable([service.get("mq_vhosts", [])
-                                                             for service in services_to_configure.values()]))
-    users_to_configure = dict()
-    neon_mq_user_auth = dict()
-    docker_compose_configuration = dict()
-    for name, service in services_to_configure.items():
-        dict_merge(users_to_configure, service.get("mq_user_permissions", dict()))
-        docker_compose_configuration[name] = service["docker_compose"]
-        if service.get("mq_username"):
-            # TODO: Update MQ services such that their service names match the docker container names DM
-            neon_mq_user_auth[service.get("mq_service_name", name)] = {"user": service["mq_username"]}
-
-    LOG.debug(f"vhosts={vhosts_to_configure}")
-    LOG.debug(f"users={users_to_configure}")
-
-    # Configure vhosts
-    for vhost in vhosts_to_configure:
-        api.add_vhost(vhost)
-
-    # Configure users
-    credentials = api.create_default_users(list(users_to_configure.keys()))
-    api.add_user("neon_api_utils", "Klatchat2021")
-
-    # Configure user permissions
-    for user, vhost_config in users_to_configure.items():
-        for vhost, permissions in vhost_config.items():
-            if not api.configure_vhost_user_permissions(vhost, user, **permissions):
-                LOG.error(f"Error setting Permission! {user} {vhost}")
-                raise
-
-    # Export and save rabbitMQ Config
-    rabbit_mq_config_file = join(expanduser(config_path), "rabbit_mq_config.json") if config_path else None
-    write_rabbit_config(api, rabbit_mq_config_file)
-
-    # Write out MQ config file
-    for service in neon_mq_user_auth.values():
-        service["password"] = credentials[service["user"]]
-    neon_mq_config_file = join(expanduser(config_path), "mq_config.json") if config_path else None
-    write_neon_mq_config(neon_mq_user_auth, neon_mq_config_file)
-
-    # Generate docker-compose file
-    docker_compose_file = join(expanduser(config_path), "docker-compose.yml") if config_path else None
-    write_docker_compose(docker_compose_configuration, docker_compose_file)
-
-
-def write_neon_mq_config(credentials: dict, config_file: Optional[str] = None):
-    """
-    Takes the passed credentials and exports an MQ config file based on the
-    """
-    config_file = config_file if config_file else \
-        join(getenv("NEON_CONFIG_PATH", "~/.config/neon"), "mq_config.json")
-    config_file = expanduser(config_file)
-    if not exists(dirname(config_file)):
-        os.makedirs(dirname(config_file))
-
-    configuration = {"server": "neon_rabbitmq",
-                     "users": credentials}
-    LOG.info(f"Writing Neon MQ configuration to {config_file}")
-    with open(config_file, 'w+') as new_config:
-        json.dump(configuration, new_config, indent=2)
-
-
-def write_rabbit_config(api: RabbitMQAPI, config_file: Optional[str] = None):
-    """
-    Writes out RabbitMQ config files for persistence on next run
-    """
-    config_file = config_file if config_file else \
-        join(getenv("NEON_CONFIG_PATH", "~/.config/neon"),
-             "rabbit_mq_config.json")
-    config_file = expanduser(config_file)
-    config_path = dirname(config_file)
-    if not exists(config_path):
-        os.makedirs(config_path)
-
-    config = api.get_definitions()
-    LOG.info(f"Exporting Rabbit MQ configuration to {config_file}")
-    with open(config_file, "w+") as exported:
-        json.dump(config, exported, indent=2)
-
-    config_basename = basename(config_file)
-    with open(join(config_path, "rabbitmq.conf"), 'w+') as rabbit:
-        rabbit.write(f"load_definitions = /config/{config_basename}")
-
-
-def write_docker_compose(services_config: dict, compose_file: Optional[str] = None):
-    """
-    Generates and writes a docker-compose.yml according to the specified services
-    :param services_config: dict services, usually read from service_mappings.yml
-    :param compose_file: path of docker-compose.yml file to write
-    """
-    compose_file = compose_file if compose_file else \
-        join(getenv("NEON_CONFIG_PATH", "~/.config/neon"), "docker-compose.yml")
-    compose_file = expanduser(compose_file)
-
-    with open(join(dirname(__file__), "templates", "docker-compose.yml")) as f:
-        compose_boilerplate = YAML().load(f)
-    compose_contents = {**compose_boilerplate, **{"services": services_config}}
-
-    neon_config_path = dirname(compose_file)
-    neon_metric_path = expanduser(getenv("NEON_METRIC_PATH", f"{neon_config_path}/metrics"))
-    with open(compose_file, "w+") as f:
-        YAML().dump(compose_contents, f)
-        f.seek(0)
-        string_contents = f.read()
-        string_contents = string_contents.replace("${NEON_CONFIG_PATH}",
-                                                  neon_config_path)\
-            .replace("${NEON_METRIC_PATH}", neon_metric_path)
-        f.seek(0)
-        f.truncate(0)
-        f.write(string_contents)
diff --git a/neon_diana_utils/version.py b/neon_diana_utils/version.py
index 64d6b121..0fd4bc81 100644
--- a/neon_diana_utils/version.py
+++ b/neon_diana_utils/version.py
@@ -26,4 +26,4 @@
 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 # SOFTWARE,  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-__version__ = "0.0.5"
+__version__ = "1.0.0"
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index c96d5a67..f60131cd 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -1,6 +1,6 @@
 requests~=2.26
-ruamel.yaml~=0.16
-neon_utils~=0.12
+pyyaml~=5.4
 docker~=5.0
 click~=8.0
-click-default-group~=1.2
\ No newline at end of file
+click-default-group~=1.2
+neon_mq_connector~=0.3,>=0.6.1a9
\ No newline at end of file
diff --git a/scripts/sync_chart_app_version.py b/scripts/sync_chart_app_version.py
new file mode 100644
index 00000000..29a82c11
--- /dev/null
+++ b/scripts/sync_chart_app_version.py
@@ -0,0 +1,55 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+#    this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from this
+#    software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE,  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from sys import argv
+from os import walk
+from os.path import join, dirname
+
+
+def sync_app_version(version: str):
+    """
+    Synchronize current Diana package version with bundled Helm charts
+    """
+    chart_dir = join(dirname(dirname(__file__)),
+                     "neon_diana_utils", "helm_charts")
+    for root, _, files in walk(chart_dir):
+        for file in files:
+            if file == "Chart.yaml":
+                file_path = join(root, file)
+                print(f"Updating {file_path}")
+                with open(file_path, 'r') as f:
+                    lines = f.readlines()
+                for idx, line in enumerate(lines):
+                    if line.startswith("appVersion: "):
+                        lines[idx] = f"appVersion: \"{version}\"\n"
+                with open(file_path, 'w') as f:
+                    f.writelines(lines)
+                break
+
+
+if __name__ == "__main__":
+    ver = argv[1]
+    sync_app_version(ver)
diff --git a/setup.py b/setup.py
index d5ca7a27..20b98fbb 100644
--- a/setup.py
+++ b/setup.py
@@ -29,9 +29,11 @@
 from setuptools import setup, find_packages
 from os import getenv, path
 
+BASE_PATH = path.abspath(path.dirname(__file__))
+
 
 def get_requirements(requirements_filename: str):
-    requirements_file = path.join(path.abspath(path.dirname(__file__)), "requirements", requirements_filename)
+    requirements_file = path.join(BASE_PATH, "requirements", requirements_filename)
     with open(requirements_file, 'r', encoding='utf-8') as r:
         requirements = r.readlines()
     requirements = [r.strip() for r in requirements if r.strip() and not r.strip().startswith("#")]
@@ -41,17 +43,17 @@ def get_requirements(requirements_filename: str):
         if "@" in r:
             parts = [p.lower() if p.strip().startswith("git+http") else p for p in r.split('@')]
             r = "@".join(parts)
-            if getenv("GITHUB_TOKEN"):
-                if "github.com" in r:
-                    r = r.replace("github.com", f"{getenv('GITHUB_TOKEN')}@github.com")
-            requirements[i] = r
+        if getenv("GITHUB_TOKEN"):
+            if "github.com" in r:
+                requirements[i] = r.replace("github.com", f"{getenv('GITHUB_TOKEN')}@github.com")
     return requirements
 
 
-with open("README.md", "r") as f:
+with open(path.join(BASE_PATH, "README.md"), "r") as f:
     long_description = f.read()
 
-with open("./neon_diana_utils/version.py", "r", encoding="utf-8") as v:
+with open(path.join(BASE_PATH, "neon_diana_utils",
+                    "version.py"), "r", encoding="utf-8") as v:
     for line in v.readlines():
         if line.startswith("__version__"):
             if '"' in line:
@@ -70,7 +72,7 @@ def get_requirements(requirements_filename: str):
     author_email='developers@neon.ai',
     license='BSD-3-Clause',
     packages=find_packages(),
-    package_data={'neon_diana_utils': ['templates/*']
+    package_data={'neon_diana_utils': ['templates/*', 'helm_charts/**']
                   },
     include_package_data=True,
     install_requires=get_requirements("requirements.txt"),


Test patch:
diff --git a/.github/workflows/license_tests.yml b/.github/workflows/license_tests.yml
new file mode 100644
index 00000000..dcf543d4
--- /dev/null
+++ b/.github/workflows/license_tests.yml
@@ -0,0 +1,10 @@
+name: Run License Tests
+on:
+  push:
+  workflow_dispatch:
+  pull_request:
+    branches:
+      - master
+jobs:
+  license_tests:
+    uses: neongeckocom/.github/.github/workflows/license_tests.yml@master
\ No newline at end of file
diff --git a/.github/workflows/publish_test_build.yml b/.github/workflows/publish_test_build.yml
index e1641e56..d871d939 100644
--- a/.github/workflows/publish_test_build.yml
+++ b/.github/workflows/publish_test_build.yml
@@ -7,33 +7,13 @@ on:
       - dev
     paths-ignore:
       - 'neon_diana_utils/version.py'
+      - '**/Chart.yaml'
 
 jobs:
-  build_and_publish:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-        with:
-          ref: ${{ github.head_ref }}
-      - name: Setup Python
-        uses: actions/setup-python@v1
-        with:
-          python-version: 3.8
-      - name: Install Build Tools
-        run: |
-          python -m pip install build wheel
-      - name: Increment Version
-        run: |
-          VER=$(python setup.py --version)
-          python version_bump.py
-      - name: Push Version Change
-        uses: stefanzweifel/git-auto-commit-action@v4
-        with:
-          commit_message: Increment Version
-      - name: Build Distribution Packages
-        run: |
-          python setup.py bdist_wheel
-      - name: Publish to Test PyPI
-        uses: pypa/gh-action-pypi-publish@master
-        with:
-          password: ${{secrets.PYPI_TOKEN}}
+  publish_alpha_release:
+    uses: neongeckocom/.github/.github/workflows/publish_alpha_release.yml@master
+    secrets: inherit
+    with:
+      version_file: "neon_diana_utils/version.py"
+      publish_prerelease: true
+      on_version_change: "scripts/sync_chart_app_version.py"
\ No newline at end of file
diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml
index 6a84e5e2..987913c2 100644
--- a/.github/workflows/unit_tests.yml
+++ b/.github/workflows/unit_tests.yml
@@ -7,7 +7,7 @@ jobs:
   unit_tests:
     strategy:
       matrix:
-        python-version: [ 3.6, 3.7, 3.8, 3.9 ]
+        python-version: [ 3.7, 3.8, 3.9, "3.10" ]
     timeout-minutes: 15
     runs-on: ubuntu-latest
     steps:
@@ -19,21 +19,13 @@ jobs:
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
-          pip install -r requirements/requirements.txt
+          pip install -e .
           pip install -r requirements/test_requirements.txt
-      - name: Run Utils Tests
+      - name: Run Tests
         run: |
-          pytest tests/test_diana_utils.py --doctest-modules --junitxml=tests/utils-test-results.xml
-      - name: Upload Utils test results
+          pytest tests --doctest-modules --junitxml=tests/diana-test-results.xml
+      - name: Upload test results
         uses: actions/upload-artifact@v2
         with:
           name: utils-test-results
-          path: tests/utils-test-results.xml
-      - name: Run Service Tests
-        run: |
-          pytest tests/test_diana_services.py --doctest-modules --junitxml=tests/services-test-results.xml
-      - name: Upload Services test results
-        uses: actions/upload-artifact@v2
-        with:
-          name: services-test-results
-          path: tests/services-test-results.xml
\ No newline at end of file
+          path: tests/diana-test-results.xml
diff --git a/neon_diana_utils/helm_charts/diana-backend/templates/tests/test-connection.yaml b/neon_diana_utils/helm_charts/diana-backend/templates/tests/test-connection.yaml
new file mode 100644
index 00000000..2c32aeaa
--- /dev/null
+++ b/neon_diana_utils/helm_charts/diana-backend/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+{{/*apiVersion: v1*/}}
+{{/*kind: Pod*/}}
+{{/*metadata:*/}}
+{{/*  name: "{{ include "diana.fullname" . }}-test-connection"*/}}
+{{/*  labels:*/}}
+{{/*    {{- include "diana.labels" . | nindent 4 }}*/}}
+{{/*  annotations:*/}}
+{{/*    "helm.sh/hook": test*/}}
+{{/*spec:*/}}
+{{/*  containers:*/}}
+{{/*    - name: wget*/}}
+{{/*      image: busybox*/}}
+{{/*      command: ['wget']*/}}
+{{/*      args: ['{{ include "diana.fullname" . }}:{{ .Values.service.port }}']*/}}
+{{/*  restartPolicy: Never*/}}
diff --git a/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/tests/test-connection.yaml b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/tests/test-connection.yaml
new file mode 100644
index 00000000..61987f9e
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/libretranslate/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "lang-libretranslate.fullname" . }}-test-connection"
+  labels:
+    {{- include "lang-libretranslate.labels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test
+spec:
+  containers:
+    - name: wget
+      image: busybox
+      command: ['wget']
+      args: ['{{ include "lang-libretranslate.fullname" . }}:{{ .Values.service.port }}']
+  restartPolicy: Never
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/tests/test-connection.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/tests/test-connection.yaml
new file mode 100644
index 00000000..726e8e68
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-coqui/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "tts-coqui.fullname" . }}-test-connection"
+  labels:
+    {{- include "tts-coqui.labels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test
+spec:
+  containers:
+    - name: wget
+      image: busybox
+      command: ['wget']
+      args: ['{{ include "tts-coqui.fullname" . }}:{{ .Values.service.port }}']
+  restartPolicy: Never
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/tests/test-connection.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/tests/test-connection.yaml
new file mode 100644
index 00000000..e01bc10c
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-glados/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "tts-glados.fullname" . }}-test-connection"
+  labels:
+    {{- include "tts-glados.labels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test
+spec:
+  containers:
+    - name: wget
+      image: busybox
+      command: ['wget']
+      args: ['{{ include "tts-glados.fullname" . }}:{{ .Values.service.port }}']
+  restartPolicy: Never
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/tests/test-connection.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/tests/test-connection.yaml
new file mode 100644
index 00000000..da34edf4
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-larynx/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "tts-larynx.fullname" . }}-test-connection"
+  labels:
+    {{- include "tts-larynx.labels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test
+spec:
+  containers:
+    - name: wget
+      image: busybox
+      command: ['wget']
+      args: ['{{ include "tts-larynx.fullname" . }}:{{ .Values.service.port }}']
+  restartPolicy: Never
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/tests/test-connection.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/tests/test-connection.yaml
new file mode 100644
index 00000000..83d3c13b
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-ljspeech/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "tts-ljspeech.fullname" . }}-test-connection"
+  labels:
+    {{- include "tts-ljspeech.labels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test
+spec:
+  containers:
+    - name: wget
+      image: busybox
+      command: ['wget']
+      args: ['{{ include "tts-ljspeech.fullname" . }}:{{ .Values.service.port }}']
+  restartPolicy: Never
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/tests/test-connection.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/tests/test-connection.yaml
new file mode 100644
index 00000000..c29f6497
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-mozilla/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "tts-mozilla.fullname" . }}-test-connection"
+  labels:
+    {{- include "tts-mozilla.labels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test
+spec:
+  containers:
+    - name: wget
+      image: busybox
+      command: ['wget']
+      args: ['{{ include "tts-mozilla.fullname" . }}:{{ .Values.service.port }}']
+  restartPolicy: Never
diff --git a/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/tests/test-connection.yaml b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/tests/test-connection.yaml
new file mode 100644
index 00000000..e2d53eed
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/tts-nancy/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "tts-nancy.fullname" . }}-test-connection"
+  labels:
+    {{- include "tts-nancy.labels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test
+spec:
+  containers:
+    - name: wget
+      image: busybox
+      command: ['wget']
+      args: ['{{ include "tts-nancy.fullname" . }}:{{ .Values.service.port }}']
+  restartPolicy: Never
diff --git a/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/tests/test-connection.yaml b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/tests/test-connection.yaml
new file mode 100644
index 00000000..0ac9a445
--- /dev/null
+++ b/neon_diana_utils/helm_charts/http-services/charts/ww-snowboy/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "ww-snowboy.fullname" . }}-test-connection"
+  labels:
+    {{- include "ww-snowboy.labels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test
+spec:
+  containers:
+    - name: wget
+      image: busybox
+      command: ['wget']
+      args: ['{{ include "ww-snowboy.fullname" . }}:{{ .Values.service.port }}']
+  restartPolicy: Never
diff --git a/tests/config/ngi_local_conf.yml b/tests/config/ngi_local_conf.yml
deleted file mode 100644
index e13b33ec..00000000
--- a/tests/config/ngi_local_conf.yml
+++ /dev/null
@@ -1,239 +0,0 @@
-prefFlags:
-  codeSource: git
-  devMode: false
-  autoStart: false
-  autoUpdate: false
-  notifyRelease: true
-  showDemo: true
-  optDiags: false
-  metrics: false
-  saveAudio: false
-  saveText: true
-  # TODO: These should go somewhere in HAL/Enclosure
-  localGui: true
-  guiEvents: true
-
-interface:
-# True, False
-  display_neon_brain: false
-  wake_word_enabled: true
-  clap_commands_enabled: false
-  blink_commands_enabled: false
-  random_dialog_enabled: false
-  confirm_listening: true
-  mute_on_listen: false
-  # True=Muted, False=lowered vol
-  use_hesitation: false
-
-devVars:
-  devName:
-  devType: generic
-  # generic, server, pi, neonK, neonX, neonPi, neonAlpha, neonU, mycroft_mark_2, ubuntu, linux
-  version:
-  touchDev:
-  micDev:
-  camDev: 0
-  soundDev:
-  defaultVolume: 60
-  defaultMicVolume: 100
-  installUser:
-  # TODO: Depreciate installUser
-
-gestures:
-  clapThreshold: 10.0e10
-
-audioService:
-  backends:
-    local: {type: simple, active: true}
-    vlc: {type: vlc, active: true, duck: true}
-  defaultBackend: vlc
-  debug: true
-
-padatious:
-  intent_cache: ~/.neon/intent_cache
-  train_delay: 4
-  single_thread: false
-
-websocket:
-  host: 0.0.0.0
-  port: 8181
-  route: /core
-  ssl: false
-  allow_self_signed: true
-  ssl_cert:
-  ssl_key:
-
-gui:
-  # TODO: This should be built so lang, enclosure are read from other places
-  # Host information for the gui server
-  lang: en-us
-  enclosure: generic
-  host: 0.0.0.0
-  port: 18181
-  route: /gui
-  ssl: false
-  resource_root: https://0000.us/klatchat/app/files/neon_qml
-
-hotwords:
-  Hey Neon: {module: ovos-ww-plugin-pocketsphinx, phonemes: HH EY . N IY AA N ., threshold: 1e-20,
-    lang: en-us, sample_rate: 16000, listen: true, sound: snd/start_listening.wav,
-    local_model_file: None}
-
-listener:
-  sample_rate: 16000
-  channels: 1
-  wake_word_upload: {disable: true, url: https://training.mycroft.ai/precise/upload}
-  mute_during_output: true
-  duck_while_listening: 0.3
-  dev_index:
-  phoneme_duration: 120
-  multiplier: 1.0
-  energy_ratio: 1.5
-  wake_word: hey neon
-  stand_up_word: wake up
-  recording_timeout: 10.0,
-  recording_timeout_with_silence: 3.0
-
-skills:
-  auto_update: true
-  install_essential: true
-  install_default: true
-  wait_for_internet: true
-  debug: true
-  blacklist: []
-  priority: [about.neon.neongeckocom, date-time.neon.neongeckocom, skill-weather.neongeckocom]
-  essential_skills: []
-  default_skills: https://raw.githubusercontent.com/NeonGeckoCom/neon-skills-submodules/master/DEFAULT-SKILLS
-  skill_manager: osm
-  # recommended osm, optional msm
-  appstore_sync_interval: 6.0
-  # time between server syncs in hours
-  auto_update_interval: 24.0
-  # time between automatic skill updates in hours
-  msm_ver: false
-  repo_url: https://github.com/MycroftAI/mycroft-skills
-  repo_branch: '18.08'
-  data_dir: ~/.neon/msm
-  neon_token:
-
-audio_parsers:
-  blacklist: [gender]
-
-session:
-  ttl: 180
-tts:
-  module: amazon
-  amazon: {region: us-west-2, aws_access_key_id: '', aws_secret_access_key: ''}
-  mozilla: {request_url: http://0.0.0.0:5002/api/tts?}
-  mimic: {voice: ap}
-  mimic2: {lang: en-us, url: https://mimic-api.mycroft.ai/synthesize?text=}
-stt:
-  module: google_cloud_streaming
-
-language:
-  boost: false
-  core_lang: en-us
-  translation_module: libretranslate_plug
-  detection_module: libretranslate_detection_plug
-  libretranslate: {libretranslate_host: https://translate.neon.ai:5000}
-
-logs:
-  blacklist: [enclosure.mouth.viseme, enclosure.mouth.display]
-
-device:
-  mac: 00:00:00:00:00:00
-  ip4: 127.0.0.1
-  ip6: ::1
-  ver: ''
-
-api:
-  url: https://api.mycroft.ai
-  version: v1
-  update: true
-  disabled: true
-  sync_skill_settings: false
-
-# Below are (mostly) depreciated DM
-
-remoteVars:
-  coreGit:
-  coreBranch:
-  skillsGit:
-  skillsBranch:
-  guiGit:
-  guiBranch:
-  url: https://api.mycroft.ai
-  ver: v1
-  remoteHost: 167.172.112.7
-  enableConnection: true
-
-gnome:
-  favApps:
-  appFolders: "['accessories', 'office', 'Neongecko']"
-
-dirVars:
-  coreDir: /opt/neon
-  # coreDir is depreciated for packaged Neon Installations
-  rootDir: ~/.local/share/neon
-  confDir: ~/.config/neon
-  cacheDir: ~/.cache/neon
-  skillsDir: ~/.local/share/neon/skills
-  ngiDir: /opt/neon/NGI
-  # ngiDir is depreciated for packaged Neon Installations
-  guiDir:
-  tempDir: /tmp/neon
-  docsDir: ~/Documents/NeonGecko
-  diagsDir: ~/Documents/NeonGecko/Diagnostics
-  ipcDir: /tmp/neon/ipc
-  # Changes to ipcDir must also be made in NGI/utilities/configHelper
-  musicDir: ~/Music
-  videoDir: ~/Videos/NeonGecko
-  picsDir: ~/Pictures/NeonGecko
-  logsDir: /var/log/mycroft
-  repoDir: ~/.neon/skills-repo
-  padatiousDir: ~/.neon/intent_cache
-
-# File paths should be absolute (or ~/ relative) or relative to /res/ (see resolve_resource_file)
-fileVars:
-  sshKey: ~/.ssh/id_rsa
-  notify: snd/loaded.wav
-
-sounds:
-  startListening: snd/start_listening.wav
-  endListening: snd/end_listening.wav
-  acknowledge: snd/acknowledge.mp3
-
-ttsOpts: {None: '', 'Chinese, Mandarin': zh-ZH, Danish: da-DK, Dutch: nl-NL, 'English, Australian': en-AU,
-  'English, British': en-GB, 'English, Indian': en-IN, 'English, US': en-US, 'English, Welsh': en-GB-WLS,
-  French: fr-FR, 'French, Canadian': fr-CA, Hindi: hi-IN, German: de-DE, Icelandic: is-IS,
-  Italian: it-IT, Japanese: ja-JP, Korean: ko-KR, Norwegian: nb-NO, Polish: pl-PL,
-  'Portuguese, Brazilian': pt-BR, 'Portuguese, European': pt-PT, Romanian: ro-RO,
-  Russian: ru-RU, 'Spanish, European': es-ES, 'Spanish, Mexican': es-MX, 'Spanish, US': es-US,
-  Swedish: sv-SE, Turkish: tr-TR, Welsh: cy-GB}
-
-sttOpts: {Deutsch (Deutschland): de-DE, English (United States): en-US, Español (España): es-ES,
-  Español (México): es-MX, Français (Canada): fr-CA, Français (France): fr-FR, Italiano (Italia): it-IT,
-  Português (Portugal): pt-PT}
-
-sttSpokenOpts: {Afrikaans: af-ZA, Amharic: am-ET, Armenian: hy-AM, Azerbaijani: az-AZ,
-  Indonesian: id-ID, Malay: ms-MY, Bengali: bn-BD, Catalan: ca-ES, Czech: cs-CZ, Danish: da-DK,
-  German: de-DE, 'English, British': en-GB, 'English, American': en-US, Spanish: es-ES,
-  'Spanish, American': es-US, 'Spanish, Mexican': es-MX, Basque: eu-ES, Filipino: fil-PH,
-  'French, Canadian': fr-CA, French: fr-FR, Galician: gl-ES, Georgian: ka-GE, Gujarati: gu-IN,
-  Croatian: hr-HR, Zulu: zu-ZA, Icelandic: is-IS, Italian: it-IT, Javanese: jv-ID,
-  Kannada: kn-IN, Khmer: km-KH, Lao: lo-LA, Latvian: lv-LV, Lithuanian: lt-LT, Hungarian: hu-HU,
-  Malayalam: ml-IN, Marathi: mr-IN, Dutch: nl-NL, Nepali: ne-NP, Norwegian Bokmål: nb-NO,
-  Polish: pl-PL, 'Portuguese, Brazilian': pt-BR, Portuguese: pt-PT, Romanian: ro-RO,
-  Sinhala: si-LK, Slovak: sk-SK, Slovenian: sl-SI, Sundanese: su-ID, Swahili: sw-TZ,
-  Finnish: fi-FI, Swedish: sv-SE, Tamil: ta-IN, Telugu: te-IN, Vietnamese: vi-VN,
-  Turkish: tr-TR, Urdu: ur-PK, Greek: el-GR, Bulgarian: bg-BG, Russian: ru-RU, Serbian: sr-RS,
-  Ukrainian: uk-UA, Hebrew: he-IL, Arabic: ar-IL, Persian: fa-IR, Hindi: hi-IN, Thai: th-TH,
-  Korean: ko-KR, Taiwanese: zh-TW, 'Chinese, Cantonese': yue-Hant-HK, Japanese: ja-JP,
-  'Chinese, Mandarin': zh}
-
-MQ:
-  server: 0.0.0.0
-  users:
-    mq_handler:
-      user: neon_api_utils
-      password: Klatchat2021
diff --git a/tests/config/valid_docker-compose-services.json b/tests/config/valid_docker-compose-services.json
deleted file mode 100644
index 38577d30..00000000
--- a/tests/config/valid_docker-compose-services.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "neon_metrics_service": {
-    "container_name": "neon_metrics_service",
-    "hostname": "neon_metrics_service",
-    "image": "ghcr.io/neongeckocom/neon_metrics_service:dev",
-    "restart": "unless-stopped",
-    "links": [
-      "neon_rabbitmq"
-    ],
-    "volumes": [
-      "host_path:/config",
-      "host_path_metrics:/metrics"
-    ]
-  },
-  "neon_rabbitmq": {
-    "container_name": "neon_rabbitmq",
-    "hostname": "neon_rabbitmq",
-    "image": "rabbitmq:3-management",
-    "restart": "unless-stopped",
-    "ports": [
-      "15672:15672",
-      "5672:5672"
-    ],
-    "volumes": [
-      "host_path/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf",
-      "host_path:/config"
-    ]
-  },
-  "neon_api_proxy": {
-    "container_name": "neon_api_proxy",
-    "hostname": "neon_api_proxy",
-    "image": "ghcr.io/neongeckocom/neon_api_proxy:dev",
-    "restart": "unless-stopped",
-    "links": [
-      "neon_rabbitmq"
-    ],
-    "volumes": [
-      "host_path:/config"
-    ]
-  }
-}
\ No newline at end of file
diff --git a/tests/config/valid_rabbitmq.json b/tests/config/valid_rabbitmq.json
deleted file mode 100644
index 35321db5..00000000
--- a/tests/config/valid_rabbitmq.json
+++ /dev/null
@@ -1,105 +0,0 @@
-{
-  "rabbit_version": "3.9.9",
-  "rabbitmq_version": "3.9.9",
-  "product_name": "RabbitMQ",
-  "product_version": "3.9.9",
-  "users": [
-    {
-      "name": "admin",
-      "password_hash": "wf7UYT6N8MZOD4LAQIs9CrUIG4qZ9bK0/OviUS2y7uHBrhPZ",
-      "hashing_algorithm": "rabbit_password_hashing_sha256",
-      "tags": [
-        "administrator"
-      ],
-      "limits": {}
-    },
-    {
-      "name": "neon_api_utils",
-      "password_hash": "S8JsaNbG7436awNWb1XyehfVcueqOvJU5ecEimzbcVCQt+d3",
-      "hashing_algorithm": "rabbit_password_hashing_sha256",
-      "tags": [],
-      "limits": {}
-    },
-    {
-      "name": "neon_metrics",
-      "password_hash": "7SgPQoQRcOSMrRCMZotCUu9zeKBK+z3YtBcG7jZ+bS69GoFa",
-      "hashing_algorithm": "rabbit_password_hashing_sha256",
-      "tags": [],
-      "limits": {}
-    },
-    {
-      "name": "neon_api",
-      "password_hash": "Ua92n5KKsUtIY49mlEnZLSnUtM/9Is6McCCCcP2qMFXUk0os",
-      "hashing_algorithm": "rabbit_password_hashing_sha256",
-      "tags": [],
-      "limits": {}
-    }
-  ],
-  "vhosts": [
-    {
-      "name": "/"
-    },
-    {
-      "name": "/neon_metrics"
-    },
-    {
-      "name": "/neon_api"
-    }
-  ],
-  "permissions": [
-    {
-      "user": "admin",
-      "vhost": "/neon_metrics",
-      "configure": ".*",
-      "write": ".*",
-      "read": ".*"
-    },
-    {
-      "user": "admin",
-      "vhost": "/neon_api",
-      "configure": ".*",
-      "write": ".*",
-      "read": ".*"
-    },
-    {
-      "user": "neon_api_utils",
-      "vhost": "/neon_metrics",
-      "configure": ".*",
-      "write": ".*",
-      "read": ""
-    },
-    {
-      "user": "neon_metrics",
-      "vhost": "/neon_metrics",
-      "configure": ".*",
-      "write": ".*",
-      "read": ".*"
-    },
-    {
-      "user": "neon_api_utils",
-      "vhost": "/neon_api",
-      "configure": "./*",
-      "write": "./*",
-      "read": "./*"
-    },
-    {
-      "user": "neon_api",
-      "vhost": "/neon_api",
-      "configure": ".*",
-      "write": ".*",
-      "read": ".*"
-    }
-  ],
-  "topic_permissions": [],
-  "parameters": [],
-  "global_parameters": [
-    {
-      "name": "internal_cluster_id",
-      "value": "rabbitmq-cluster-id-WZ_YB7W-2OfQZV_NAU1_kg"
-    }
-  ],
-  "policies": [],
-  "queues": [],
-  "exchanges": [],
-  "bindings": []
-}
\ No newline at end of file
diff --git a/tests/test_diana_services.py b/tests/test_diana_services.py
deleted file mode 100644
index 5a1bc03c..00000000
--- a/tests/test_diana_services.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
-# All trademark and other rights reserved by their respective owners
-# Copyright 2008-2021 Neongecko.com Inc.
-# BSD-3
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright notice,
-#    this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from this
-#    software without specific prior written permission.
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-# CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
-# OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE,  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import os
-import subprocess
-import unittest
-import pytest
-from time import time
-from neon_utils.mq_utils import send_mq_request
-from neon_mq_connector.utils import wait_for_mq_startup
-
-
-class TestDianaServices(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls) -> None:
-        cls.cached_config_path = os.environ.get("NEON_CONFIG_PATH")
-        os.environ["NEON_CONFIG_PATH"] = os.path.join(os.path.dirname(__file__), "config")
-        cls.docker_compose_path = os.path.join(os.path.dirname(__file__), "test_diana_services_config")
-        subprocess.Popen(["/bin/bash", "-c", f"cd {cls.docker_compose_path} && docker-compose up"])
-        wait_for_mq_startup("0.0.0.0", 5672)
-
-    @classmethod
-    def tearDownClass(cls) -> None:
-        os.environ.pop("NEON_CONFIG_PATH")
-        if cls.cached_config_path:
-            os.environ["NEON_CONFIG_PATH"] = cls.cached_config_path
-        subprocess.Popen(["/bin/bash", "-c", f"cd {cls.docker_compose_path} && docker-compose down"])
-
-    @pytest.mark.skip
-    def test_api_proxy(self):
-        resp = send_mq_request("/neon_api", {"test": True,
-                                             "service": "api_test_endpoint"}, "neon_api_input", "neon_api_output")
-        self.assertIsInstance(resp, dict)
-        self.assertEqual(resp['status_code'], 200)
-
-        resp = send_mq_request("/neon_api", {"test": True,
-                                             "service": "not_implemented"}, "neon_api_input", "neon_api_output")
-        self.assertIsInstance(resp, dict)
-        self.assertEqual(resp['status_code'], 401)
-
-    @pytest.mark.skip
-    def test_brands_service(self):
-        brand_coupon_data = send_mq_request("/neon_coupons", {"brands": True,
-                                                              "coupons": True}, "neon_coupons_input")
-        self.assertTrue(brand_coupon_data["success"])
-
-    @pytest.mark.skip
-    def test_email_proxy(self):
-        request_data = {"recipient": "test@neongecko.com",
-                        "subject": "Test Message",
-                        "body": "This is a test\ncalled from neon_skill_tests.py in neon-utils",
-                        "attachments": None}
-        data = send_mq_request("/neon_emails", request_data, "neon_emails_input")
-        self.assertTrue(data.get("success"))
-
-    def test_metrics_service(self):
-        t = time()
-        send_mq_request("/neon_metrics", {"name": "test",
-                                          "time": t}, "neon_metrics_input", expect_response=False)
-        # TODO: Check metrics dir for this entry
-
-    @pytest.mark.skip
-    def test_script_parser(self):
-        with open(os.path.join(os.path.dirname(__file__), "ccl", "test.nct")) as f:
-            text = f.read()
-        resp = send_mq_request("/neon_script_parser", {"text": text,
-                                                       "meta": {"test": "val"}}, "neon_script_parser_input", timeout=45)
-        self.assertIsInstance(resp, dict)
-        self.assertIsInstance(resp["parsed_file"], str)
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tests/test_diana_services_config/.env b/tests/test_diana_services_config/.env
deleted file mode 100644
index 064fe4b2..00000000
--- a/tests/test_diana_services_config/.env
+++ /dev/null
@@ -1,2 +0,0 @@
-NEON_CONFIG_PATH=/home/d_mcknight/PycharmProjects/_.API/neon-diana-backend/tests/test_diana_services_config
-NEON_METRIC_PATH=/home/d_mcknight/PycharmProjects/_.API/neon-diana-backend/tests/test_diana_services_config/metrics
\ No newline at end of file
diff --git a/tests/test_diana_services_config/docker-compose.yml b/tests/test_diana_services_config/docker-compose.yml
deleted file mode 100644
index 871112c0..00000000
--- a/tests/test_diana_services_config/docker-compose.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-version: '3.1'
-networks:
-  mq_backend:
-services:
-  neon_rabbitmq:
-    container_name: neon-rabbitmq
-    image: rabbitmq:3-management
-    restart: always
-    networks:
-    - mq_backend
-    ports:
-    - 15672:15672
-    - 5672:5672
-    volumes:
-      - ${NEON_CONFIG_PATH}/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
-      - ${NEON_CONFIG_PATH}:/config
-  neon_metrics_service:
-    container_name: neon-metrics-service
-    image: ghcr.io/neongeckocom/neon_metrics_service:dev
-    restart: on-failure
-    networks:
-    - mq_backend
-    volumes:
-    - ${NEON_CONFIG_PATH}:/config
-    - ${NEON_CONFIG_PATH}/metrics:/metrics
-    labels:
-      kompose.service.type: headless
-  neon_api_proxy:
-    container_name: neon-api-proxy
-    image: ghcr.io/neongeckocom/neon_api_proxy:dev
-    restart: on-failure
-    networks:
-    - mq_backend
-    volumes:
-    - ${NEON_CONFIG_PATH}:/config
-    labels:
-      kompose.service.type: headless
diff --git a/tests/test_diana_services_config/mq_config.json b/tests/test_diana_services_config/mq_config.json
deleted file mode 100644
index 9faeea12..00000000
--- a/tests/test_diana_services_config/mq_config.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "server": "neon_rabbitmq",
-  "users": {
-    "neon_metrics_connector": {
-      "user": "neon_metrics",
-      "password": "0kUv28tVK0Omw3H4HOq7AqnDERoUqPmWx3-vdsLjmRQ"
-    },
-    "neon_api_connector": {
-      "user": "neon_api",
-      "password": "5akSUyWvSEV62ymdc6DvADUEnx46W5E8vZFmFjYbYGI"
-    }
-  }
-}
\ No newline at end of file
diff --git a/tests/test_diana_services_config/ngi_auth_vars.yml b/tests/test_diana_services_config/ngi_auth_vars.yml
deleted file mode 100644
index ec3e64e8..00000000
--- a/tests/test_diana_services_config/ngi_auth_vars.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-track_my_brands: {}
-emails: {}
-api_services: {}
\ No newline at end of file
diff --git a/tests/test_diana_services_config/rabbit_mq_config.json b/tests/test_diana_services_config/rabbit_mq_config.json
deleted file mode 100644
index c63938b3..00000000
--- a/tests/test_diana_services_config/rabbit_mq_config.json
+++ /dev/null
@@ -1,105 +0,0 @@
-{
-  "rabbit_version": "3.9.9",
-  "rabbitmq_version": "3.9.9",
-  "product_name": "RabbitMQ",
-  "product_version": "3.9.9",
-  "users": [
-    {
-      "name": "admin",
-      "password_hash": "zTCCKdLkkImVHINvBxosIMB7zgo+Ohvlfaoqz7iMQK7afX2o",
-      "hashing_algorithm": "rabbit_password_hashing_sha256",
-      "tags": [
-        "administrator"
-      ],
-      "limits": {}
-    },
-    {
-      "name": "neon_api_utils",
-      "password_hash": "au3vx9SxJqT01D+kczKOCgWsotSLcPCZxFOipTgFGaapsFso",
-      "hashing_algorithm": "rabbit_password_hashing_sha256",
-      "tags": [],
-      "limits": {}
-    },
-    {
-      "name": "neon_metrics",
-      "password_hash": "6H2oEqaqeThINnGtRrruAqh69iih48iCnKTPe58sLl6hW7tZ",
-      "hashing_algorithm": "rabbit_password_hashing_sha256",
-      "tags": [],
-      "limits": {}
-    },
-    {
-      "name": "neon_api",
-      "password_hash": "t15jXuyThl06tgnO1zp9n+id5rCITh2xXoikmj6nJHYlGdwL",
-      "hashing_algorithm": "rabbit_password_hashing_sha256",
-      "tags": [],
-      "limits": {}
-    }
-  ],
-  "vhosts": [
-    {
-      "name": "/"
-    },
-    {
-      "name": "/neon_metrics"
-    },
-    {
-      "name": "/neon_api"
-    }
-  ],
-  "permissions": [
-    {
-      "user": "admin",
-      "vhost": "/neon_metrics",
-      "configure": ".*",
-      "write": ".*",
-      "read": ".*"
-    },
-    {
-      "user": "admin",
-      "vhost": "/neon_api",
-      "configure": ".*",
-      "write": ".*",
-      "read": ".*"
-    },
-    {
-      "user": "neon_api_utils",
-      "vhost": "/neon_metrics",
-      "configure": ".*",
-      "write": ".*",
-      "read": ""
-    },
-    {
-      "user": "neon_metrics",
-      "vhost": "/neon_metrics",
-      "configure": ".*",
-      "write": ".*",
-      "read": ".*"
-    },
-    {
-      "user": "neon_api_utils",
-      "vhost": "/neon_api",
-      "configure": "./*",
-      "write": "./*",
-      "read": "./*"
-    },
-    {
-      "user": "neon_api",
-      "vhost": "/neon_api",
-      "configure": ".*",
-      "write": ".*",
-      "read": ".*"
-    }
-  ],
-  "topic_permissions": [],
-  "parameters": [],
-  "global_parameters": [
-    {
-      "name": "internal_cluster_id",
-      "value": "rabbitmq-cluster-id-CTk1SeHQ9dgq7XGJpl-YLg"
-    }
-  ],
-  "policies": [],
-  "queues": [],
-  "exchanges": [],
-  "bindings": []
-}
\ No newline at end of file
diff --git a/tests/test_diana_services_config/rabbitmq.conf b/tests/test_diana_services_config/rabbitmq.conf
deleted file mode 100644
index f1682722..00000000
--- a/tests/test_diana_services_config/rabbitmq.conf
+++ /dev/null
@@ -1,1 +0,0 @@
-load_definitions = /config/rabbit_mq_config.json
\ No newline at end of file
diff --git a/tests/test_diana_utils.py b/tests/test_diana_utils.py
index 93cbe9dc..9ece8bb2 100644
--- a/tests/test_diana_utils.py
+++ b/tests/test_diana_utils.py
@@ -26,112 +26,152 @@
 
 import json
 import os
-import sys
+import shutil
 import unittest
-import docker
-import docker.models.containers
-
-from docker.errors import APIError
-from mock import Mock
-from ruamel.yaml import YAML
-
-sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
-from neon_diana_utils.utils import _run_clean_rabbit_mq, cleanup_docker_container, create_diana_configurations, \
-    write_neon_mq_config, write_rabbit_config, write_docker_compose
-
-
-class TestDianaUtils(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls) -> None:
-        cls.cached_config_path = os.environ.get("NEON_CONFIG_PATH")
-        os.environ["NEON_CONFIG_PATH"] = os.path.join(os.path.dirname(__file__), "config")
-
-    @classmethod
-    def tearDownClass(cls) -> None:
-        os.environ.pop("NEON_CONFIG_PATH")
-        if cls.cached_config_path:
-            os.environ["NEON_CONFIG_PATH"] = cls.cached_config_path
-
-    def test_run_clean_rmq_docker(self):
-        container = _run_clean_rabbit_mq()
-        self.assertIsInstance(container, docker.models.containers.Container)
-        self.assertIn(container, docker.from_env().containers.list())
-        cleanup_docker_container(container)
-        self.assertNotIn(container, docker.from_env().containers.list(all=True))
-
-        _run_clean_rabbit_mq()
-        with self.assertRaises(APIError):
-            _run_clean_rabbit_mq()
-        container = _run_clean_rabbit_mq(True)
-        self.assertIsInstance(container, docker.models.containers.Container)
-        self.assertIn(container, docker.from_env().containers.list())
-        cleanup_docker_container(container)
-        self.assertNotIn(container, docker.from_env().containers.list(all=True))
-
-    def test_create_diana_configurations(self):
-        create_diana_configurations("admin", "admin", {"neon_rabbitmq",
-                                                       "neon_api_proxy",
-                                                       "neon_metrics_service",
-                                                       "neon_unknown"})
-        valid_files = {"docker-compose.yml", "mq_config.json", "rabbitmq.conf", "rabbit_mq_config.json"}
-        for file in valid_files:
-            file_path = os.path.join(os.environ["NEON_CONFIG_PATH"], file)
-            self.assertTrue(os.path.isfile(file_path))
-            os.remove(file_path)
-
-    def test_write_neon_mq_config(self):
-        sample_creds = {
-            'neon_metrics_connector':
-                {'user': 'neon_metrics',
-                 'password': 'jWAPqP4a2oL8mOq2T5Lj8cxMo72KwXv6keh1WItNWSk'},
-            'neon_api_connector':
-                {'user': 'neon_api',
-                 'password': 'N3h-w4I561l20gZ_cSRvuMHdseYVYA-wHlw-6aJh0VQ'}}
-        write_neon_mq_config(sample_creds)
-        config_file = os.path.join(os.environ["NEON_CONFIG_PATH"], "mq_config.json")
-        self.assertTrue(os.path.isfile(config_file))
-        with open(config_file) as f:
-            config = json.load(f)
-        self.assertEqual(config["users"], sample_creds)
-        self.assertIsInstance(config["server"], str)
-        os.remove(config_file)
-
-    def test_write_neon_rabbit_config(self):
-        with open(os.path.join(os.path.dirname(__file__), "config", "valid_rabbitmq.json")) as f:
-            sample_config = json.load(f)
-        api = Mock()
-
-        def get_definitions():
-            return sample_config
-        api.get_definitions = get_definitions
-
-        write_rabbit_config(api)
-
-        config_file = os.path.join(os.environ["NEON_CONFIG_PATH"], "rabbit_mq_config.json")
-        self.assertTrue(os.path.isfile(config_file))
-        with open(config_file) as f:
-            config = json.load(f)
-        self.assertEqual(config, sample_config)
-        os.remove(config_file)
-
-        rabbitmq_conf = os.path.join(os.environ["NEON_CONFIG_PATH"], "rabbitmq.conf")
-        self.assertTrue(os.path.isfile(rabbitmq_conf))
-        with open(rabbitmq_conf) as f:
-            self.assertIn("load_definitions = /config/rabbit_mq_config.json", f.read())
-        os.remove(rabbitmq_conf)
-
-    def test_write_docker_compose(self):
-        with open(os.path.join(os.path.dirname(__file__), "config", "valid_docker-compose-services.json")) as f:
-            sample_config = json.load(f)
-        write_docker_compose(sample_config)
-
-        docker_compose_file = os.path.join(os.environ["NEON_CONFIG_PATH"], "docker-compose.yml")
-        self.assertTrue(os.path.isfile(docker_compose_file))
-        with open(docker_compose_file) as f:
-            docker_compose = YAML().load(f)
-        self.assertIn("version", docker_compose)
-        self.assertEqual(docker_compose["services"], sample_config)
-        os.remove(docker_compose_file)
+import yaml
+
+from unittest.mock import patch
+from os.path import join, dirname, isdir, isfile
+
+
+class TestConfiguration(unittest.TestCase):
+    def test_orchestrator(self):
+        from neon_diana_utils.configuration import Orchestrator
+        self.assertEqual(Orchestrator("kubernetes"), Orchestrator.KUBERNETES)
+        self.assertEqual(Orchestrator("docker-compose"), Orchestrator.COMPOSE)
+        with self.assertRaises(ValueError):
+            Orchestrator("invalid")
+
+    def test_validate_output_file(self):
+        from neon_diana_utils.configuration import validate_output_path
+        valid_output_path = join(dirname(__file__), 'test_output')
+        self.assertFalse(isdir(valid_output_path))
+        self.assertTrue(validate_output_path(valid_output_path))
+        self.assertTrue(validate_output_path(valid_output_path))
+        self.assertFalse(validate_output_path(dirname(valid_output_path)))
+        self.assertTrue(validate_output_path(join(valid_output_path,
+                                                  'test.txt')))
+        shutil.rmtree(valid_output_path)
+
+    @patch("click.confirm")
+    def test_make_keys_config(self, confirm):
+        from neon_diana_utils.configuration import make_keys_config
+        confirm.return_value = False
+        test_output_file = join(dirname(__file__), "test_keys.yaml")
+        # Test without file write
+        config = make_keys_config(False, test_output_file)
+        self.assertFalse(isfile(test_output_file))
+        self.assertIsInstance(config, dict)
+        self.assertIsInstance(config['keys'], dict)
+        self.assertIsInstance(config['ChatGPT'], dict)
+        self.assertIsInstance(config['FastChat'], dict)
+
+        # Test with file write
+        config2 = make_keys_config(True, test_output_file)
+        self.assertTrue(isfile(test_output_file))
+        self.assertEqual(config, config2)
+        with open(test_output_file, 'r') as f:
+            from_disk = yaml.safe_load(f)
+        self.assertEqual(from_disk, config2)
+
+        os.remove(test_output_file)
+
+    def test_generate_rmq_config(self):
+        from neon_diana_utils.configuration import generate_rmq_config
+        test_output_file = join(dirname(__file__), "test_rmq.json")
+        # Test without write
+        config = generate_rmq_config("test_admin1", "test_password1")
+        self.assertFalse(isfile(test_output_file))
+        self.assertIsInstance(config, dict)
+        self.assertEqual(config['users'][0], {'name': 'neon_api_utils',
+                                              'password': 'Klatchat2021',
+                                              'tags': ['backend', 'user']})
+        self.assertEqual(config['users'][-1], {'name': 'test_admin1',
+                                               'password': 'test_password1',
+                                               'tags': ['administrator']})
+        self.assertEqual(set(config.keys()), {'users', 'vhosts', 'permissions'})
+        for user in config['users']:
+            self.assertEqual(set(user.keys()), {'name', 'password', 'tags'})
+        for vhost in config['vhosts']:
+            self.assertEqual(set(vhost.keys()), {'name'})
+            self.assertTrue(vhost['name'].startswith('/'))
+        for permission in config['permissions']:
+            self.assertEqual(set(permission.keys()),
+                             {'user', 'vhost', 'configure', 'write', 'read'})
+
+        # Test with valid write
+        config2 = generate_rmq_config("test_admin2", "test_password2",
+                                      test_output_file)
+        self.assertTrue(isfile(test_output_file))
+        self.assertNotEqual(config, config2)
+        self.assertEqual(config2['users'][-1], {'name': 'test_admin2',
+                                                'password': 'test_password2',
+                                                'tags': ['administrator']})
+        with open(test_output_file, 'r') as f:
+            from_disk = json.load(f)
+        self.assertEqual(config2, from_disk)
+
+        # Test invalid write
+        config3 = generate_rmq_config("test_admin3", "test_password3",
+                                      test_output_file)
+        self.assertTrue(isfile(test_output_file))
+        self.assertNotEqual(config2, config3)
+        self.assertEqual(config3['users'][-1], {'name': 'test_admin3',
+                                                'password': 'test_password3',
+                                                'tags': ['administrator']})
+        with open(test_output_file, 'r') as f:
+            from_disk = json.load(f)
+        self.assertEqual(config2, from_disk)
+
+        os.remove(test_output_file)
+
+    def test_generate_mq_auth_config(self):
+        from neon_diana_utils.configuration import generate_mq_auth_config, \
+            generate_rmq_config
+        mq_config = generate_rmq_config("test", "test")
+        auth = generate_mq_auth_config(mq_config)
+        self.assertIsInstance(auth, dict)
+        for service_auth in auth.values():
+            rmq_users = [user for user in mq_config['users']
+                         if user['name'] == service_auth['user']]
+            self.assertEqual(len(rmq_users), 1)
+            user = rmq_users[0]
+            self.assertEqual(user['name'], service_auth['user'])
+            self.assertEqual(user['password'], service_auth['password'])
+
+    def test_configure_backend(self):
+        from neon_diana_utils.configuration import configure_backend
+        # TODO
+
+
+class TestKubernetesUtils(unittest.TestCase):
+    def test_generate_github_secret(self):
+        from neon_diana_utils.kubernetes_utils import create_github_secret
+        output_path = os.path.join(os.path.dirname(__file__), "test_gh.yaml")
+        output_file = create_github_secret("username",
+                                           "123123adsfasdf123123",
+                                           output_path)
+        self.assertTrue(os.path.isfile(output_file))
+        self.assertEqual(output_file, output_path)
+        with open(output_file) as f:
+            contents = yaml.safe_load(f)
+        self.assertEqual(contents['kind'], "Secret")
+        self.assertEqual(contents['type'], "kubernetes.io/dockerconfigjson")
+        self.assertEqual(contents['metadata']['name'], 'github-auth')
+        self.assertEqual(contents['data']['.dockerconfigjson'],
+                         "eyJhdXRocyI6IHsiZ2hjci5pbyI6IHsiYXV0aCI6ICJkWE5sY201"
+                         "aGJXVTZNVEl6TVRJellXUnpabUZ6WkdZeE1qTXhNak09In19fQ=="
+                         )
+
+        with self.assertRaises(FileExistsError):
+            create_github_secret("test", "test", output_path)
+
+        os.remove(output_path)
+
+
+class TestRabbitMQAPI(unittest.TestCase):
+    from neon_diana_utils.rabbitmq_api import RabbitMQAPI
+    # TODO
 
 
 if __name__ == '__main__':