Repository: oxheadalpha/tezos-k8s (language: python)

Issue:
Don't create bootstrap node p2p service if Zerotier is in use

WIP Doc updates



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cc9baaf73..a26a0dcf8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,6 +2,13 @@ name: CI
 
 on:
   push:
+    # Trigger CI on all branch pushes but...
+    branches:
+      - "**"
+    # don't double trigger on new tag push when creating release. Should only
+    # trigger once for the release.
+    tags-ignore:
+      - "*.*.*"
   pull_request:
   release:
     types: [created]
@@ -9,6 +16,9 @@ on:
 jobs:
   check-lint-and-format:
     runs-on: ubuntu-latest
+    defaults:
+      run:
+        working-directory: ./mkchain
     steps:
       - uses: actions/checkout@v2
 
@@ -20,7 +30,7 @@ jobs:
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
-          pip install black isort autoflake
+          pip install black isort==5.7.0 autoflake
 
       - name: Remove unused imports
         run: |
@@ -29,7 +39,7 @@ jobs:
           autoflake . -r --remove-all-unused-imports -c
 
       - name: Sort imports
-        run: isort . --check --diff -rc
+        run: isort . --check --diff
 
       - name: black
         run: black . --check
@@ -37,6 +47,9 @@ jobs:
   test:
     runs-on: ${{ matrix.os }}
     needs: check-lint-and-format
+    defaults:
+      run:
+        working-directory: ./mkchain
     strategy:
       fail-fast: false
       matrix:
@@ -62,7 +75,7 @@ jobs:
       - name: pytest
         run: pytest tests
 
-  publish:
+  publish_mkchain:
     needs: test
     runs-on: ${{ matrix.os }}
     strategy:
@@ -87,15 +100,17 @@ jobs:
 
       - name: Build Python package
         run: python setup.py bdist_wheel
+        working-directory: mkchain
 
       - name: Install wheels
         run: pip install dist/*.whl
+        working-directory: mkchain
 
       - name: Release
         uses: softprops/action-gh-release@v1
         if: startsWith(github.ref, 'refs/tags/')
         with:
-          files: dist/*.whl
+          files: "**/mkchain/dist/*.whl"
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
@@ -105,6 +120,7 @@ jobs:
         with:
           user: __token__
           password: ${{ secrets.PYPI_TOKEN }}
+          packages_dir: mkchain/dist/
 
   list_containers_to_publish:
     # based on
@@ -119,7 +135,7 @@ jobs:
 
       - id: gen-containers-matrix
         run: |
-          container_list=$(jq -c -n --arg cont "$(find docker  -mindepth 1 -type d -exec basename {} \;)" '{ container: $cont | split("\n")}')
+          container_list=$(jq -c -n --arg cont "$(find -name 'Dockerfile' -printf '%h\n' | sort -u | sed 's/.\///')" '{ container: $cont | split("\n")}')
           echo "Dynamically generated container list based on subdirectories of the docker directory. The following list will be passed to next build step:"
           echo $container_list
           echo "::set-output name=matrix::$container_list"
@@ -143,7 +159,7 @@ jobs:
         uses: docker/setup-buildx-action@v1
 
       - name: Login to DockerHub
-        uses: docker/login-action@v1 
+        uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           password: ${{ secrets.DOCKERHUB_PASSWORD }}
@@ -158,7 +174,7 @@ jobs:
 
           curl -s -H "Authorization: JWT ${TOKEN}" "https://hub.docker.com/v2/repositories/" \
           --data 'description="tezos-k8s ${{ matrix.container }}"' \
-          --data 'full_description="The container for https://github.com/tqtezos/tezos-k8s/tree/master/docker/${{ matrix.container }}' \
+          --data 'full_description="The container for https://github.com/tqtezos/tezos-k8s/tree/master/${{ matrix.container }}' \
           --data 'is_private=false' \
           --data 'name=tezos-k8s-${{ matrix.container }}' \
           --data "namespace=tqtezos" || true
@@ -173,8 +189,68 @@ jobs:
       - name: Publish ${{ matrix.container }} container to Docker Hub
         uses: docker/build-push-action@v2
         with:
-          context: docker/${{ matrix.container }}/.
-          file: docker/${{ matrix.container }}/Dockerfile
+          context: ${{ matrix.container }}/.
+          file: ${{ matrix.container }}/Dockerfile
           push: true
           tags: ${{ steps.docker_meta.outputs.tags }}
           labels: ${{ steps.docker_meta.outputs.labels }}
+
+  lint_helm_charts:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Set up Helm
+        uses: azure/setup-helm@v1
+        with:
+          version: v3.4.2
+
+      - name: Lint Helm Charts
+        run: helm lint charts/*
+
+  publish_helm_charts:
+    runs-on: ubuntu-latest
+    needs: [lint_helm_charts, publish_containers]
+    if: github.event_name == 'release' && github.event.action == 'created'
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Install yq
+        run: |
+          sudo wget -q https://github.com/mikefarah/yq/releases/download/v4.2.0/yq_linux_amd64 -O /usr/bin/yq
+          sudo chmod +x /usr/bin/yq
+
+      - name: Get Release Version
+        id: get_release_version
+        run: echo "::set-output name=RELEASE_VERSION::${GITHUB_REF/refs\/tags\//}"
+
+      - name: Set Helm Chart and Image Versions
+        run: |
+          RELEASE_VERSION=${{ steps.get_release_version.outputs.RELEASE_VERSION }}
+
+          for chart in charts/*; do
+            [[ ! -d "$chart" ]] && continue
+            echo $chart
+
+            # Update Chart.yaml with release version
+            yq e ".version = \"$RELEASE_VERSION\"" -i "$chart/Chart.yaml"
+
+            # Get tqtezos/tezos-k8s images specified in values.yaml
+            tq_images=$(yq e '(.images[] | select(. == "tezos-k8s*")) | path | .[-1]' "$chart/values.yaml")
+            # Update the release version of each of tezos-k8s images
+            for image in $tq_images; do
+              image_name=$(yq e ".images.$image" $chart/values.yaml | sed -E "s/tezos-k8s-(.*):.*/\1/")
+              yq e ".images.$image = \"tqtezos/tezos-k8s-$image_name:$RELEASE_VERSION\"" -i $chart/values.yaml
+            done
+          done
+
+      - name: Publish Helm charts
+        uses: stefanprodan/helm-gh-pages@master
+        with:
+          linting: off # We already linted in a job before
+          token: ${{ secrets.CI_GITHUB_TOKEN }}
+          branch: main
+          owner: ${{ github.repository_owner }}
+          repository: tezos-helm-charts
diff --git a/.gitignore b/.gitignore
index 708a13976..3c4e4b2bb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,10 +3,15 @@
 dist
 *.pyc
 *.egg-info
+*.eggs
 .venv*
 .DS_Store
-build
+.vscode
+
 .devspace/
-mkchain-devspace.yaml
-/*_chain.yaml
-/*_chain_invite.yaml
+build
+
+# Ignore mkchain generated files
+*_values.yaml
+
+charts/tezos/charts
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 1834643cb..1ed1c84a3 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -1,43 +1,56 @@
-# How to develop on mkchain
+# Helm Chart Development
 
-Using mkchain on a non-release workdir requires building custom containers in your minikube docker context.
+## Prerequisites
 
-## Set up minikube and install custom containers
+- Install [devspace](https://devspace.sh/cli/docs/introduction).
 
-[Follow installation instructions](https://devspace.sh/cli/docs/introduction).
+- Ensure minikube is running:
 
-Ensure minikube is running:
+  ```shell
+  minikube start
+  ```
 
-``` shell
-minikube start
-```
+- Configure your shell to use minikube's docker daemon:
 
-Build all containers:
+  ```shell
+  eval $(minikube docker-env)
+  ```
 
-``` shell
-devspace build --skip-push --tag=dev
-```
+# Using devspace
 
-# Develop with devspace
-(This is all still being worked on! Things should become more clear with the introduction of Helm.)
+- Tell devspace which namespace to use:
 
-## Deploy a chain and have devspace watch only k8s manifest:
-- Generate constants (with or without Zerotier flags)
-- `devspace dev --var=CHAIN_NAME="$CHAIN_NAME"`
+  ```shell
+  devspace use namespace tqtezos
+  ```
 
-## Deploy chain with Zerotier and also watch the ZT docker file:
-- Generate constants with ZT flags
-- Use zerotier profile: `devspace dev --var=CHAIN_NAME="$CHAIN_NAME" -p zerotier`
-- You may leave out the profile flag if you don't want devspace to watch for zerotier file changes.
+- Run `mkchain` to generate your Helm values. (Note: Devspace will only deploy `rpc-auth` if you use the `rpc-auth` profile, regardless if you set it in mkchain. This is to avoid devspace deployment issues. See more below.)
 
-## RPC Auth backend
-I've experimented with the idea of giving each service its own devspace.yaml. Inside RPC auth's devspace.yaml, it defines mkchain as a dependency which by running RPC auth devspace.yaml as the entrypoint, will also spin up mkchain (with zerotier if you generated it). Devspace is currently limited in that it will not reload dependencies if their files change. Therefore right now, the recommended way if you would like to view logs and/or have dependencies auto reload, is to run `devspace dev/logs` in multiple shells.
+- Run `helm dependency update charts/tezos`. This grabs all the Tezos chart dependencies and packages them inside the chart's `charts/` directory. Currently this is just the `rpc-auth` chart.
 
-### Deploy RPC auth backend and watch its files:
-- `cd ./docker/rpc-auth`
-- `devspace dev --var=CHAIN_NAME="$CHAIN_NAME"`
+- Set a `CHAIN_NAME` env var.
 
-## Misc
-- Devspace runs a hook to increase `fs.inotify.max_user_watches` to 1048576 in minikube. This is to avoid a "no space left on device" error. See [here](https://serverfault.com/questions/963529/minikube-k8s-kubectl-failed-to-watch-file-no-space-left-on-device) for more.
-- If you would like to avoid passing `--var=CHAIN_NAME="$CHAIN_NAME"` to devspace, you can execute in your shell `export CHAIN_NAME=my_chain`.
-- You can pass mkchain args like so: `devspace dev --var=CHAIN_NAME="$CHAIN_NAME" --var=MKCHAIN_ARGS="--number-of-nodes 2"`
+- Run `devspace dev --var CHAIN_NAME=$CHAIN_NAME` (you can leave out the `--var` flag if you used `export CHAIN_NAME=my-chain`).
+
+- You may add the `rpc-auth` devspace [profile](https://devspace.sh/cli/docs/configuration/profiles/basics) by using the `-p rpc-auth` flag in the `devspace dev` command. This tells devspace deploy `rpc-auth` and to redeploy it if its files change. You can also pass another `--var` flag for `rpc-auth` like so: `--var FLASK_ENV=<development|production>`. Devpsace defaults it to `development`. Running with `development` will allow the the python server to hot reload on file changes. Devspace does not need to restart the pod on file changes as the python server file is [synced](https://devspace.sh/cli/docs/configuration/development/file-synchronization) to the container.
+
+Devspace will now do a few things:
+
+- Create namespace if it doesn't already exist.
+- Runs a hook to enable the minikube nginx ingress addon. This is the gateway for external users to access the `rpc-auth` backend and to then make RPC calls to the Tezos node.
+- Runs a hook to increase `fs.inotify.max_user_watches` to 1048576 in minikube. This is to avoid a "no space left on device" error. See [here](https://serverfault.com/questions/963529/minikube-k8s-kubectl-failed-to-watch-file-no-space-left-on-device) for more.
+- Builds docker images and tags them.
+- Deploys Helm charts.
+- Starts [sync](https://devspace.sh/cli/docs/configuration/development/file-synchronization), [logging](https://devspace.sh/cli/docs/configuration/development/log-streaming), and [port-forwarding](https://devspace.sh/cli/docs/configuration/development/port-forwarding) services.
+  - Right now just `rpc-auth` produces logs from the python server.
+  - Port-forwarding allows you to directly communicate with containers, allowing for easy bootstrap node RPC calls, as well as requests to the `rpc-auth` server instead of having to go through the NGINX ingress. Example: `curl localhost:8732/chains/main/chain_id`
+- Will automatically redeploy Helm charts and rebuild docker images depending upon the files you modify.
+
+## Notes
+
+- Due to a current limitation of devspace, multiple profiles cannot be used at one time. Therefore, devspace will watch `zerotier` files even if tezos nodes are not configured to use it via `mkchain`. Preferably `zerotier` would also be a profile in addition to `rpc-auth` being one.
+
+# Releases
+The way releases currently work is that upon release, every component of the tezos-k8s repo will be bumped to that version. This is regardless if there were changes or not to that particular component. This is because tezos-k8s is a monorepo and we'd like to keep the versions consistent across the different components.
+
+...
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 85234bf19..000000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,4 +0,0 @@
-include tqchain/deployment/*
-include tqchain/utils/*
-include versioneer.py
-include tqchain/_version.py
diff --git a/MULTICLUSTER.md b/MULTICLUSTER.md
deleted file mode 100644
index c41cfd129..000000000
--- a/MULTICLUSTER.md
+++ /dev/null
@@ -1,191 +0,0 @@
-# Multicluster Permissioned Chain
-
-This tutorial will walk you through setting up a Tezos based
-permissioned blockchain. Upon successful completion of this tutorial
-you will have a permissioned chain running with six nodes in 2 clusters in a
-peer-to-peer network. This tutorial assumes the use of Minikube.
-
-
-## Prerequisites
-
-* docker
-* minikube
-* helm
-* python3
-* a ZeroTier network and api access token
-
-## Installing prerequisites
-
-This section varies depending on OS.
-
-### Mac w/ homebrew
-
-Make sure homebrew is installed:
-
-``` shell
-/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
-```
-
-Install prerequisites:
-
-``` shell
-brew install python3
-brew install minikube
-brew install helm
-```
-
-### Arch Linux
-
-```shell
-pacman -Syu && pacman -S python3 minikube kubectl kubectx helm
-```
-
-## Ensure minikube is running
-
-``` shell
-minikube start
-```
-
-## Setting up mkchain
-
-Install the mkchain program:
-
-``` shell
-python3 -m venv .venv
-source .venv/bin/activate
-pip install mkchain
-```
-
-Create a ZeroTier network:
-
-* Go to https://my.zerotier.com
-* Login with google credentials or create a new account
-* Create a new API access token by clicking on the "Generate New
-Token" button. Save the generated access token. e.g. "yEflQt726fjXuSUyQ73WqXvAFoijXkLt"
-* Go to https://my.zerotier.com/network
-* Create a new network by clicking on the "Create a Network"
-button. Save the 16 character generated network
-id. e.g. "1c33c1ced02a5eee"
-
-Set environment variables so we can access these values with later commands:
-``` shell
-ZT_TOKEN=yEflQt726fjXuSUyQ73WqXvAFoijXkLt
-ZT_NET=1c33c1ced02a5eee
-```
-
-Give your private chain a name:
-
-``` shell
-CHAIN_NAME=my_chain
-```
-
-Set unbuffered IO for python:
-
-``` shell
-PYTHONUNBUFFERED=x
-```
-
-Run the following command to create the helm configuration and feed it to helm:
-
-``` shell
-mkchain --zerotier-network $ZT_NET --zerotier-token $ZT_TOKEN $CHAIN_NAME
-helm install $CHAIN_NAME tezos-helm/ -f ${CHAIN_NAME}_values.yaml --namespace tqtezos --create-namespace
-```
-
-Your kubernetes cluster will now be running a series of jobs to
-perform the following tasks:
-
-* get a zerotier ip
-* generate a node identity
-* generate a genesis block for your chain
-* activate the protocol
-* bake the first block
-* start the bootstrap-node node and a baker to validate the chain
-
-You can find your node in the tqtezos namespace using kubectl.
-
-``` shell
-kubectl -n tqtezos get pods
-```
-
-You can view logs for your node using the following command:
-``` shell
-kubectl -n tqtezos logs -l appType=tezos -c tezos-node -f
-```
-
-Congratulations! You now have an operational Tezos based permissioned
-chain running one node.
-
-## Add nodes within the cluster
-
-You can configure a self-contained testnet within your cluster with
-a number of nodes of your choice by passing `--number-of-nodes` to
-mkchain.
-
-The nodes will establish peer-to-peer connections in a full mesh topology.
-
-If you previously spun up the chain with just one node, you may scale
-up your setup to an arbitrary number of nodes by adding more nodes to the node 
-list in the values yaml:
-
-```
-# ${CHAIN_NAME}_values.yaml
-...
-nodes:
-  - bake_for: baker
-  - {} # second non-baking node
-  - {} # third non-baking node
-...
-```
-
-## Add external nodes to the cluster
-
-Additional nodes can be added to your network by sharing a yaml file
-generated by the mkchain command.
-
-The name of this file is: `${CHAIN_NAME}_invite_values.yaml`
-
-Send this file to the recipients you want to invite.
-
-### on the computer of the joining node
-
-The member needs to follow the installation instructions, build the private zerotier container, put the invite file in the mkchain directory, then run:
-
-``` shell
-helm install $CHAIN_NAME tezos-helm/ -f ${CHAIN_NAME}_invite_values.yaml
-```
-
-At this point additional nodes will be added in a full mesh
-topology.
-
-Congratulations! You now have a multi-cluster Tezos based permissioned
-chain.
-
-Check that the nodes have matching heads by comparing their hashes:
-
-``` shell
-kubectl get pod -n tqtezos -l appType=tezos -o name | while read line; do kubectl -n tqtezos exec $line -c tezos-node -- /usr/local/bin/tezos-client rpc get /chains/main/blocks/head/hash; done
-```
-
-## RPC Authentication
-You can optionally spin up an RPC authentication server allowing clients with your given permission to make RPC calls:
-
-```shell
-mkchain create $CHAIN_NAME --rpc-auth ...
-```
-
-### Current authentication flow
-The client authenticates themselves and will receive a secret url that allows them to make RPC calls.
-- You provide a trusted client with your cluster ip/address and your private tezos chain id.
-- To see your chain id:
-  ```shell
-  kubectl exec -it -n tqtezos deployment/tezos-bootstrap-node -c tezos-node -- tezos-client rpc get /chains/main/chain_id
-  ```
-  Or use a tool like like [Lens](https://k8slens.dev/) or run `kubectl logs -n tqtezos deployment/tezos-bootstrap-node -c tezos-node` to see the tezos node logs at the top.
-- client runs: `scripts/rpc-auth-client.sh --cluster-address $CLUSTER_IP --tz-alias $TZ_ALIAS --chain-id $CHAIN_ID`
-- TZ_ALIAS is the alias of a client's tz address secret key. The client's secret key is used to sign some data for the server to then verify.
-- If the client is authenticated, the response should contain a secret url such as `http://192.168.64.51/tezos-node-rpc/ffff3eb3d7dd4f6bbff3f2fd096722ae/`
-- Client can then make RPC requests:
-  - `curl http://192.168.64.51/tezos-node-rpc/ffff3eb3d7dd4f6bbff3f2fd096722ae/chains/main/chain_id`
-  - Bug in tezos client v8, so as of version `tezos/tezos:master_08d3405e_20201113152010`:
-    - `tezos-client --endpoint http://192.168.64.51/tezos-node-rpc/ffff3eb3d7dd4f6bbff3f2fd096722ae/ rpc get chains/main/chain_id`
diff --git a/README.md b/README.md
index 6af4c94ce..4ef543112 100644
--- a/README.md
+++ b/README.md
@@ -1,61 +1,207 @@
-# tezos-k8s
+# Tezos k8s Private Chain
 
-Helper program to configure the tezos-chain helm chart
+This README will walk you through setting up a Tezos based private blockchain where you will spin up one bootstrap node as well as additional peer nodes if you'd like. Using `minikube`, these nodes will be running in a peer-to-peer network via a Zerotier VPN, inside of a Kubernetes cluster.
 
-## quickstart
+## Prerequisites
 
-``` shell
-python3 -m venv .venv
-source .venv/bin/activate
-pip install mkchain
-mkchain my-chain
-helm install my-chain tezos-helm/ -f my-chain_values.yaml --namespace tqtezos --create-namespace
+- python3
+- [kubectl](https://kubernetes.io/docs/reference/kubectl/kubectl/)
+- [minikube](https://minikube.sigs.k8s.io/docs/)
+- [helm](https://helm.sh/)
+- A [ZeroTier](https://www.zerotier.com/) network with api access token
+
+## Installing prerequisites
+
+This section varies depending on OS.
+
+### Mac with homebrew
+
+Make sure [homebrew](https://brew.sh/) is installed:
+
+```shell
+/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
+```
+
+Install prerequisites:
+
+```shell
+brew install python3 kubectl minikube helm
 ```
 
-## Generate constants
+### Arch Linux
 
-Your chain is uniquely defined by a set of values such as bootstrap account keys, chain id, timestamp...
+```shell
+pacman -Syu && pacman -S python3 minikube kubectl kubectx helm
+```
+
+## Zerotier
+
+Zerotier is a VPN service that the Tezos nodes in your cluster will use to communicate with each other.
+
+Create a ZeroTier network:
 
-Create these values:
+- Go to https://my.zerotier.com
+- Login with google credentials or create a new account
+- Create a new API access token by clicking on the "Generate New
+  Token" button. Save the generated access token. e.g. "yEflQt726fjXuSUyQ73WqXvAFoijXkLt"
+- Go to https://my.zerotier.com/network
+- Create a new network by clicking on the "Create a Network"
+  button. Save the 16 character generated network
+  id. e.g. "1c33c1ced02a5eee"
 
-``` shell
-mkchain $CHAIN_NAME
+Set Zerotier environment variables in order to access these values with later commands:
+
+```shell
+ZT_TOKEN=yEflQt726fjXuSUyQ73WqXvAFoijXkLt
+ZT_NET=1c33c1ced02a5eee
 ```
 
-It will create two 2 yaml files, `<$CHAIN_NAME>_values.yaml` and `<$CHAIN_NAME>_invite_values.yaml`.
+## Start Minikube
 
-### Chain parameters
+```shell
+minikube start
+```
 
-You can modify these parameters by:
+If you don't have docker installed, or you do but don't want to use your local install, configure your shell environment to use minikube’s Docker daemon:
 
-* passing parameters to `mkchain`
-* modifying the values yaml file generated by `mkchain`
-* passing argument to `helm install` commands, which will selectively override the yaml parameters
+```shell
+eval $(minikube docker-env)
+```
 
-| YAML Parameter | mkchain argument | Description | Default |
-| ----- | ----------- | ------ | ----- |
-| number_of_nodes | --number-of-nodes | Number of peers in the cluster | 1 |
-| docker_image | --docker-image | Version of the Tezos docker image | tezos/tezos:v7-release |
-| zerotier_network | --zerotier-network | Zerotier network id for external chain access | |
-| zerotier_token | --zerotier-token | Zerotier token for external chain access | |
-| bootstrap_peer | --bootstrap-peer | peer ip to join | |
-| rpc_auth | --rpc-auth | whether or not an RPC auth server will be spun up | False |
+## mkchain
 
+mkchain is a python script that generates Helm values which Helm then uses to create your Tezos chain on k8s.
 
-## private chain
+Follow _just_ the [Install mkchain](./mkchain/README.md#install-mkchain) step in `mkchain/README.md` under the Quickstart. See there for more info on how you can customize your chain.
 
-### create a self-contained chain
-$CHAIN_NAME: is your private chain's name
+Set as an environment variable the name you would like to give to your chain:
 
-``` shell
-mkchain $CHAIN_NAME
-helm install $CHAIN_NAME tezos-helm/ -f ${CHAIN_NAME}_values.yaml --namespace tqtezos --create-namespace
+```shell
+CHAIN_NAME=my-chain
 ```
+NOTE: k8s will throw an error when deploying if your chain name format does not match certain requirements. From k8s: `DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`
+
+Set [unbuffered IO](https://docs.python.org/3.6/using/cmdline.html#envvar-PYTHONUNBUFFERED) for python:
+
+```shell
+PYTHONUNBUFFERED=x
+```
+
+## Start your chain
+
+Run the following commands to create the Helm values, get the Helm chart repo, and install the Helm chart to start your chain.
+
+```shell
+mkchain $CHAIN_NAME --zerotier-network $ZT_NET --zerotier-token $ZT_TOKEN
+
+helm repo add tqtezos https://tqtezos.github.io/tezos-helm-charts
+
+helm install $CHAIN_NAME tqtezos/tezos-chain \
+--values ./${CHAIN_NAME}_values.yaml \
+--namespace tqtezos --create-namespace
+```
+
+Your kubernetes cluster will now be running a series of jobs to
+perform the following tasks:
+
+- get a zerotier ip
+- generate a node identity
+- generate a genesis block for your chain
+- activate the protocol
+- bake the first block
+- start the bootstrap-node node and a baker to validate the chain
+
+You can find your node in the tqtezos namespace using kubectl.
+
+```shell
+kubectl -n tqtezos get pods
+```
+
+You can view logs for your node using the following command:
+
+```shell
+kubectl -n tqtezos logs -l appType=tezos -c tezos-node -f
+```
+
+Congratulations! You now have an operational Tezos based permissioned
+chain running one node.
+
+## Adding nodes within the cluster
+
+You can configure a self-contained testnet within your cluster with
+a number of nodes of your choice by passing `--number-of-nodes N` to `mkchain`. Pass this along with your previously used flags (`--zerotier-network` and `--zerotier-token`). You can use this to scale up and down.
+
+Or if you previously spun up the chain using `mkchain`, you may scale up/down your setup to an arbitrary number of nodes by adding or removing nodes in the `nodes` list in the values yaml file:
+
+```
+# <CURRENT WORKING DIRECTORY>/${CHAIN_NAME}_values.yaml
+...
+nodes:
+  - bake_for: baker
+  - {} # second non-baking node
+  - {} # third non-baking node
+...
+```
+
+To upgrade your Helm release run:
+
+```shell
+helm upgrade $CHAIN_NAME tqtezos/tezos-chain \
+--values ./${CHAIN_NAME}_values.yaml \
+--namespace tqtezos
+```
+
+The nodes will start up and establish peer-to-peer connections in a full mesh topology.
+
+List all of your running nodes: `kubectl -n tqtezos get pods -l appType=tezos`
+
+## Adding external nodes to the cluster
+
+External nodes to your local cluster can be added to your network by sharing a yaml file
+generated by the `mkchain` command.
+
+The file is located at: `<CURRENT WORKING DIRECTORY>/${CHAIN_NAME}_invite_values.yaml`
+
+Send this file to the recipients you want to invite.
+
+### On the computer of the joining node
+
+The member needs to:
+
+1. Follow the [prerequisite installation instructions](#installing-prerequisites)
+2. [Start minikube](#start-minikube)
+3. [Install mkchain](./mkchain/README.md#install-mkchain)
+
+Then run:
+
+```shell
+helm repo add tqtezos https://tqtezos.github.io/tezos-helm-charts
+
+helm install $CHAIN_NAME tqtezos/tezos-chain \
+--values <LOCATION OF ${CHAIN_NAME}_invite_values.yaml> \
+--namespace tqtezos --create-namespace
+```
+
+At this point additional nodes will be added in a full mesh
+topology.
+
+Congratulations! You now have a multi-node Tezos based permissioned chain.
+
+On each computer, run this command to check that the nodes have matching heads by comparing their hashes (it may take a minute for the nodes to sync up):
+
+```shell
+kubectl get pod -n tqtezos -l appType=tezos -o name |
+while read line;
+  do kubectl -n tqtezos exec $line -c tezos-node -- /usr/local/bin/tezos-client rpc get /chains/main/blocks/head/hash;
+done
+```
+
+## RPC Authentication
 
-## multi-cluster chain
+You can optionally spin up an RPC authentication backend allowing trusted users to make RPC requests to your cluster.
 
-See the [multicluster howto](MULTICLUSTER.md).
+Follow the steps [here](./rpc-auth/README.md).
 
-## development
+# Notes
 
-See [development](DEVELOPMENT.md).
+We recommend using a very nice GUI for your k8s Tezos chain infrastructure called [Lens](https://k8slens.dev/). This allows you to easily see all of the k8s resources that have been spun up as well as to view the logs for your Tezos nodes.
diff --git a/chain-initiator/Dockerfile b/chain-initiator/Dockerfile
new file mode 100644
index 000000000..05c0cbd09
--- /dev/null
+++ b/chain-initiator/Dockerfile
@@ -0,0 +1,5 @@
+FROM tezos/tezos:v8-release
+RUN sudo apk add jq
+COPY entrypoint.sh /
+ENTRYPOINT ["/entrypoint.sh"]
+CMD []
diff --git a/chain-initiator/entrypoint.sh b/chain-initiator/entrypoint.sh
new file mode 100755
index 000000000..d49ea2b38
--- /dev/null
+++ b/chain-initiator/entrypoint.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+set -x
+# wait for node to exist
+until nslookup tezos-bootstrap-node-rpc; do echo waiting for tezos-bootstrap-node-rpc; sleep 2; done;
+# wait for node to respond to rpc
+until wget -O- http://tezos-bootstrap-node-rpc:8732/version; do sleep 2; done;
+
+protocol_hash=$(echo $CHAIN_PARAMS | jq -r '.protocol_hash')
+/usr/local/bin/tezos-client -A tezos-bootstrap-node-rpc -P 8732 -d /var/tezos/client -l --block genesis activate protocol "${protocol_hash}" with fitness -1 and key genesis and parameters /etc/tezos/parameters.json
diff --git a/charts/rpc-auth/.helmignore b/charts/rpc-auth/.helmignore
new file mode 100644
index 000000000..0e8a0eb36
--- /dev/null
+++ b/charts/rpc-auth/.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/charts/rpc-auth/Chart.yaml b/charts/rpc-auth/Chart.yaml
new file mode 100644
index 000000000..b700f2aca
--- /dev/null
+++ b/charts/rpc-auth/Chart.yaml
@@ -0,0 +1,9 @@
+apiVersion: v2
+name: authenticated-rpc-tezos-chain
+description: A Helm chart for Tezos RPC authentication for a private chain
+type: application
+version: 0.0.0
+keywords:
+  - tezos
+  - blockchain
+home: https://github.com/tqtezos/tezos-k8s
diff --git a/charts/rpc-auth/templates/_helpers.tpl b/charts/rpc-auth/templates/_helpers.tpl
new file mode 100644
index 000000000..b059a2f81
--- /dev/null
+++ b/charts/rpc-auth/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "my-chart.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 "my-chart.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 "my-chart.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "my-chart.labels" -}}
+helm.sh/chart: {{ include "my-chart.chart" . }}
+{{ include "my-chart.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "my-chart.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "my-chart.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "my-chart.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "my-chart.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/tezos-helm/templates/rpc-auth.yaml b/charts/rpc-auth/templates/rpc-auth.yaml
similarity index 87%
rename from tezos-helm/templates/rpc-auth.yaml
rename to charts/rpc-auth/templates/rpc-auth.yaml
index cd2fcef92..e7fce4532 100644
--- a/tezos-helm/templates/rpc-auth.yaml
+++ b/charts/rpc-auth/templates/rpc-auth.yaml
@@ -1,4 +1,3 @@
-{{ if .Values.rpc_auth }}
 apiVersion: v1
 kind: Service
 metadata:
@@ -22,7 +21,7 @@ metadata:
     app: redis
 spec:
   accessModes:
-    - ReadWriteMany
+    - ReadWriteOnce
   resources:
     requests:
       storage: 5Gi
@@ -104,7 +103,8 @@ spec:
   selector:
     app: rpc-auth
   ports:
-    - port: 8080
+    - name: rpc-auth
+      port: 8080
       targetPort: 8080
 ---
 apiVersion: apps/v1
@@ -124,21 +124,21 @@ spec:
     spec:
       containers:
         - name: rpc-auth
-          image: {{ .Values.container_images.rpc_auth_image | quote }}
+          image: {{ .Values.tezos_k8s_images.rpc_auth | quote }}
           imagePullPolicy: IfNotPresent
           ports:
             - containerPort: 8080
           env:
             - name: REDIS_HOST
-              value: redis-service
+              value: {{ .Values.redis.host | quote }}
             - name: REDIS_PORT
-              value: "6379"
+              value: {{ .Values.redis.port | quote }}
             - name: TEZOS_RPC_SERVICE
               value: tezos-bootstrap-node-rpc
             - name: TEZOS_RPC_SERVICE_PORT
               value: "8732"
 ---
-apiVersion: networking.k8s.io/v1
+apiVersion: networking.k8s.io/v1beta1
 kind: Ingress
 metadata:
   name: rpc-vending-machine-ingress
@@ -153,20 +153,16 @@ spec:
           - path: /vending-machine/(.*)
             pathType: Exact
             backend:
-              service:
-                name: rpc-auth
-                port:
-                  number: 8080
+              serviceName: rpc-auth
+              servicePort: 8080
           # Client provides signed data and gets back a secret url
           - path: /vending-machine
             pathType: Exact
             backend:
-              service:
-                name: rpc-auth
-                port:
-                  number: 8080
+              serviceName: rpc-auth
+              servicePort: 8080
 ---
-apiVersion: networking.k8s.io/v1
+apiVersion: networking.k8s.io/v1beta1
 kind: Ingress
 metadata:
   name: tezos-rpc-ingress
@@ -189,8 +185,5 @@ spec:
           - path: /tezos-node-rpc/(.*?)/(.*)
             pathType: Exact
             backend:
-              service:
-                name: tezos-bootstrap-node-rpc
-                port:
-                  number: 8732
-{{- end }}
+              serviceName: tezos-bootstrap-node-rpc
+              servicePort: 8732
diff --git a/charts/rpc-auth/values.yaml b/charts/rpc-auth/values.yaml
new file mode 100644
index 000000000..79a4a86dc
--- /dev/null
+++ b/charts/rpc-auth/values.yaml
@@ -0,0 +1,10 @@
+# Images not part of the tezos-k8s repo go here
+images:
+
+# Images that are part of the tezos-k8s repo go here
+tezos_k8s_images:
+  rpc_auth: tezos-k8s-rpc-auth:dev
+
+redis:
+  host: redis-service
+  port: 6379
diff --git a/charts/tezos/.helmignore b/charts/tezos/.helmignore
new file mode 100644
index 000000000..0e8a0eb36
--- /dev/null
+++ b/charts/tezos/.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/charts/tezos/Chart.lock b/charts/tezos/Chart.lock
new file mode 100644
index 000000000..6a1bd656f
--- /dev/null
+++ b/charts/tezos/Chart.lock
@@ -0,0 +1,6 @@
+dependencies:
+- name: authenticated-rpc-tezos-chain
+  repository: file://../rpc-auth
+  version: 0.0.0
+digest: sha256:359e854ddadc434084a5894da498d9170a778a6aa345414f97d4c570eaf9b99b
+generated: "2021-01-11T16:20:15.112419-05:00"
diff --git a/tezos-helm/Chart.yaml b/charts/tezos/Chart.yaml
similarity index 59%
rename from tezos-helm/Chart.yaml
rename to charts/tezos/Chart.yaml
index 51682c2ca..b53fdced9 100644
--- a/tezos-helm/Chart.yaml
+++ b/charts/tezos/Chart.yaml
@@ -2,9 +2,12 @@ apiVersion: v2
 name: tezos-chain
 description: A Helm chart for Tezos nodes on Kubernetes
 type: application
-version: 0.0.1
-appVersion: 1.0b1
+version: 0.0.0
 keywords:
   - tezos
   - blockchain
 home: https://github.com/tqtezos/tezos-k8s
+dependencies:
+  - name: authenticated-rpc-tezos-chain
+    repository: file://../rpc-auth
+    condition: rpc_auth
diff --git a/tezos-helm/templates/bootstrap.yaml b/charts/tezos/templates/bootstrap.yaml
similarity index 65%
rename from tezos-helm/templates/bootstrap.yaml
rename to charts/tezos/templates/bootstrap.yaml
index 14d37818c..7907c1463 100644
--- a/tezos-helm/templates/bootstrap.yaml
+++ b/charts/tezos/templates/bootstrap.yaml
@@ -18,104 +18,45 @@ spec:
           name: job-done
           imagePullPolicy: IfNotPresent
       initContainers:
-        - command:
-            - sh
-            - /opt/tqtezos/import_keys.sh
+        - image: {{ .Values.tezos_k8s_images.key_importer }}
+          name: key-importer
+          imagePullPolicy: IfNotPresent
           envFrom:
             - secretRef:
                 name: tezos-secret
-          image: {{ .Values.container_images.tezos_docker_image | quote}}
-          name: import-keys
           volumeMounts:
-            - mountPath: /opt/tqtezos
-              name: tqtezos-utils
             - mountPath: /var/tezos
               name: var-volume
-        - command:
-            - python
-            - /opt/tqtezos/generateTezosConfig.py
+        - image: {{ .Values.tezos_k8s_images.config_generator }}
+          imagePullPolicy: IfNotPresent
+          name: config-generator
+          args:
             - "--generate-parameters-json"
           envFrom:
             - configMapRef:
                 name: tezos-config
-          image: python:alpine
-          imagePullPolicy: IfNotPresent
-          name: tezos-config-generator
           volumeMounts:
             - mountPath: /etc/tezos
               name: config-volume
-            - mountPath: /opt/tqtezos
-              name: tqtezos-utils
             - mountPath: /var/tezos
               name: var-volume
-        - command:
-            - sh
-            - "-c"
-            - "until nslookup tezos-bootstrap-node-rpc; do echo waiting for tezos-bootstrap-node-rpc; sleep 2; done;"
-          image: busybox
-          name: wait-for-node
+        - image: "{{ .Values.tezos_k8s_images.chain_initiator }}"
           imagePullPolicy: IfNotPresent
-        - args:
-            - "-A"
-            - tezos-bootstrap-node-rpc
-            - "-P"
-            - '8732'
-            - "-d"
-            - /var/tezos/client
-            - "-l"
-            - "--block"
-            - genesis
-            - activate
-            - protocol
-            - "{{ .Values.protocol.hash }}"
-            - with
-            - fitness
-            - '-1'
-            - and
-            - key
-            - genesis
-            - and
-            - parameters
-            - /etc/tezos/parameters.json
-          command:
-            - /usr/local/bin/tezos-client
-          image: "{{ .Values.container_images.tezos_docker_image }}"
-          name: activate
-          volumeMounts:
-            - mountPath: /etc/tezos
-              name: config-volume
-            - mountPath: /var/tezos
-              name: var-volume
-        - args:
-            - "-A"
-            - tezos-bootstrap-node-rpc
-            - "-P"
-            - '8732'
-            - "-d"
-            - /var/tezos/client
-            - "-l"
-            - bake
-            - for
-            - baker
-            - "--minimal-timestamp"
-          command:
-            - /usr/local/bin/tezos-client
-          image: "{{ .Values.container_images.tezos_docker_image }}"
-          name: bake-once
+          name: chain-initiator
           volumeMounts:
             - mountPath: /etc/tezos
               name: config-volume
             - mountPath: /var/tezos
               name: var-volume
+          envFrom:
+            - configMapRef:
+                name: tezos-config
       restartPolicy: Never
       volumes:
         - emptyDir: {}
           name: config-volume
         - emptyDir: {}
           name: var-volume
-        - configMap:
-            name: tqtezos-utils
-          name: tqtezos-utils
 {{ end }}
 ---
 {{- if not .Values.is_invitation }}
@@ -145,7 +86,7 @@ spec:
             - /etc/tezos/config.json
           command:
             - /usr/local/bin/tezos-node
-          image: "{{ .Values.container_images.tezos_docker_image }}"
+          image: "{{ .Values.images.tezos }}"
           imagePullPolicy: IfNotPresent
           name: tezos-node
           ports:
@@ -183,7 +124,7 @@ spec:
             - baker
           command:
             - "{{ .Values.baker_command }}"
-          image: "{{ .Values.container_images.tezos_docker_image }}"
+          image: "{{ .Values.images.tezos }}"
           name: baker-job
           imagePullPolicy: IfNotPresent
           volumeMounts:
@@ -201,7 +142,7 @@ spec:
             - baker
           command:
             - sh
-          image: "{{ .Values.container_images.zerotier_docker_image }}"
+          image: "{{ .Values.tezos_k8s_images.zerotier }}"
           imagePullPolicy: IfNotPresent
           name: zerotier
           securityContext:
@@ -220,7 +161,7 @@ spec:
         - envFrom:
             - configMapRef:
                 name: zerotier-config
-          image: "{{ .Values.container_images.zerotier_docker_image }}"
+          image: "{{ .Values.tezos_k8s_images.zerotier }}"
           imagePullPolicy: IfNotPresent
           name: get-zerotier-ip
           securityContext:
@@ -231,38 +172,29 @@ spec:
                 - SYS_ADMIN
             privileged: true
           volumeMounts:
-            - mountPath: /opt/tqtezos
-              name: tqtezos-utils
             - mountPath: /var/tezos
               name: var-volume
             - mountPath: /dev/net/tun
               name: dev-net-tun
 {{- end }}
-        - command:
-            - sh
-            - /opt/tqtezos/import_keys.sh
+        - image: {{ .Values.tezos_k8s_images.key_importer }}
+          name: key-importer
+          imagePullPolicy: IfNotPresent
           envFrom:
             - secretRef:
                 name: tezos-secret
-          image: {{ .Values.container_images.tezos_docker_image | quote}}
-          imagePullPolicy: IfNotPresent
-          name: import-keys
           volumeMounts:
-            - mountPath: /opt/tqtezos
-              name: tqtezos-utils
             - mountPath: /var/tezos
               name: var-volume
-        - command:
-            - python
-            - /opt/tqtezos/generateTezosConfig.py
+        - image: {{ .Values.tezos_k8s_images.config_generator }}
+          imagePullPolicy: IfNotPresent
+          name: config-generator
+          args:
             - "--generate-config-json"
             - "--generate-parameters-json"
           envFrom:
             - configMapRef:
                 name: tezos-config
-          image: python:alpine
-          imagePullPolicy: IfNotPresent
-          name: tezos-config-generator
           env:
             - name: MY_POD_IP
               valueFrom:
@@ -271,8 +203,6 @@ spec:
           volumeMounts:
             - mountPath: /etc/tezos
               name: config-volume
-            - mountPath: /opt/tqtezos
-              name: tqtezos-utils
             - mountPath: /var/tezos
               name: var-volume
         - args:
@@ -280,7 +210,7 @@ spec:
             - "[ -f /var/tezos/node/identity.json ] || (mkdir -p /var/tezos/node && /usr/local/bin/tezos-node identity generate 0 --data-dir /var/tezos/node --config-file /etc/tezos/config.json)"
           command:
             - /bin/sh
-          image: {{ .Values.container_images.tezos_docker_image | quote }}
+          image: {{ .Values.images.tezos | quote }}
           name: identity-job
           imagePullPolicy: IfNotPresent
           volumeMounts:
@@ -296,9 +226,6 @@ spec:
           name: dev-net-tun
         - emptyDir: {}
           name: config-volume
-        - configMap:
-            name: tqtezos-utils
-          name: tqtezos-utils
         - name: var-volume
           persistentVolumeClaim:
             claimName: tezos-bootstrap-node-pv-claim
diff --git a/tezos-helm/templates/configs.yaml b/charts/tezos/templates/configs.yaml
similarity index 86%
rename from tezos-helm/templates/configs.yaml
rename to charts/tezos/templates/configs.yaml
index c5c065022..bc55c6d7f 100644
--- a/tezos-helm/templates/configs.yaml
+++ b/charts/tezos/templates/configs.yaml
@@ -7,7 +7,8 @@ data:
       "chain_name": "{{ .Values.chain_name }}",
       "genesis_block": "{{ .Values.genesis.genesis_chain_id }}",
       "timestamp": "{{ .Values.genesis.bootstrap_timestamp }}",
-      "zerotier_in_use": {{ .Values.zerotier_in_use }}
+      "zerotier_in_use": {{ .Values.zerotier_in_use }},
+      "protocol_hash": "{{ .Values.protocol.hash }}"
     }
 kind: ConfigMap
 metadata:
@@ -60,23 +61,23 @@ value:
 
 apiVersion: v1
 data:
-  BOOTSTRAP_ACCOUNTS: !!binary |
-{{ include "bootstrapAccounts" . | fromYaml | values | join " " | b64enc | b64enc | indent 4 }}
+  BOOTSTRAP_ACCOUNTS: |
+{{ include "bootstrapAccounts" . | fromYaml | values | join " " | b64enc | indent 4 }}
 {{- if .Values.is_invitation }}
-  KEYS_TYPE: !!binary |
-{{ "public" | b64enc | b64enc | indent 4 }}
+  KEYS_TYPE: |
+{{ "public" | b64enc | indent 4 }}
   {{- range $account := $.Values.accounts }}
     {{- if not $account.private }}
-{{ $account.name | indent 2 }}_public_key: !!binary |
+{{ $account.name | indent 2 }}_public_key: |
 {{ $account.key | b64enc | indent 4 }}
     {{- end }}
   {{- end }}
 {{- else }}
-  KEYS_TYPE: !!binary |
-{{ "secret" | b64enc | b64enc | indent 4 }}
+  KEYS_TYPE: |
+{{ "secret" | b64enc | indent 4 }}
   {{- range $account := $.Values.accounts }}
     {{- if $account.private }}
-{{ $account.name | indent 2 }}_secret_key: !!binary |
+{{ $account.name | indent 2 }}_secret_key: |
 {{ $account.key | b64enc | indent 4 }}
     {{- end }}
   {{- end }}
diff --git a/tezos-helm/templates/node.yaml b/charts/tezos/templates/node.yaml
similarity index 81%
rename from tezos-helm/templates/node.yaml
rename to charts/tezos/templates/node.yaml
index e676330df..d814a38e7 100644
--- a/tezos-helm/templates/node.yaml
+++ b/charts/tezos/templates/node.yaml
@@ -31,7 +31,7 @@ spec:
             - /etc/tezos/config.json
           command:
             - /usr/local/bin/tezos-node
-          image: {{ .Values.container_images.tezos_docker_image | quote }}
+          image: {{ .Values.images.tezos | quote }}
           imagePullPolicy: IfNotPresent
           name: tezos-node
           ports:
@@ -66,7 +66,7 @@ spec:
             - baker
           command:
             - sh
-          image: "{{ .Values.container_images.zerotier_docker_image }}"
+          image: "{{ .Values.tezos_k8s_images.zerotier }}"
           imagePullPolicy: IfNotPresent
           name: zerotier
           securityContext:
@@ -85,7 +85,7 @@ spec:
         - envFrom:
             - configMapRef:
                 name: zerotier-config
-          image: "{{ .Values.container_images.zerotier_docker_image }}"
+          image: "{{ .Values.tezos_k8s_images.zerotier }}"
           imagePullPolicy: IfNotPresent
           name: get-zerotier-ip
           securityContext:
@@ -96,37 +96,28 @@ spec:
                 - SYS_ADMIN
             privileged: true
           volumeMounts:
-            - mountPath: /opt/tqtezos
-              name: tqtezos-utils
             - mountPath: /var/tezos
               name: var-volume
             - mountPath: /dev/net/tun
               name: dev-net-tun
 {{- end }}
-        - command:
-            - sh
-            - /opt/tqtezos/import_keys.sh
+        - image: {{ .Values.tezos_k8s_images.key_importer }}
+          name: key-importer
+          imagePullPolicy: IfNotPresent
           envFrom:
             - secretRef:
                 name: tezos-secret
-          image: {{ .Values.container_images.tezos_docker_image | quote }}
-          name: import-keys
-          imagePullPolicy: IfNotPresent
           volumeMounts:
-            - mountPath: /opt/tqtezos
-              name: tqtezos-utils
             - mountPath: /var/tezos
               name: var-volume
-        - command:
-            - python
-            - /opt/tqtezos/generateTezosConfig.py
+        - image: {{ .Values.tezos_k8s_images.config_generator }}
+          imagePullPolicy: IfNotPresent
+          name: config-generator
+          args:
             - "--generate-config-json"
           envFrom:
             - configMapRef:
                 name: tezos-config
-          image: python:alpine
-          imagePullPolicy: IfNotPresent
-          name: tezos-config-generator
           env:
             - name: MY_POD_IP
               valueFrom:
@@ -135,8 +126,6 @@ spec:
           volumeMounts:
             - mountPath: /etc/tezos
               name: config-volume
-            - mountPath: /opt/tqtezos
-              name: tqtezos-utils
             - mountPath: /var/tezos
               name: var-volume
         - args:
@@ -144,7 +133,7 @@ spec:
             - "[ -f /var/tezos/node/identity.json ] || (mkdir -p /var/tezos/node && /usr/local/bin/tezos-node identity generate 0 --data-dir /var/tezos/node --config-file /etc/tezos/config.json)"
           command:
             - /bin/sh
-          image: {{ .Values.container_images.tezos_docker_image | quote }}
+          image: {{ .Values.images.tezos | quote }}
           name: identity-job
           imagePullPolicy: IfNotPresent
           volumeMounts:
@@ -160,9 +149,6 @@ spec:
           name: dev-net-tun
         - emptyDir: {}
           name: config-volume
-        - configMap:
-            name: tqtezos-utils
-          name: tqtezos-utils
   volumeClaimTemplates:
     - metadata:
         name: var-volume
diff --git a/tezos-helm/templates/static.yaml b/charts/tezos/templates/static.yaml
similarity index 100%
rename from tezos-helm/templates/static.yaml
rename to charts/tezos/templates/static.yaml
diff --git a/tezos-helm/values.yaml b/charts/tezos/values.yaml
similarity index 55%
rename from tezos-helm/values.yaml
rename to charts/tezos/values.yaml
index 6bdcb42b7..4cc0bde16 100644
--- a/tezos-helm/values.yaml
+++ b/charts/tezos/values.yaml
@@ -1,7 +1,5 @@
 baker: true
 baker_command: tezos-baker-007-PsDELPH1
-bootstrap_peers:
-- tezos-bootstrap-node-p2p:9732
 chain_name: YOUR_CHAIN_NAME_HERE
 is_invitation: false
 rpc_auth: false
@@ -9,31 +7,35 @@ zerotier_in_use: false
 zerotier_config:
   zerotier_network: YOUR_NETWORK_ID_HERE
   zerotier_token: YOUR_ZEROTIER_TOKEN_HERE
-container_images:
-  zerotier_docker_image: "tqtezos/tezos-k8s-zerotier:1.0b1"
-  rpc_auth_image: "tqtezos/tezos-k8s-rpc-auth:1.0b1"
-  tezos_docker_image: tezos/tezos:v7-release
+
+# Images not part of the tezos-k8s repo go here
+images:
+  tezos: tezos/tezos:v8-release
+# Images that are part of the tezos-k8s repo go here
+tezos_k8s_images:
+  chain_initiator: tezos-k8s-chain-initiator:dev
+  config_generator: tezos-k8s-config-generator:dev
+  key_importer: tezos-k8s-key-importer:dev
+  zerotier: tezos-k8s-zerotier:dev
+
 protocol:
-  bootstrap_mutez: '4000000000000'
+  bootstrap_mutez: "4000000000000"
   hash: PsDELPH1Kxsxt8f9eWbxQeRxkjfbxoqM52jvs5Y5fBxWWh4ifpo
 genesis:
   genesis_chain_id: "YOUR_GENESIS_CHAIN_ID_HERE"
-  bootstrap_timestamp: 'YOUR_BOOTSTRAP_TIMESTAMP_HERE'
+  bootstrap_timestamp: "YOUR_BOOTSTRAP_TIMESTAMP_HERE"
 accounts:
   - name: sample1
-    key: !!binary |
-      WldSd2EzWk1jR1psUVZNNU5EUmpSRGRXT0hVelRFaHdVR0oxVVZaUlVYVjRZVzFOYmxGR1dubG5T
-      RkZhZFZKUmVHSklPVFU0
+    key: edsk...
     private: true
     bootstrap: true
     baker: true
   - name: sample2
-    key: !!binary |
-      SAMPLE==
+    key: edsk...
     private: true
     bootstrap: true
 nodes:
-  - bake_for: baker
+  - bake_for: sample2
 # TODO
 # archive nodes, node with open rpc
 # take into account fundraisers accounts that people need to activate
diff --git a/config-generator/Dockerfile b/config-generator/Dockerfile
new file mode 100644
index 000000000..2ce163099
--- /dev/null
+++ b/config-generator/Dockerfile
@@ -0,0 +1,5 @@
+FROM python:alpine
+ENV PYTHONUNBUFFERED=1
+COPY entrypoint.py /
+ENTRYPOINT ["python", "/entrypoint.py"]
+CMD []
diff --git a/tezos-helm/utils/generateTezosConfig.py b/config-generator/entrypoint.py
similarity index 98%
rename from tezos-helm/utils/generateTezosConfig.py
rename to config-generator/entrypoint.py
index 9bcbe4464..fb71bc631 100644
--- a/tezos-helm/utils/generateTezosConfig.py
+++ b/config-generator/entrypoint.py
@@ -1,4 +1,3 @@
-#!/bin/python
 import argparse
 import json
 import os
@@ -36,12 +35,14 @@ def main():
 
     if main_args.generate_config_json:
         net_addr = None
-        bootstrap_peers = CHAIN_PARAMS["bootstrap_peers"]
+        bootstrap_peers = CHAIN_PARAMS.get("bootstrap_peers", [])
         if CHAIN_PARAMS["zerotier_in_use"]:
             with open("/var/tezos/zerotier_data.json", "r") as f:
                 net_addr = json.load(f)[0]["assignedAddresses"][0].split("/")[0]
             if bootstrap_peers == [] and "bootstrap" not in socket.gethostname():
                 bootstrap_peers.extend(get_zerotier_bootstrap_peer_ips())
+        else:
+            bootstrap_peers.append("tezos-bootstrap-node-p2p:9732")
 
         config_json = json.dumps(
             get_node_config(
diff --git a/devspace.yaml b/devspace.yaml
index b137d86e9..508e56afb 100755
--- a/devspace.yaml
+++ b/devspace.yaml
@@ -1,59 +1,119 @@
 version: v1beta9
 deployments:
   - name: chain
-    namespace: tqtezos
-    kubectl:
-      manifests:
-        - ./mkchain-devspace.yaml
+    helm:
+      chart:
+        name: ./charts/tezos
+      valuesFiles:
+        - ./${CHAIN_NAME}_values.yaml
+      # Don't deploy rpc-auth based on value generated by mkchain. You need to
+      # use the devspace rpc-auth profile to deploy it. `devspace dev ... -p rpc-auth`
+      values:
+        rpc_auth: false
+
+images:
+  zerotier:
+    image: tezos-k8s-zerotier
+    dockerfile: ./zerotier/Dockerfile
+    context: ./zerotier
+  chain-initiator:
+    image: tezos-k8s-chain-initiator
+    dockerfile: ./chain-initiator/Dockerfile
+    context: ./chain-initiator
+  key-importer:
+    image: tezos-k8s-key-importer
+    dockerfile: ./key-importer/Dockerfile
+    context: ./key-importer
+  config-generator:
+    image: tezos-k8s-config-generator
+    dockerfile: ./config-generator/Dockerfile
+    context: ./config-generator
 
 dev:
+  ports:
+    - labelSelector:
+        app: tezos-bootstrap-node
+      forward:
+        - port: 8732
+          remotePort: 8732
   logs:
     disabled: true
   autoReload:
     deployments:
       - chain
     paths:
-      - ./tqchain/deployment/*
+      - ./zerotier/*
 
 hooks:
-  - command: sh
+  - command: minikube
     args:
-      - -c
-      -  minikube ssh "sudo sysctl fs.inotify.max_user_watches=1048576"
+      - addons
+      - enable
+      - ingress
     when:
       before:
-        deployments: all
+        pullSecrets: all
   - command: sh
     args:
       - -c
-      - "mkchain ${ACTION} ${CHAIN_NAME} ${CHAIN_ARGS} > mkchain-devspace.yaml"
+      - minikube ssh "sudo sysctl fs.inotify.max_user_watches=1048576"
     when:
       before:
-        deployments: all
+        pullSecrets: all
 
 vars:
-  - name: ACTION
-    question: Mkchain action (create or invite)
-    default: "create"
-    source: env
   - name: CHAIN_NAME
     question: Name of the chain as passed to generate-constants
     default: "devspace"
     source: env
-  - name: CHAIN_ARGS
-    default: ""
+  - name: FLASK_ENV # development | production
+    question: Deployment env to run RPC authentication
+    default: development
     source: env
 
 profiles:
-  - name: zerotier
-    replace:
+  - name: rpc-auth
+    strategicMerge:
       images:
-        zerotier:
-          image: tezos-zerotier
-          dockerfile: ./docker/zerotier/Dockerfile
-          context: ./docker/zerotier
+        rpc-auth:
+          image: tezos-k8s-rpc-auth
+          dockerfile: ./rpc-auth/Dockerfile
+          context: ./rpc-auth
+          build:
+            docker:
+              options:
+                buildArgs:
+                  FLASK_ENV: ${FLASK_ENV}
+      dev:
+        logs:
+          disabled: false
+          images:
+            - rpc-auth
+        sync:
+          - imageName: rpc-auth
+            localSubPath: ./rpc-auth/server
     patches:
       - op: add
-        path: dev.autoReload.images
+        path: deployments
+        value:
+          name: rpc-auth
+          helm:
+            chart:
+              name: ./charts/rpc-auth
+      - op: add
+        path: dev.autoReload.deployments
+        value: rpc-auth
+      - op: add
+        path: dev.autoReload.paths
+        value: ./rpc-auth/Dockerfile
+      - op: add
+        path: dev.autoReload.paths
+        value: ./rpc-auth/server/requirements.txt
+      - op: add
+        path: dev.ports
         value:
-          - zerotier
+          labelSelector:
+            app: rpc-auth
+          forward:
+            - port: 8080
+              remotePort: 8080
diff --git a/docker/README.md b/docker/README.md
deleted file mode 100644
index d37732376..000000000
--- a/docker/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Docker containers
-=================
-
-This contains the containers necessary for docker-k8s operation.
-
-Any new folder put here will automatically be released by CI.
diff --git a/docker/rpc-auth/devspace.yaml b/docker/rpc-auth/devspace.yaml
deleted file mode 100644
index 4295ad36f..000000000
--- a/docker/rpc-auth/devspace.yaml
+++ /dev/null
@@ -1,54 +0,0 @@
-version: v1beta9
-deployments:
-  - name: rpc-auth
-    namespace: tqtezos
-    kubectl:
-      manifests:
-        - ../../tqchain/deployment/rpc-auth.yaml
-
-images:
-  rpc-auth:
-    image: tezos-rpc-auth
-    dockerfile: ./Dockerfile
-    context: ./
-    build:
-      docker:
-        options:
-          buildArgs:
-            FLASK_ENV: ${FLASK_ENV}
-dev:
-  logs:
-    images:
-      - rpc-auth
-  sync:
-    - imageName: rpc-auth
-      namespace: tqtezos
-      labelSelector:
-        app: rpc-auth
-      localSubPath: ./server
-  autoReload:
-    images:
-      - rpc-auth
-    deployments:
-      - rpc-auth
-
-hooks:
-  - command: minikube
-    args:
-      - addons
-      - enable
-      - ingress
-    when:
-      before:
-        deployments: rpc-auth
-
-vars:
-  - name: FLASK_ENV # development | production
-    question: Deployment env to run RPC authentication
-    default: development
-    source: env
-
-dependencies:
-  - name: chain
-    source:
-      path: ../../
diff --git a/key-importer/Dockerfile b/key-importer/Dockerfile
new file mode 100644
index 000000000..2172387ba
--- /dev/null
+++ b/key-importer/Dockerfile
@@ -0,0 +1,4 @@
+FROM tezos/tezos:v8-release
+COPY entrypoint.sh /
+ENTRYPOINT ["/entrypoint.sh"]
+CMD []
diff --git a/tezos-helm/utils/import_keys.sh b/key-importer/entrypoint.sh
old mode 100644
new mode 100755
similarity index 100%
rename from tezos-helm/utils/import_keys.sh
rename to key-importer/entrypoint.sh
diff --git a/.gitattributes b/mkchain/.gitattributes
similarity index 100%
rename from .gitattributes
rename to mkchain/.gitattributes
diff --git a/mkchain/MANIFEST.in b/mkchain/MANIFEST.in
new file mode 100644
index 000000000..44bda8232
--- /dev/null
+++ b/mkchain/MANIFEST.in
@@ -0,0 +1,2 @@
+include versioneer.py
+include tqchain/_version.py
diff --git a/mkchain/README.md b/mkchain/README.md
new file mode 100644
index 000000000..ad4859dd4
--- /dev/null
+++ b/mkchain/README.md
@@ -0,0 +1,72 @@
+# mkchain
+
+Helper program to generate values for the Tezos chain Helm chart
+
+## Quickstart
+
+This assumes you have [minikube](https://minikube.sigs.k8s.io/docs/), [helm](https://helm.sh/), and `python3` installed. If you don't have docker installed, or you do but don't want to use your local install, configure your shell environment to use minikube’s Docker daemon:
+
+```shell
+eval $(minikube docker-env)
+```
+
+### Install mkchain
+
+```shell
+mkdir mkchain && cd mkchain
+python3 -m venv .venv
+source .venv/bin/activate
+pip install wheel && pip install mkchain
+```
+
+Set [unbuffered IO](https://docs.python.org/3.6/using/cmdline.html#envvar-PYTHONUNBUFFERED) for python:
+
+```shell
+PYTHONUNBUFFERED=x
+```
+
+### Create Tezos Chain
+
+Make sure you have the Tezos Helm chart repo:
+
+```shell
+helm repo add tqtezos https://tqtezos.github.io/tezos-helm-charts
+```
+
+```shell
+CHAIN_NAME=my-chain
+mkchain $CHAIN_NAME
+
+helm install $CHAIN_NAME tqtezos/tezos-chain \
+--values ./${CHAIN_NAME}_values.yaml \
+--namespace tqtezos --create-namespace
+```
+
+## Generate Helm Values
+
+Your chain is uniquely defined by a set of values such as bootstrap account keys, chain id, timestamp...
+
+To generate these values run:
+
+```shell
+mkchain $CHAIN_NAME
+```
+
+This command will create two yaml files in your current working directory. `<$CHAIN_NAME>_values.yaml` and `<$CHAIN_NAME>_invite_values.yaml`.
+
+### mkchain parameters
+
+You can explicitly specify some values by:
+
+- passing CLI parameters to `mkchain`
+- directly modifying the values yaml file generated by `mkchain`
+- passing arguments to [helm install](https://helm.sh/docs/helm/helm_install/) or [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) commands using the `--set` flag, which will selectively override the current yaml values
+
+| YAML Key                         | mkchain argument   | Description                                                                 | Default                |
+| -------------------------------- | ------------------ | --------------------------------------------------------------------------- | ---------------------- |
+| bootstrap_peer                   | --bootstrap-peer   | Peer ip to connect to                                                       |                        |
+| images.tezos                     | --docker-image     | Version of the Tezos docker image                                           | tezos/tezos:v8-release |
+| number_of_nodes                  | --number-of-nodes  | Number of peers in the cluster                                              | 1                      |
+| rpc_auth                         | --rpc-auth         | Whether or not an [RPC auth](../rpc-auth/README.md) backend will be spun up | False                  |
+| zerotier_config.zerotier_network | --zerotier-network | Zerotier network id for external chain access                               |                        |
+| zerotier_config.zerotier_token   | --zerotier-token   | Zerotier token for external chain access                                    |                        |
diff --git a/setup.cfg b/mkchain/setup.cfg
similarity index 100%
rename from setup.cfg
rename to mkchain/setup.cfg
diff --git a/setup.py b/mkchain/setup.py
similarity index 100%
rename from setup.py
rename to mkchain/setup.py
diff --git a/tqchain/_version.py b/mkchain/tqchain/_version.py
similarity index 99%
rename from tqchain/_version.py
rename to mkchain/tqchain/_version.py
index 398c1f524..7f138f9aa 100644
--- a/tqchain/_version.py
+++ b/mkchain/tqchain/_version.py
@@ -40,9 +40,9 @@ def get_config():
     cfg = VersioneerConfig()
     cfg.VCS = "git"
     cfg.style = "pep440"
-    cfg.tag_prefix = "None"
+    cfg.tag_prefix = ""
     cfg.parentdir_prefix = "None"
-    cfg.versionfile_source = "tqchain/_version.py"
+    cfg.versionfile_source = "_version.py"
     cfg.verbose = False
     return cfg
 
diff --git a/tqchain/mkchain.py b/mkchain/tqchain/mkchain.py
similarity index 69%
rename from tqchain/mkchain.py
rename to mkchain/tqchain/mkchain.py
index f128c013d..227744c04 100644
--- a/tqchain/mkchain.py
+++ b/mkchain/tqchain/mkchain.py
@@ -1,5 +1,4 @@
 import argparse
-import base64
 import os
 import random
 import string
@@ -16,9 +15,6 @@
 __version__ = get_versions()["version"]
 
 
-my_path = os.path.dirname(os.path.abspath(__file__))
-
-
 def run_docker(image, entrypoint, *args):
     return subprocess.check_output(
         "docker run --entrypoint %s --rm %s %s" % (entrypoint, image, " ".join(args)),
@@ -27,6 +23,10 @@ def run_docker(image, entrypoint, *args):
     )
 
 
+def extract_key(keys, index: int) -> bytes:
+    return keys[index].split(b":")[index].strip().decode("ascii")
+
+
 def gen_key(image):
     keys = run_docker(
         image,
@@ -35,31 +35,7 @@ def gen_key(image):
         "'/usr/local/bin/tezos-client --protocol PsDELPH1Kxsx gen keys mykey && /usr/local/bin/tezos-client --protocol PsDELPH1Kxsx show address mykey -S'",
     ).split(b"\n")
 
-    def extract_key(index: int) -> bytes:
-        return base64.b64encode(
-            keys[index].split(b":")[index].strip().decode("utf-8").encode("ascii")
-        )
-
-    return {"public_key": extract_key(1), "secret_key": extract_key(2)}
-
-
-def get_rpc_auth_container():
-    return {
-        "name": "rpc-auth",
-        "image": (
-            "tezos-rpc-auth:dev"
-            if "-" in __version__ or "+" in __version__
-            else "tqtezos/tezos-k8s-rpc-auth:%s" % __version__
-        ),
-        "imagePullPolicy": "IfNotPresent",
-        "ports": [{"containerPort": 8080}],
-        "env": [
-            {"name": "TEZOS_RPC_SERVICE", "value": "tezos-bootstrap-node-rpc"},
-            {"name": "TEZOS_RPC_SERVICE_PORT", "value": "8732"},
-            {"name": "REDIS_HOST", "value": "redis-service"},
-            {"name": "REDIS_PORT", "value": "6379"},
-        ],
-    }
+    return {"public_key": extract_key(keys, 1), "secret_key": extract_key(keys, 2)}
 
 
 def get_genesis_vanity_chain_id(seed_len=16):
@@ -96,7 +72,7 @@ def get_genesis_vanity_chain_id(seed_len=16):
     "bootstrap_peer": {"help": "peer ip to join"},
     "docker_image": {
         "help": "Version of the Tezos docker image",
-        "default": "tezos/tezos:v7-release",
+        "default": "tezos/tezos:v8-release",
     },
     "rpc_auth": {
         "help": "Should spin up an RPC authentication server",
@@ -140,24 +116,10 @@ def main():
         "genesis",
     ]
 
-    zerotier_image = (
-        "tezos-zerotier:dev"
-        if "-" in __version__ or "+" in __version__
-        else "tqtezos/tezos-k8s-zerotier:%s" % __version__
-    )
-
-    rpc_auth_image = (
-        "tezos-rpc-auth:dev"
-        if "-" in __version__ or "+" in __version__
-        else "tqtezos/tezos-k8s-rpc-auth:%s" % __version__
-    )
-
     base_constants = {
         "chain_name": args.chain_name,
-        "container_images": {
-            "zerotier_docker_image": zerotier_image,
-            "rpc_auth_image": rpc_auth_image,
-            "tezos_docker_image": args.docker_image,
+        "images": {
+            "tezos": args.docker_image,
         },
         "genesis": {
             "genesis_chain_id": get_genesis_vanity_chain_id(),
@@ -194,7 +156,7 @@ def main():
         **base_constants,
         "accounts": accounts["secret_key"],
         "is_invitation": False,
-        "bootstrap_peers": bootstrap_peers + ["tezos-bootstrap-node-p2p:9732"],
+        "bootstrap_peers": bootstrap_peers,
     }
     invitation_constants = {
         **base_constants,
@@ -202,12 +164,14 @@ def main():
         "is_invitation": True,
         "bootstrap_peers": bootstrap_peers,
     }
+    invitation_constants.pop("rpc_auth")
 
-    with open(f"{args.chain_name}_values.yaml", "w") as yaml_file:
+    files_path = f"{os.getcwd()}/{args.chain_name}"
+    with open(f"{files_path}_values.yaml", "w") as yaml_file:
         yaml.dump(creation_constants, yaml_file)
-        print(f"Wrote create constants in {args.chain_name}_values.yaml")
-    with open(f"{args.chain_name}_invite_values.yaml", "w") as yaml_file:
-        print(f"Wrote invitation constants in {args.chain_name}_invite_values.yaml")
+        print(f"Wrote chain creation constants to {files_path}_values.yaml")
+    with open(f"{files_path}_invite_values.yaml", "w") as yaml_file:
+        print(f"Wrote chain invitation constants to {files_path}_invite_values.yaml")
         yaml.dump(invitation_constants, yaml_file)
 
 
diff --git a/versioneer.py b/mkchain/versioneer.py
similarity index 100%
rename from versioneer.py
rename to mkchain/versioneer.py
diff --git a/rpc-auth/.dockerignore b/rpc-auth/.dockerignore
new file mode 100644
index 000000000..8bf81bf5a
--- /dev/null
+++ b/rpc-auth/.dockerignore
@@ -0,0 +1,2 @@
+client
+README.md
diff --git a/docker/rpc-auth/Dockerfile b/rpc-auth/Dockerfile
similarity index 96%
rename from docker/rpc-auth/Dockerfile
rename to rpc-auth/Dockerfile
index 1216a7dbd..fd4662fd7 100644
--- a/docker/rpc-auth/Dockerfile
+++ b/rpc-auth/Dockerfile
@@ -17,7 +17,7 @@ RUN apt-get update -y \
   pkg-config \
   && echo
 
-COPY requirements.txt .
+COPY ./requirements.txt .
 RUN mkdir wheels \
   && pip wheel -r requirements.txt \
   --wheel-dir ./wheels --no-cache-dir
@@ -38,7 +38,7 @@ RUN apt-get update -y \
 
 # Installing python dependencies
 COPY --from=builder /var/rpc-auth/wheels wheels
-COPY requirements.txt .
+COPY ./requirements.txt .
 RUN pip install -r requirements.txt \
   --no-index --find-links ./wheels \
   && rm -rf ./wheels ./requirements.txt
diff --git a/rpc-auth/README.md b/rpc-auth/README.md
new file mode 100644
index 000000000..3639856e0
--- /dev/null
+++ b/rpc-auth/README.md
@@ -0,0 +1,82 @@
+# Tezos RPC Authentication
+
+`rpc-auth` provides a mechanism where a user authenticates themselves and will receive a secret url that they then use to make RPC calls.
+
+## Deploy RPC Auth Backend
+
+This assumes that you have followed the steps [here](../README.md) necessary to deploy a Tezos private chain.
+
+To deploy an RPC Authentication backend for your private chain, do either:
+
+- Run your original `mkchain` command with the flags you used (i.e. Zerotier flags), adding in the `--rpc-auth` flag:
+
+  ```shell
+  mkchain $CHAIN_NAME ... --rpc-auth
+  ```
+
+- Or manually add the field `rpc_auth: true` in your generated Helm values file `./${CHAIN_NAME}_values.yaml`.
+
+Make sure you have the Tezos Helm chart repo:
+
+```shell
+helm repo add tqtezos https://tqtezos.github.io/tezos-helm-charts
+```
+
+If you don't currently have a chain running, run the following command to start it:
+
+```shell
+helm install $CHAIN_NAME tqtezos/tezos-chain \
+--values ./${CHAIN_NAME}_values.yaml \
+--namespace tqtezos --create-namespace
+```
+
+If you already have a chain running, you need to use Helm's `upgrade` cmd instead of `install`:
+
+```shell
+helm upgrade $CHAIN_NAME tqtezos/tezos-chain \
+--values ./${CHAIN_NAME}_values.yaml \
+--namespace tqtezos
+```
+
+## Client Authentication
+
+### Prerequisites
+
+- [tezos-client](https://assets.tqtezos.com/docs/setup/1-tezos-client/)
+
+## Authentication flow
+
+1. You provide a trusted user with your cluster ip/address and your private tezos chain id.
+   To see your chain id, either:
+
+   - Run
+     ```shell
+     kubectl exec -it -n tqtezos deployment/tezos-bootstrap-node -c tezos-node -- tezos-client rpc get /chains/main/chain_id
+     ```
+   - Use a tool like [Lens](https://k8slens.dev/) to view the logs of the Tezos node. (As well as the rest of your k8s infrastructure)
+   - Manually run the logs command `kubectl logs -n tqtezos deployment/tezos-bootstrap-node -c tezos-node`. The top of the logs should look similar to:
+     ```
+     Dec 21 19:42:08 - node.main: starting the Tezos node (chain = my-chain)
+     Dec 21 19:42:08 - node.main: disabled local peer discovery
+     Dec 21 19:42:08 - node.main: read identity file (peer_id = idsbTksk6cHggEndHLQBAJvxaViUnz)
+     Dec 21 19:42:08 - main: shell-node initialization: bootstrapping
+     Dec 21 19:42:08 - main: shell-node initialization: p2p_maintain_started
+     Dec 21 19:42:08 - block_validator_process_external: Initialized
+     Dec 21 19:42:08 - block_validator_process_external: Block validator started on pid 11
+     Dec 21 19:42:08 - validator.block: Worker started
+     Dec 21 19:42:08 - node.validator: activate chain NetXitypWekag8Z
+     ```
+     The chain id is printed on the last line: `NetXitypWekag8Z`.
+
+2. The user needs to have a Tezos secret key either generated or imported by `tezos-client`. The user's secret key is used to sign some data for the server to then verify.
+
+3. The user runs: `rpc-auth/client/init.sh --cluster-address $CLUSTER_IP --tz-alias $TZ_ALIAS --chain-id $CHAIN_ID`
+
+   - `TZ_ALIAS` is the alias of a user's tz address secret key.
+
+4. If the user is authenticated, the response should contain a secret url that looks like `http://192.168.64.51/tezos-node-rpc/ffff3eb3d7dd4f6bbff3f2fd096722ae/`
+
+5. Client can then make RPC requests:
+   - `curl http://192.168.64.51/tezos-node-rpc/ffff3eb3d7dd4f6bbff3f2fd096722ae/chains/main/chain_id`
+   - Bug in tezos client v8, so as of version `tezos/tezos:master_08d3405e_20201113152010`:
+     - `tezos-client --endpoint http://192.168.64.51/tezos-node-rpc/ffff3eb3d7dd4f6bbff3f2fd096722ae/ rpc get chains/main/chain_id`
diff --git a/scripts/rpc-auth-client.sh b/rpc-auth/client/init.sh
similarity index 97%
rename from scripts/rpc-auth-client.sh
rename to rpc-auth/client/init.sh
index 160164d73..d7f0ecaca 100755
--- a/scripts/rpc-auth-client.sh
+++ b/rpc-auth/client/init.sh
@@ -83,7 +83,7 @@ fi
 # echo NONCE: "$NONCE"
 
 echo "Signing data..."
-SIGNATURE=$(tezos-client -p PsCARTHAGazK sign bytes 0x05${NONCE} for ${TZ_ALIAS} | cut -f 2 -d " ")
+SIGNATURE=$(tezos-client -p PsDELPH1Kxsx sign bytes 0x05${NONCE} for ${TZ_ALIAS} | cut -f 2 -d " ")
 PUBLIC_KEY=$(tezos-client show address ${TZ_ALIAS} 2>/dev/null | grep "Public Key: " | awk '{print $3}')
 
 # echo SIGNATURE: "$SIGNATURE"
diff --git a/docker/rpc-auth/requirements.txt b/rpc-auth/requirements.txt
similarity index 100%
rename from docker/rpc-auth/requirements.txt
rename to rpc-auth/requirements.txt
diff --git a/docker/rpc-auth/server/index.py b/rpc-auth/server/index.py
similarity index 99%
rename from docker/rpc-auth/server/index.py
rename to rpc-auth/server/index.py
index cc23a8aeb..d4e16af67 100755
--- a/docker/rpc-auth/server/index.py
+++ b/rpc-auth/server/index.py
@@ -7,7 +7,6 @@
 
 import requests
 from flask import Flask, abort, request
-
 from pytezos.crypto import Key
 from redis import StrictRedis, WatchError
 
diff --git a/scripts/lint.sh b/scripts/lint.sh
deleted file mode 100755
index 432a276d1..000000000
--- a/scripts/lint.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-autoflake . -r --remove-all-unused-imports -i
-isort . -rc
-black .
diff --git a/tezos-helm/templates/utils.yaml b/tezos-helm/templates/utils.yaml
deleted file mode 100644
index 484016d89..000000000
--- a/tezos-helm/templates/utils.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-apiVersion: v1
-data:
-{{ (.Files.Glob "utils/*").AsConfig | indent 2 }}
-kind: ConfigMap
-metadata:
-  name: tqtezos-utils
-  namespace: {{ .Release.Namespace }}
diff --git a/tqchain/deployment/rpc-auth.yaml b/tqchain/deployment/rpc-auth.yaml
deleted file mode 100644
index 152e7bf49..000000000
--- a/tqchain/deployment/rpc-auth.yaml
+++ /dev/null
@@ -1,194 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
-  name: redis-service
-  namespace: tqtezos
-  labels:
-    app: redis
-spec:
-  ports:
-    - name: redis
-      port: 6379
-  selector:
-    app: redis
----
-kind: PersistentVolumeClaim
-apiVersion: v1
-metadata:
-  name: redis-pv-claim
-  namespace: tqtezos
-  labels:
-    app: redis
-spec:
-  accessModes:
-    - ReadWriteMany
-  resources:
-    requests:
-      storage: 5Gi
----
-apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: redis-config
-  namespace: tqtezos
-data:
-  redis.conf: |
-    appendonly yes
-    appendfsync always
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: redis
-  namespace: tqtezos
-  labels:
-    app: redis
-spec:
-  selector:
-    matchLabels:
-      app: redis
-  template:
-    metadata:
-      labels:
-        app: redis
-    spec:
-      containers:
-        - name: master
-          image: redis
-          command:
-            - redis-server
-            - "/redis/redis.conf"
-          readinessProbe:
-            exec:
-              command:
-                - sh
-                - -c
-                - "redis-cli -h $(hostname) ping"
-            initialDelaySeconds: 5
-            timeoutSeconds: 5
-          livenessProbe:
-            exec:
-              command:
-                - sh
-                - -c
-                - "redis-cli -h $(hostname) ping"
-            initialDelaySeconds: 5
-            periodSeconds: 3
-          # resources:
-          #   requests:
-          #     cpu: 100m
-          #     memory: 100Mi
-          ports:
-            - containerPort: 6379
-          volumeMounts:
-            - mountPath: /data
-              name: redis-data
-            - mountPath: /redis
-              name: redis-config
-
-      volumes:
-        - name: redis-data
-          persistentVolumeClaim:
-            claimName: redis-pv-claim
-        - name: redis-config
-          configMap:
-            name: redis-config
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: rpc-auth
-  namespace: tqtezos
-spec:
-  selector:
-    app: rpc-auth
-  ports:
-    - port: 8080
-      targetPort: 8080
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: rpc-auth
-  namespace: tqtezos
-spec:
-  selector:
-    matchLabels:
-      app: rpc-auth
-  template:
-    metadata:
-      labels:
-        app: rpc-auth
-        appType: rpc-auth
-    spec:
-      containers:
-        - name: rpc-auth
-          image: tezos-rpc-auth:dev
-          imagePullPolicy: Never
-          ports:
-            - containerPort: 8080
-          env:
-            - name: REDIS_HOST
-              value: redis-service
-            - name: REDIS_PORT
-              value: "6379"
-            - name: TEZOS_RPC_SERVICE
-              value: tezos-bootstrap-node-rpc
-            - name: TEZOS_RPC_SERVICE_PORT
-              value: "8732"
----
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
-  name: rpc-vending-machine-ingress
-  namespace: tqtezos
-  annotations:
-    nginx.ingress.kubernetes.io/use-regex: "true"
-spec:
-  rules:
-    - http:
-        paths:
-          # Client provides chain id and gets back a nonce
-          - path: /vending-machine/(.*)
-            pathType: Exact
-            backend:
-              service:
-                name: rpc-auth
-                port:
-                  number: 8080
-          # Client provides signed data and gets back a secret url
-          - path: /vending-machine
-            pathType: Exact
-            backend:
-              service:
-                name: rpc-auth
-                port:
-                  number: 8080
----
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
-  name: tezos-rpc-ingress
-  namespace: tqtezos
-  annotations:
-    nginx.ingress.kubernetes.io/use-regex: "true"
-    nginx.ingress.kubernetes.io/rewrite-target: /$2
-    # https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md#external-authentication
-    nginx.ingress.kubernetes.io/auth-url: http://$service_name.$namespace.svc.cluster.local:8080/auth
-    nginx.ingress.kubernetes.io/auth-snippet: |
-      # Bug with nginx setting auth-url annotation port with a variable.
-      # Service name and namespace variables work.
-      # https://github.com/kubernetes/ingress-nginx/issues/6427
-      set $service_name "rpc-auth";
-spec:
-  rules:
-    - http:
-        paths:
-          # Client uses secret url to access the RPC endpoint
-          - path: /tezos-node-rpc/(.*?)/(.*)
-            pathType: Exact
-            backend:
-              service:
-                name: tezos-bootstrap-node-rpc
-                port:
-                  number: 8732
diff --git a/zerotier/.dockerignore b/zerotier/.dockerignore
new file mode 100644
index 000000000..e69de29bb
diff --git a/docker/zerotier/Dockerfile b/zerotier/Dockerfile
similarity index 100%
rename from docker/zerotier/Dockerfile
rename to zerotier/Dockerfile
diff --git a/docker/zerotier/entrypoint.sh b/zerotier/entrypoint.sh
similarity index 100%
rename from docker/zerotier/entrypoint.sh
rename to zerotier/entrypoint.sh
diff --git a/docker/zerotier/supervisor-zerotier.conf b/zerotier/supervisor-zerotier.conf
similarity index 100%
rename from docker/zerotier/supervisor-zerotier.conf
rename to zerotier/supervisor-zerotier.conf


Test patch:
diff --git a/tests/test_package_exists.py b/mkchain/tests/test_package_exists.py
similarity index 100%
rename from tests/test_package_exists.py
rename to mkchain/tests/test_package_exists.py